From ede52c5260bbc30b1435ad7dcbb4b3fadbe191b1 Mon Sep 17 00:00:00 2001 From: jgobbo Date: Wed, 15 May 2024 14:02:57 -0700 Subject: [PATCH 1/3] fix deprecated numpy type --- igor/binarywave.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/igor/binarywave.py b/igor/binarywave.py index 6d87d14..86b4862 100644 --- a/igor/binarywave.py +++ b/igor/binarywave.py @@ -107,7 +107,7 @@ class NullStaticStringField (StaticStringField): # From IgorMath.h TYPE_TABLE = { # (key: integer flag, value: numpy dtype) 0:None, # Text wave, not handled in ReadWave.c - 1:_numpy.complex, # NT_CMPLX, makes number complex. + 1:_numpy.complex128, # NT_CMPLX, makes number complex. 2:_numpy.float32, # NT_FP32, 32 bit fp numbers. 3:_numpy.complex64, 4:_numpy.float64, # NT_FP64, 64 bit fp numbers. From 5d6de3bbd56cd3573e77bfa4816911359e4ddb7e Mon Sep 17 00:00:00 2001 From: jgobbo Date: Sun, 15 Dec 2024 13:42:05 -0800 Subject: [PATCH 2/3] small bug fixes and black formatting --- .gitignore | 2 + igor/__init__.py | 7 +- igor/binarywave.py | 866 ++++++++++++++++++++++++++++++++------------- igor/igorpy.py | 193 ++++++---- igor/packed.py | 112 +++--- igor/script.py | 44 ++- igor/struct.py | 203 ++++++----- igor/util.py | 40 ++- 8 files changed, 969 insertions(+), 498 deletions(-) diff --git a/.gitignore b/.gitignore index 8d35cb3..04fd334 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ __pycache__ *.pyc +build/ +igor.egg-info/ \ No newline at end of file diff --git a/igor/__init__.py b/igor/__init__.py index 7f0220a..2e24491 100644 --- a/igor/__init__.py +++ b/igor/__init__.py @@ -17,14 +17,15 @@ "Interface for reading binary IGOR files." -__version__ = '0.3.1' +__version__ = "0.3.1" import logging as _logging -LOG = _logging.getLogger('igor') +LOG = _logging.getLogger("igor") LOG.setLevel(_logging.ERROR) LOG.addHandler(_logging.StreamHandler()) LOG.handlers[-1].setFormatter( - _logging.Formatter('%(name)s - %(levelname)s - %(message)s')) + _logging.Formatter("%(name)s - %(levelname)s - %(message)s") +) diff --git a/igor/binarywave.py b/igor/binarywave.py index 86b4862..5daf468 100644 --- a/igor/binarywave.py +++ b/igor/binarywave.py @@ -50,22 +50,21 @@ # So we roll our own types. See # http://docs.scipy.org/doc/numpy/user/basics.rec.html # http://docs.scipy.org/doc/numpy/reference/generated/numpy.dtype.html -complexInt8 = _numpy.dtype([('real', _numpy.int8), ('imag', _numpy.int8)]) -complexInt16 = _numpy.dtype([('real', _numpy.int16), ('imag', _numpy.int16)]) -complexInt32 = _numpy.dtype([('real', _numpy.int32), ('imag', _numpy.int32)]) -complexUInt8 = _numpy.dtype([('real', _numpy.uint8), ('imag', _numpy.uint8)]) -complexUInt16 = _numpy.dtype( - [('real', _numpy.uint16), ('imag', _numpy.uint16)]) -complexUInt32 = _numpy.dtype( - [('real', _numpy.uint32), ('imag', _numpy.uint32)]) +complexInt8 = _numpy.dtype([("real", _numpy.int8), ("imag", _numpy.int8)]) +complexInt16 = _numpy.dtype([("real", _numpy.int16), ("imag", _numpy.int16)]) +complexInt32 = _numpy.dtype([("real", _numpy.int32), ("imag", _numpy.int32)]) +complexUInt8 = _numpy.dtype([("real", _numpy.uint8), ("imag", _numpy.uint8)]) +complexUInt16 = _numpy.dtype([("real", _numpy.uint16), ("imag", _numpy.uint16)]) +complexUInt32 = _numpy.dtype([("real", _numpy.uint32), ("imag", _numpy.uint32)]) -class StaticStringField (_DynamicField): +class StaticStringField(_DynamicField): _null_terminated = False _array_size_field = None + def __init__(self, *args, **kwargs): - if 'array' not in kwargs: - kwargs['array'] = True + if "array" not in kwargs: + kwargs["array"] = True super(StaticStringField, self).__init__(*args, **kwargs) def post_unpack(self, parents, data): @@ -77,12 +76,12 @@ def post_unpack(self, parents, data): def _normalize_string(self, d): if isinstance(d, bytes): pass - elif hasattr(d, 'tobytes'): + elif hasattr(d, "tobytes"): d = d.tobytes() - elif hasattr(d, 'tostring'): # Python 2 compatibility + elif hasattr(d, "tostring"): # Python 2 compatibility d = d.tostring() else: - d = b''.join(d) + d = b"".join(d) if self._array_size_field: start = 0 strings = [] @@ -91,44 +90,44 @@ def _normalize_string(self, d): if end > start: strings.append(d[start:end]) if self._null_terminated: - strings[-1] = strings[-1].split(b'\x00', 1)[0] + strings[-1] = strings[-1].split(b"\x00", 1)[0] start = end elif self._null_terminated: - d = d.split(b'\x00', 1)[0] + d = d.split(b"\x00", 1)[0] return d -class NullStaticStringField (StaticStringField): +class NullStaticStringField(StaticStringField): _null_terminated = True # Begin IGOR constants and typedefs from IgorBin.h # From IgorMath.h -TYPE_TABLE = { # (key: integer flag, value: numpy dtype) - 0:None, # Text wave, not handled in ReadWave.c - 1:_numpy.complex128, # NT_CMPLX, makes number complex. - 2:_numpy.float32, # NT_FP32, 32 bit fp numbers. - 3:_numpy.complex64, - 4:_numpy.float64, # NT_FP64, 64 bit fp numbers. - 5:_numpy.complex128, - 8:_numpy.int8, # NT_I8, 8 bit signed integer. Requires Igor Pro - # 2.0 or later. - 9:complexInt8, - 0x10:_numpy.int16,# NT_I16, 16 bit integer numbers. Requires Igor - # Pro 2.0 or later. - 0x11:complexInt16, - 0x20:_numpy.int32,# NT_I32, 32 bit integer numbers. Requires Igor - # Pro 2.0 or later. - 0x21:complexInt32, -# 0x40:None, # NT_UNSIGNED, Makes above signed integers -# # unsigned. Requires Igor Pro 3.0 or later. - 0x48:_numpy.uint8, - 0x49:complexUInt8, - 0x50:_numpy.uint16, - 0x51:complexUInt16, - 0x60:_numpy.uint32, - 0x61:complexUInt32, +TYPE_TABLE = { # (key: integer flag, value: numpy dtype) + 0: None, # Text wave, not handled in ReadWave.c + 1: _numpy.complex128, # NT_CMPLX, makes number complex. + 2: _numpy.float32, # NT_FP32, 32 bit fp numbers. + 3: _numpy.complex64, + 4: _numpy.float64, # NT_FP64, 64 bit fp numbers. + 5: _numpy.complex128, + 8: _numpy.int8, # NT_I8, 8 bit signed integer. Requires Igor Pro + # 2.0 or later. + 9: complexInt8, + 0x10: _numpy.int16, # NT_I16, 16 bit integer numbers. Requires Igor + # Pro 2.0 or later. + 0x11: complexInt16, + 0x20: _numpy.int32, # NT_I32, 32 bit integer numbers. Requires Igor + # Pro 2.0 or later. + 0x21: complexInt32, + # 0x40:None, # NT_UNSIGNED, Makes above signed integers + # # unsigned. Requires Igor Pro 3.0 or later. + 0x48: _numpy.uint8, + 0x49: complexUInt8, + 0x50: _numpy.uint16, + 0x51: complexUInt16, + 0x60: _numpy.uint32, + 0x61: complexUInt32, } # From wave.h @@ -136,136 +135,402 @@ class NullStaticStringField (StaticStringField): # From binary.h BinHeader1 = _Structure( # `version` field pulled out into Wave - name='BinHeader1', + name="BinHeader1", fields=[ - _Field('l', 'wfmSize', help='The size of the WaveHeader2 data structure plus the wave data plus 16 bytes of padding.'), - _Field('h', 'checksum', help='Checksum over this header and the wave header.'), - ]) + _Field( + "l", + "wfmSize", + help="The size of the WaveHeader2 data structure plus the wave data plus 16 bytes of padding.", + ), + _Field("h", "checksum", help="Checksum over this header and the wave header."), + ], +) BinHeader2 = _Structure( # `version` field pulled out into Wave - name='BinHeader2', + name="BinHeader2", fields=[ - _Field('l', 'wfmSize', help='The size of the WaveHeader2 data structure plus the wave data plus 16 bytes of padding.'), - _Field('l', 'noteSize', help='The size of the note text.'), - _Field('l', 'pictSize', default=0, help='Reserved. Write zero. Ignore on read.'), - _Field('h', 'checksum', help='Checksum over this header and the wave header.'), - ]) + _Field( + "l", + "wfmSize", + help="The size of the WaveHeader2 data structure plus the wave data plus 16 bytes of padding.", + ), + _Field("l", "noteSize", help="The size of the note text."), + _Field( + "l", "pictSize", default=0, help="Reserved. Write zero. Ignore on read." + ), + _Field("h", "checksum", help="Checksum over this header and the wave header."), + ], +) BinHeader3 = _Structure( # `version` field pulled out into Wave - name='BinHeader3', + name="BinHeader3", fields=[ - _Field('l', 'wfmSize', help='The size of the WaveHeader2 data structure plus the wave data plus 16 bytes of padding.'), - _Field('l', 'noteSize', help='The size of the note text.'), - _Field('l', 'formulaSize', help='The size of the dependency formula, if any.'), - _Field('l', 'pictSize', default=0, help='Reserved. Write zero. Ignore on read.'), - _Field('h', 'checksum', help='Checksum over this header and the wave header.'), - ]) + _Field( + "l", + "wfmSize", + help="The size of the WaveHeader2 data structure plus the wave data plus 16 bytes of padding.", + ), + _Field("l", "noteSize", help="The size of the note text."), + _Field("l", "formulaSize", help="The size of the dependency formula, if any."), + _Field( + "l", "pictSize", default=0, help="Reserved. Write zero. Ignore on read." + ), + _Field("h", "checksum", help="Checksum over this header and the wave header."), + ], +) BinHeader5 = _Structure( # `version` field pulled out into Wave - name='BinHeader5', + name="BinHeader5", fields=[ - _Field('h', 'checksum', help='Checksum over this header and the wave header.'), - _Field('l', 'wfmSize', help='The size of the WaveHeader5 data structure plus the wave data.'), - _Field('l', 'formulaSize', help='The size of the dependency formula, if any.'), - _Field('l', 'noteSize', help='The size of the note text.'), - _Field('l', 'dataEUnitsSize', help='The size of optional extended data units.'), - _Field('l', 'dimEUnitsSize', help='The size of optional extended dimension units.', count=MAXDIMS, array=True), - _Field('l', 'dimLabelsSize', help='The size of optional dimension labels.', count=MAXDIMS, array=True), - _Field('l', 'sIndicesSize', help='The size of string indicies if this is a text wave.'), - _Field('l', 'optionsSize1', default=0, help='Reserved. Write zero. Ignore on read.'), - _Field('l', 'optionsSize2', default=0, help='Reserved. Write zero. Ignore on read.'), - ]) + _Field("h", "checksum", help="Checksum over this header and the wave header."), + _Field( + "l", + "wfmSize", + help="The size of the WaveHeader5 data structure plus the wave data.", + ), + _Field("l", "formulaSize", help="The size of the dependency formula, if any."), + _Field("l", "noteSize", help="The size of the note text."), + _Field("l", "dataEUnitsSize", help="The size of optional extended data units."), + _Field( + "l", + "dimEUnitsSize", + help="The size of optional extended dimension units.", + count=MAXDIMS, + array=True, + ), + _Field( + "l", + "dimLabelsSize", + help="The size of optional dimension labels.", + count=MAXDIMS, + array=True, + ), + _Field( + "l", + "sIndicesSize", + help="The size of string indicies if this is a text wave.", + ), + _Field( + "l", "optionsSize1", default=0, help="Reserved. Write zero. Ignore on read." + ), + _Field( + "l", "optionsSize2", default=0, help="Reserved. Write zero. Ignore on read." + ), + ], +) # From wave.h -MAX_WAVE_NAME2 = 18 # Maximum length of wave name in version 1 and 2 - # files. Does not include the trailing null. -MAX_WAVE_NAME5 = 31 # Maximum length of wave name in version 5 - # files. Does not include the trailing null. +MAX_WAVE_NAME2 = 18 # Maximum length of wave name in version 1 and 2 +# files. Does not include the trailing null. +MAX_WAVE_NAME5 = 31 # Maximum length of wave name in version 5 +# files. Does not include the trailing null. MAX_UNIT_CHARS = 3 # Header to an array of waveform data. # `wData` field pulled out into DynamicWaveDataField1 WaveHeader2 = _DynamicStructure( - name='WaveHeader2', + name="WaveHeader2", fields=[ - _Field('h', 'type', help='See types (e.g. NT_FP64) above. Zero for text waves.'), - _Field('P', 'next', default=0, help='Used in memory only. Write zero. Ignore on read.'), - NullStaticStringField('c', 'bname', help='Name of wave plus trailing null.', count=MAX_WAVE_NAME2+2), - _Field('h', 'whVersion', default=0, help='Write 0. Ignore on read.'), - _Field('h', 'srcFldr', default=0, help='Used in memory only. Write zero. Ignore on read.'), - _Field('P', 'fileName', default=0, help='Used in memory only. Write zero. Ignore on read.'), - _Field('c', 'dataUnits', default=0, help='Natural data units go here - null if none.', count=MAX_UNIT_CHARS+1, array=True), - _Field('c', 'xUnits', default=0, help='Natural x-axis units go here - null if none.', count=MAX_UNIT_CHARS+1, array=True), - _Field('l', 'npnts', help='Number of data points in wave.'), - _Field('h', 'aModified', default=0, help='Used in memory only. Write zero. Ignore on read.'), - _Field('d', 'hsA', help='X value for point p = hsA*p + hsB'), - _Field('d', 'hsB', help='X value for point p = hsA*p + hsB'), - _Field('h', 'wModified', default=0, help='Used in memory only. Write zero. Ignore on read.'), - _Field('h', 'swModified', default=0, help='Used in memory only. Write zero. Ignore on read.'), - _Field('h', 'fsValid', help='True if full scale values have meaning.'), - _Field('d', 'topFullScale', help='The min full scale value for wave.'), # sic, 'min' should probably be 'max' - _Field('d', 'botFullScale', help='The min full scale value for wave.'), - _Field('c', 'useBits', default=0, help='Used in memory only. Write zero. Ignore on read.'), - _Field('c', 'kindBits', default=0, help='Reserved. Write zero. Ignore on read.'), - _Field('P', 'formula', default=0, help='Used in memory only. Write zero. Ignore on read.'), - _Field('l', 'depID', default=0, help='Used in memory only. Write zero. Ignore on read.'), - _Field('L', 'creationDate', help='DateTime of creation. Not used in version 1 files.'), - _Field('c', 'wUnused', default=0, help='Reserved. Write zero. Ignore on read.', count=2, array=True), - _Field('L', 'modDate', help='DateTime of last modification.'), - _Field('P', 'waveNoteH', help='Used in memory only. Write zero. Ignore on read.'), - ]) + _Field( + "h", "type", help="See types (e.g. NT_FP64) above. Zero for text waves." + ), + _Field( + "P", + "next", + default=0, + help="Used in memory only. Write zero. Ignore on read.", + ), + NullStaticStringField( + "c", + "bname", + help="Name of wave plus trailing null.", + count=MAX_WAVE_NAME2 + 2, + ), + _Field("h", "whVersion", default=0, help="Write 0. Ignore on read."), + _Field( + "h", + "srcFldr", + default=0, + help="Used in memory only. Write zero. Ignore on read.", + ), + _Field( + "P", + "fileName", + default=0, + help="Used in memory only. Write zero. Ignore on read.", + ), + _Field( + "c", + "dataUnits", + default=0, + help="Natural data units go here - null if none.", + count=MAX_UNIT_CHARS + 1, + array=True, + ), + _Field( + "c", + "xUnits", + default=0, + help="Natural x-axis units go here - null if none.", + count=MAX_UNIT_CHARS + 1, + array=True, + ), + _Field("l", "npnts", help="Number of data points in wave."), + _Field( + "h", + "aModified", + default=0, + help="Used in memory only. Write zero. Ignore on read.", + ), + _Field("d", "hsA", help="X value for point p = hsA*p + hsB"), + _Field("d", "hsB", help="X value for point p = hsA*p + hsB"), + _Field( + "h", + "wModified", + default=0, + help="Used in memory only. Write zero. Ignore on read.", + ), + _Field( + "h", + "swModified", + default=0, + help="Used in memory only. Write zero. Ignore on read.", + ), + _Field("h", "fsValid", help="True if full scale values have meaning."), + _Field( + "d", "topFullScale", help="The min full scale value for wave." + ), # sic, 'min' should probably be 'max' + _Field("d", "botFullScale", help="The min full scale value for wave."), + _Field( + "c", + "useBits", + default=0, + help="Used in memory only. Write zero. Ignore on read.", + ), + _Field( + "c", "kindBits", default=0, help="Reserved. Write zero. Ignore on read." + ), + _Field( + "P", + "formula", + default=0, + help="Used in memory only. Write zero. Ignore on read.", + ), + _Field( + "l", + "depID", + default=0, + help="Used in memory only. Write zero. Ignore on read.", + ), + _Field( + "L", + "creationDate", + help="DateTime of creation. Not used in version 1 files.", + ), + _Field( + "c", + "wUnused", + default=0, + help="Reserved. Write zero. Ignore on read.", + count=2, + array=True, + ), + _Field("L", "modDate", help="DateTime of last modification."), + _Field( + "P", "waveNoteH", help="Used in memory only. Write zero. Ignore on read." + ), + ], +) # `sIndices` pointer unset (use Wave5_data['sIndices'] instead). This # field is filled in by DynamicStringIndicesDataField. # `wData` field pulled out into DynamicWaveDataField5 WaveHeader5 = _DynamicStructure( - name='WaveHeader5', + name="WaveHeader5", fields=[ - _Field('P', 'next', help='link to next wave in linked list.'), - _Field('L', 'creationDate', help='DateTime of creation.'), - _Field('L', 'modDate', help='DateTime of last modification.'), - _Field('l', 'npnts', help='Total number of points (multiply dimensions up to first zero).'), - _Field('h', 'type', help='See types (e.g. NT_FP64) above. Zero for text waves.'), - _Field('h', 'dLock', default=0, help='Reserved. Write zero. Ignore on read.'), - _Field('c', 'whpad1', default=0, help='Reserved. Write zero. Ignore on read.', count=6, array=True), - _Field('h', 'whVersion', default=1, help='Write 1. Ignore on read.'), - NullStaticStringField('c', 'bname', help='Name of wave plus trailing null.', count=MAX_WAVE_NAME5+1), - _Field('l', 'whpad2', default=0, help='Reserved. Write zero. Ignore on read.'), - _Field('P', 'dFolder', default=0, help='Used in memory only. Write zero. Ignore on read.'), + _Field("P", "next", help="link to next wave in linked list."), + _Field("L", "creationDate", help="DateTime of creation."), + _Field("L", "modDate", help="DateTime of last modification."), + _Field( + "l", + "npnts", + help="Total number of points (multiply dimensions up to first zero).", + ), + _Field( + "h", "type", help="See types (e.g. NT_FP64) above. Zero for text waves." + ), + _Field("h", "dLock", default=0, help="Reserved. Write zero. Ignore on read."), + _Field( + "c", + "whpad1", + default=0, + help="Reserved. Write zero. Ignore on read.", + count=6, + array=True, + ), + _Field("h", "whVersion", default=1, help="Write 1. Ignore on read."), + NullStaticStringField( + "c", + "bname", + help="Name of wave plus trailing null.", + count=MAX_WAVE_NAME5 + 1, + ), + _Field("l", "whpad2", default=0, help="Reserved. Write zero. Ignore on read."), + _Field( + "P", + "dFolder", + default=0, + help="Used in memory only. Write zero. Ignore on read.", + ), # Dimensioning info. [0] == rows, [1] == cols etc - _Field('l', 'nDim', help='Number of of items in a dimension -- 0 means no data.', count=MAXDIMS, array=True), - _Field('d', 'sfA', help='Index value for element e of dimension d = sfA[d]*e + sfB[d].', count=MAXDIMS, array=True), - _Field('d', 'sfB', help='Index value for element e of dimension d = sfA[d]*e + sfB[d].', count=MAXDIMS, array=True), + _Field( + "l", + "nDim", + help="Number of of items in a dimension -- 0 means no data.", + count=MAXDIMS, + array=True, + ), + _Field( + "d", + "sfA", + help="Index value for element e of dimension d = sfA[d]*e + sfB[d].", + count=MAXDIMS, + array=True, + ), + _Field( + "d", + "sfB", + help="Index value for element e of dimension d = sfA[d]*e + sfB[d].", + count=MAXDIMS, + array=True, + ), # SI units - _Field('c', 'dataUnits', default=0, help='Natural data units go here - null if none.', count=MAX_UNIT_CHARS+1, array=True), - _Field('c', 'dimUnits', default=0, help='Natural dimension units go here - null if none.', count=(MAXDIMS, MAX_UNIT_CHARS+1), array=True), - _Field('h', 'fsValid', help='TRUE if full scale values have meaning.'), - _Field('h', 'whpad3', default=0, help='Reserved. Write zero. Ignore on read.'), - _Field('d', 'topFullScale', help='The max and max full scale value for wave'), # sic, probably "max and min" - _Field('d', 'botFullScale', help='The max and max full scale value for wave.'), # sic, probably "max and min" - _Field('P', 'dataEUnits', default=0, help='Used in memory only. Write zero. Ignore on read.'), - _Field('P', 'dimEUnits', default=0, help='Used in memory only. Write zero. Ignore on read.', count=MAXDIMS, array=True), - _Field('P', 'dimLabels', default=0, help='Used in memory only. Write zero. Ignore on read.', count=MAXDIMS, array=True), - _Field('P', 'waveNoteH', default=0, help='Used in memory only. Write zero. Ignore on read.'), - _Field('l', 'whUnused', default=0, help='Reserved. Write zero. Ignore on read.', count=16, array=True), + _Field( + "c", + "dataUnits", + default=0, + help="Natural data units go here - null if none.", + count=MAX_UNIT_CHARS + 1, + array=True, + ), + _Field( + "c", + "dimUnits", + default=0, + help="Natural dimension units go here - null if none.", + count=(MAXDIMS, MAX_UNIT_CHARS + 1), + array=True, + ), + _Field("h", "fsValid", help="TRUE if full scale values have meaning."), + _Field("h", "whpad3", default=0, help="Reserved. Write zero. Ignore on read."), + _Field( + "d", "topFullScale", help="The max and max full scale value for wave" + ), # sic, probably "max and min" + _Field( + "d", "botFullScale", help="The max and max full scale value for wave." + ), # sic, probably "max and min" + _Field( + "P", + "dataEUnits", + default=0, + help="Used in memory only. Write zero. Ignore on read.", + ), + _Field( + "P", + "dimEUnits", + default=0, + help="Used in memory only. Write zero. Ignore on read.", + count=MAXDIMS, + array=True, + ), + _Field( + "P", + "dimLabels", + default=0, + help="Used in memory only. Write zero. Ignore on read.", + count=MAXDIMS, + array=True, + ), + _Field( + "P", + "waveNoteH", + default=0, + help="Used in memory only. Write zero. Ignore on read.", + ), + _Field( + "l", + "whUnused", + default=0, + help="Reserved. Write zero. Ignore on read.", + count=16, + array=True, + ), # The following stuff is considered private to Igor. - _Field('h', 'aModified', default=0, help='Used in memory only. Write zero. Ignore on read.'), - _Field('h', 'wModified', default=0, help='Used in memory only. Write zero. Ignore on read.'), - _Field('h', 'swModified', default=0, help='Used in memory only. Write zero. Ignore on read.'), - _Field('c', 'useBits', default=0, help='Used in memory only. Write zero. Ignore on read.'), - _Field('c', 'kindBits', default=0, help='Reserved. Write zero. Ignore on read.'), - _Field('P', 'formula', default=0, help='Used in memory only. Write zero. Ignore on read.'), - _Field('l', 'depID', default=0, help='Used in memory only. Write zero. Ignore on read.'), - _Field('h', 'whpad4', default=0, help='Reserved. Write zero. Ignore on read.'), - _Field('h', 'srcFldr', default=0, help='Used in memory only. Write zero. Ignore on read.'), - _Field('P', 'fileName', default=0, help='Used in memory only. Write zero. Ignore on read.'), - _Field('P', 'sIndices', default=0, help='Used in memory only. Write zero. Ignore on read.'), - ]) - - -class DynamicWaveDataField1 (_DynamicField): + _Field( + "h", + "aModified", + default=0, + help="Used in memory only. Write zero. Ignore on read.", + ), + _Field( + "h", + "wModified", + default=0, + help="Used in memory only. Write zero. Ignore on read.", + ), + _Field( + "h", + "swModified", + default=0, + help="Used in memory only. Write zero. Ignore on read.", + ), + _Field( + "c", + "useBits", + default=0, + help="Used in memory only. Write zero. Ignore on read.", + ), + _Field( + "c", "kindBits", default=0, help="Reserved. Write zero. Ignore on read." + ), + _Field( + "P", + "formula", + default=0, + help="Used in memory only. Write zero. Ignore on read.", + ), + _Field( + "l", + "depID", + default=0, + help="Used in memory only. Write zero. Ignore on read.", + ), + _Field("h", "whpad4", default=0, help="Reserved. Write zero. Ignore on read."), + _Field( + "h", + "srcFldr", + default=0, + help="Used in memory only. Write zero. Ignore on read.", + ), + _Field( + "P", + "fileName", + default=0, + help="Used in memory only. Write zero. Ignore on read.", + ), + _Field( + "P", + "sIndices", + default=0, + help="Used in memory only. Write zero. Ignore on read.", + ), + ], +) + + +class DynamicWaveDataField1(_DynamicField): def pre_pack(self, parents, data): raise NotImplementedError() @@ -274,29 +539,30 @@ def pre_unpack(self, parents, data): wave_structure = parents[-1] wave_header_structure = wave_structure.fields[1].format wave_data = self._get_structure_data(parents, data, wave_structure) - version = data['version'] - bin_header = wave_data['bin_header'] - wave_header = wave_data['wave_header'] + version = data["version"] + bin_header = wave_data["bin_header"] + wave_header = wave_data["wave_header"] - self.count = wave_header['npnts'] + self.count = wave_header["npnts"] self.data_size = self._get_size(bin_header, wave_header_structure.size) - type_ = TYPE_TABLE.get(wave_header['type'], None) + type_ = TYPE_TABLE.get(wave_header["type"], None) if type_: self.shape = self._get_shape(bin_header, wave_header) else: # text wave - type_ = _numpy.dtype('S1') + type_ = _numpy.dtype("S1") self.shape = (self.data_size,) # dtype() wrapping to avoid numpy.generic and # getset_descriptor issues with the builtin numpy types # (e.g. int32). It has no effect on our local complex # integers. - self.dtype = _numpy.dtype(type_).newbyteorder( - wave_structure.byte_order) - if (version == 3 and - self.count > 0 and - bin_header['formulaSize'] > 0 and - self.data_size == 0): + self.dtype = _numpy.dtype(type_).newbyteorder(wave_structure.byte_order) + if ( + version == 3 + and self.count > 0 + and bin_header["formulaSize"] > 0 + and self.data_size == 0 + ): """From TN003: Igor Pro 2.00 included support for dependency formulae. If @@ -317,15 +583,21 @@ def pre_unpack(self, parents, data): without the actual wave data. """ self.shape = (0,) - elif TYPE_TABLE.get(wave_header['type'], None) is not None: + elif TYPE_TABLE.get(wave_header["type"], None) is not None: assert self.data_size == self.count * self.dtype.itemsize, ( - self.data_size, self.count, self.dtype.itemsize, self.dtype) + self.data_size, + self.count, + self.dtype.itemsize, + self.dtype, + ) else: assert self.data_size >= 0, ( - bin_header['wfmSize'], wave_header_structure.size) + bin_header["wfmSize"], + wave_header_structure.size, + ) def _get_size(self, bin_header, wave_header_size): - return bin_header['wfmSize'] - wave_header_size - 16 + return bin_header["wfmSize"] - wave_header_size - 16 def _get_shape(self, bin_header, wave_header): return (self.count,) @@ -337,29 +609,30 @@ def unpack(self, stream): shape=self.shape, dtype=self.dtype, buffer=data_b, - order='F', - ) + order="F", + ) except: _LOG.error( - 'could not reshape data from {} to {}'.format( - self.shape, data_b)) + "could not reshape data from {} to {}".format(self.shape, data_b) + ) raise return data -class DynamicWaveDataField5 (DynamicWaveDataField1): +class DynamicWaveDataField5(DynamicWaveDataField1): "Adds support for multidimensional data." + def _get_size(self, bin_header, wave_header_size): - return bin_header['wfmSize'] - wave_header_size + return bin_header["wfmSize"] - wave_header_size def _get_shape(self, bin_header, wave_header): - return [n for n in wave_header['nDim'] if n > 0] or (0,) + return [n for n in wave_header["nDim"] if n > 0] or (0,) # End IGOR constants and typedefs from IgorBin.h -class DynamicStringField (StaticStringField): +class DynamicStringField(StaticStringField): _size_field = None def pre_unpack(self, parents, data): @@ -374,15 +647,15 @@ def pre_unpack(self, parents, data): def _get_size_data(self, parents, data): wave_structure = parents[-1] wave_data = self._get_structure_data(parents, data, wave_structure) - bin_header = wave_data['bin_header'] + bin_header = wave_data["bin_header"] return bin_header[self._size_field] -class DynamicWaveNoteField (DynamicStringField): - _size_field = 'noteSize' +class DynamicWaveNoteField(DynamicStringField): + _size_field = "noteSize" -class DynamicDependencyFormulaField (DynamicStringField): +class DynamicDependencyFormulaField(DynamicStringField): """Optional wave dependency formula Excerpted from TN003: @@ -392,13 +665,14 @@ class DynamicDependencyFormulaField (DynamicStringField): dependency formula is "sin(x)". The formula is stored with no trailing null byte. """ - _size_field = 'formulaSize' + + _size_field = "formulaSize" # Except when it is stored with a trailing null byte :p. See, for # example, test/data/mac-version3Dependent.ibw. _null_terminated = True -class DynamicDataUnitsField (DynamicStringField): +class DynamicDataUnitsField(DynamicStringField): """Optional extended data units data Excerpted from TN003: @@ -411,10 +685,11 @@ class DynamicDataUnitsField (DynamicStringField): stored using the optional extended data units section of the file. """ - _size_field = 'dataEUnitsSize' + _size_field = "dataEUnitsSize" -class DynamicDimensionUnitsField (DynamicStringField): + +class DynamicDimensionUnitsField(DynamicStringField): """Optional extended dimension units data Excerpted from TN003: @@ -429,11 +704,12 @@ class DynamicDimensionUnitsField (DynamicStringField): supports units of 0 to 3 bytes. Longer units can be stored using the optional extended dimension units section of the file. """ - _size_field = 'dimEUnitsSize' + + _size_field = "dimEUnitsSize" _array_size_field = True -class DynamicLabelsField (DynamicStringField): +class DynamicLabelsField(DynamicStringField): """Optional dimension label data From TN003: @@ -451,13 +727,14 @@ class DynamicLabelsField (DynamicStringField): writes dimension labels to disk, it writes each dimension label as a C string (null-terminated) in a field of 32 bytes. """ - _size_field = 'dimLabelsSize' + + _size_field = "dimLabelsSize" _array_size_field = True def post_unpack(self, parents, data): wave_structure = parents[-1] wave_data = self._get_structure_data(parents, data, wave_structure) - bin_header = wave_data['bin_header'] + bin_header = wave_data["bin_header"] d = wave_data[self.name] dim_labels = [] start = 0 @@ -466,13 +743,13 @@ def post_unpack(self, parents, data): if end > start: dim_data = d[start:end] chunks = [] - for i in range(size//32): - chunks.append(dim_data[32*i:32*(i+1)]) - labels = [b''] + for i in range(size // 32): + chunks.append(dim_data[32 * i : 32 * (i + 1)]) + labels = [b""] for chunk in chunks: - labels[-1] = labels[-1] + b''.join(chunk) - if b'\x00' in chunk: - labels.append(b'') + labels[-1] = labels[-1] + b"".join(chunk) + if b"\x00" in chunk: + labels.append(b"") labels.pop(-1) start = end else: @@ -481,21 +758,21 @@ def post_unpack(self, parents, data): wave_data[self.name] = dim_labels -class DynamicStringIndicesDataField (_DynamicField): - """String indices used for text waves only - """ +class DynamicStringIndicesDataField(_DynamicField): + """String indices used for text waves only""" + def pre_pack(self, parents, data): raise NotImplementedError() def pre_unpack(self, parents, data): wave_structure = parents[-1] wave_data = self._get_structure_data(parents, data, wave_structure) - bin_header = wave_data['bin_header'] - wave_header = wave_data['wave_header'] - self.string_indices_size = bin_header['sIndicesSize'] + bin_header = wave_data["bin_header"] + wave_header = wave_data["wave_header"] + self.string_indices_size = bin_header["sIndicesSize"] self.count = self.string_indices_size // 4 if self.count: # make sure we're in a text wave - assert TYPE_TABLE[wave_header['type']] is None, wave_header + assert TYPE_TABLE[wave_header["type"]] is None, wave_header self.setup() def post_unpack(self, parents, data): @@ -503,45 +780,47 @@ def post_unpack(self, parents, data): return wave_structure = parents[-1] wave_data = self._get_structure_data(parents, data, wave_structure) - wave_header = wave_data['wave_header'] - wdata = wave_data['wData'] + wave_header = wave_data["wave_header"] + wdata = wave_data["wData"] strings = [] start = 0 - for i,offset in enumerate(wave_data['sIndices']): + for i, offset in enumerate(wave_data["sIndices"]): if offset > start: chars = wdata[start:offset] - strings.append(b''.join(chars)) + strings.append(b"".join(chars)) start = offset elif offset == start: - strings.append(b'') + strings.append(b"") else: - raise ValueError((offset, wave_data['sIndices'])) + raise ValueError((offset, wave_data["sIndices"])) wdata = _numpy.array(strings) - shape = [n for n in wave_header['nDim'] if n > 0] or (0,) + shape = [n for n in wave_header["nDim"] if n > 0] or (0,) try: wdata = wdata.reshape(shape) except ValueError: _LOG.error( - 'could not reshape strings from {} to {}'.format( - shape, wdata.shape)) + "could not reshape strings from {} to {}".format(shape, wdata.shape) + ) raise - wave_data['wData'] = wdata + wave_data["wData"] = wdata -class DynamicVersionField (_DynamicField): +class DynamicVersionField(_DynamicField): def pre_pack(self, parents, byte_order): raise NotImplementedError() def post_unpack(self, parents, data): wave_structure = parents[-1] wave_data = self._get_structure_data(parents, data, wave_structure) - version = wave_data['version'] - if wave_structure.byte_order in '@=': + version = wave_data["version"] + if wave_structure.byte_order in "@=": need_to_reorder_bytes = _need_to_reorder_bytes(version) wave_structure.byte_order = _byte_order(need_to_reorder_bytes) _LOG.debug( - 'get byte order from version: {} (reorder? {})'.format( - wave_structure.byte_order, need_to_reorder_bytes)) + "get byte order from version: {} (reorder? {})".format( + wave_structure.byte_order, need_to_reorder_bytes + ) + ) else: need_to_reorder_bytes = False @@ -555,12 +834,14 @@ def post_unpack(self, parents, data): elif version == 5: wave_structure.fields[-1].format = Wave5 elif not need_to_reorder_bytes: - raise ValueError( - 'invalid binary wave version: {}'.format(version)) + raise ValueError("invalid binary wave version: {}".format(version)) if wave_structure.fields[-1].format != old_format: - _LOG.debug('change wave headers from {} to {}'.format( - old_format, wave_structure.fields[-1].format)) + _LOG.debug( + "change wave headers from {} to {}".format( + old_format, wave_structure.fields[-1].format + ) + ) wave_structure.setup() elif need_to_reorder_bytes: wave_structure.setup() @@ -569,7 +850,7 @@ def post_unpack(self, parents, data): return need_to_reorder_bytes -class DynamicWaveField (_DynamicField): +class DynamicWaveField(_DynamicField): def post_unpack(self, parents, data): return raise NotImplementedError() # TODO @@ -581,71 +862,152 @@ def post_unpack(self, parents, data): c = _checksum(b, parents[-1].byte_order, 0, checksum_size) if c != 0: raise ValueError( - ('This does not appear to be a valid Igor binary wave file. ' - 'Error in checksum: should be 0, is {}.').format(c)) + ( + "This does not appear to be a valid Igor binary wave file. " + "Error in checksum: should be 0, is {}." + ).format(c) + ) + Wave1 = _DynamicStructure( - name='Wave1', + name="Wave1", fields=[ - _Field(BinHeader1, 'bin_header', help='Binary wave header'), - _Field(WaveHeader2, 'wave_header', help='Wave header'), - DynamicWaveDataField1('f', 'wData', help='The start of the array of waveform data.', count=0, array=True), - ]) + _Field(BinHeader1, "bin_header", help="Binary wave header"), + _Field(WaveHeader2, "wave_header", help="Wave header"), + DynamicWaveDataField1( + "f", + "wData", + help="The start of the array of waveform data.", + count=0, + array=True, + ), + ], +) Wave2 = _DynamicStructure( - name='Wave2', + name="Wave2", fields=[ - _Field(BinHeader2, 'bin_header', help='Binary wave header'), - _Field(WaveHeader2, 'wave_header', help='Wave header'), - DynamicWaveDataField1('f', 'wData', help='The start of the array of waveform data.', count=0, array=True), - _Field('x', 'padding', help='16 bytes of padding in versions 2 and 3.', count=16, array=True), - DynamicWaveNoteField('c', 'note', help='Optional wave note data', count=0, array=True), - ]) + _Field(BinHeader2, "bin_header", help="Binary wave header"), + _Field(WaveHeader2, "wave_header", help="Wave header"), + DynamicWaveDataField1( + "f", + "wData", + help="The start of the array of waveform data.", + count=0, + array=True, + ), + _Field( + "x", + "padding", + help="16 bytes of padding in versions 2 and 3.", + count=16, + array=True, + ), + DynamicWaveNoteField( + "c", "note", help="Optional wave note data", count=0, array=True + ), + ], +) Wave3 = _DynamicStructure( - name='Wave3', + name="Wave3", fields=[ - _Field(BinHeader3, 'bin_header', help='Binary wave header'), - _Field(WaveHeader2, 'wave_header', help='Wave header'), - DynamicWaveDataField1('f', 'wData', help='The start of the array of waveform data.', count=0, array=True), - _Field('x', 'padding', help='16 bytes of padding in versions 2 and 3.', count=16, array=True), - DynamicWaveNoteField('c', 'note', help='Optional wave note data', count=0, array=True), - DynamicDependencyFormulaField('c', 'formula', help='Optional wave dependency formula', count=0, array=True), - ]) + _Field(BinHeader3, "bin_header", help="Binary wave header"), + _Field(WaveHeader2, "wave_header", help="Wave header"), + DynamicWaveDataField1( + "f", + "wData", + help="The start of the array of waveform data.", + count=0, + array=True, + ), + _Field( + "x", + "padding", + help="16 bytes of padding in versions 2 and 3.", + count=16, + array=True, + ), + DynamicWaveNoteField( + "c", "note", help="Optional wave note data", count=0, array=True + ), + DynamicDependencyFormulaField( + "c", "formula", help="Optional wave dependency formula", count=0, array=True + ), + ], +) Wave5 = _DynamicStructure( - name='Wave5', + name="Wave5", fields=[ - _Field(BinHeader5, 'bin_header', help='Binary wave header'), - _Field(WaveHeader5, 'wave_header', help='Wave header'), - DynamicWaveDataField5('f', 'wData', help='The start of the array of waveform data.', count=0, array=True), - DynamicDependencyFormulaField('c', 'formula', help='Optional wave dependency formula.', count=0, array=True), - DynamicWaveNoteField('c', 'note', help='Optional wave note data.', count=0, array=True), - DynamicDataUnitsField('c', 'data_units', help='Optional extended data units data.', count=0, array=True), - DynamicDimensionUnitsField('c', 'dimension_units', help='Optional dimension label data', count=0, array=True), - DynamicLabelsField('c', 'labels', help="Optional dimension label data", count=0, array=True), - DynamicStringIndicesDataField('P', 'sIndices', help='Dynamic string indices for text waves.', count=0, array=True), - ]) + _Field(BinHeader5, "bin_header", help="Binary wave header"), + _Field(WaveHeader5, "wave_header", help="Wave header"), + DynamicWaveDataField5( + "f", + "wData", + help="The start of the array of waveform data.", + count=0, + array=True, + ), + DynamicDependencyFormulaField( + "c", + "formula", + help="Optional wave dependency formula.", + count=0, + array=True, + ), + DynamicWaveNoteField( + "c", "note", help="Optional wave note data.", count=0, array=True + ), + DynamicDataUnitsField( + "c", + "data_units", + help="Optional extended data units data.", + count=0, + array=True, + ), + DynamicDimensionUnitsField( + "c", + "dimension_units", + help="Optional dimension label data", + count=0, + array=True, + ), + DynamicLabelsField( + "c", "labels", help="Optional dimension label data", count=0, array=True + ), + DynamicStringIndicesDataField( + "P", + "sIndices", + help="Dynamic string indices for text waves.", + count=0, + array=True, + ), + ], +) Wave = _DynamicStructure( - name='Wave', + name="Wave", fields=[ - DynamicVersionField('h', 'version', help='Version number for backwards compatibility.'), - DynamicWaveField(Wave1, 'wave', help='The rest of the wave data.'), - ]) + DynamicVersionField( + "h", "version", help="Version number for backwards compatibility." + ), + DynamicWaveField(Wave1, "wave", help="The rest of the wave data."), + ], +) def load(filename): - if hasattr(filename, 'read'): + if hasattr(filename, "read"): f = filename # filename is actually a stream object else: - f = open(filename, 'rb') + f = open(filename, "rb") try: - Wave.byte_order = '=' + Wave.byte_order = "=" Wave.setup() data = Wave.unpack_stream(f) finally: - if not hasattr(filename, 'read'): + if not hasattr(filename, "read"): f.close() return data diff --git a/igor/igorpy.py b/igor/igorpy.py index b28de2e..efdd247 100644 --- a/igor/igorpy.py +++ b/igor/igorpy.py @@ -35,117 +35,167 @@ from .record.variables import VariablesRecord as _VariablesRecord -__version__='0.10' +__version__ = "0.10" ENCODING = _locale.getpreferredencoding() or _sys.getdefaultencoding() -PYKEYWORDS = set(('and','as','assert','break','class','continue', - 'def','elif','else','except','exec','finally', - 'for','global','if','import','in','is','lambda', - 'or','pass','print','raise','return','try','with', - 'yield')) +PYKEYWORDS = set( + ( + "and", + "as", + "assert", + "break", + "class", + "continue", + "def", + "elif", + "else", + "except", + "exec", + "finally", + "for", + "global", + "if", + "import", + "in", + "is", + "lambda", + "or", + "pass", + "print", + "raise", + "return", + "try", + "with", + "yield", + ) +) PYID = _re.compile(r"^[^\d\W]\w*$", _re.UNICODE) + + def valid_identifier(s): """Check if a name is a valid identifier""" return PYID.match(s) and s not in PYKEYWORDS class IgorObject(object): - """ Parent class for all objects the parser can return """ + """Parent class for all objects the parser can return""" + pass + class Variables(IgorObject): """ Contains system numeric variables (e.g., K0) and user numeric and string variables. """ + def __init__(self, record): - self.sysvar = record.variables['variables']['sysVars'] - self.uservar = record.variables['variables']['userVars'] - self.userstr = record.variables['variables']['userStrs'] - self.depvar = record.variables['variables'].get('dependentVars', {}) - self.depstr = record.variables['variables'].get('dependentStrs', {}) + self.sysvar = record.variables["variables"]["sysVars"] + self.uservar = record.variables["variables"]["userVars"] + self.userstr = record.variables["variables"]["userStrs"] + self.depvar = record.variables["variables"].get("dependentVars", {}) + self.depstr = record.variables["variables"].get("dependentStrs", {}) def format(self, indent=0): - return " "*indent+""\ - %(len(self.sysvar), - len(self.uservar)+len(self.userstr), - len(self.depvar)+len(self.depstr)) + return " " * indent + "" % ( + len(self.sysvar), + len(self.uservar) + len(self.userstr), + len(self.depvar) + len(self.depstr), + ) + class History(IgorObject): """ Contains the experiment's history as plain text. """ + def __init__(self, data): self.data = data + def format(self, indent=0): - return " "*indent+"" + return " " * indent + "" + class Wave(IgorObject): """ Contains the data for a wave """ + def __init__(self, record): - d = record.wave['wave'] - self.name = d['wave_header']['bname'].decode(ENCODING) - self.data = d['wData'] - self.fs = d['wave_header']['fsValid'] - self.fstop = d['wave_header']['topFullScale'] - self.fsbottom = d['wave_header']['botFullScale'] - version = record.wave['version'] - if version in [1,2,3]: - dims = [d['wave_header']['npnts']] + [0]*(_MAXDIMS-1) - sfA = [d['wave_header']['hsA']] + [0]*(_MAXDIMS-1) - sfB = [d['wave_header']['hsB']] + [0]*(_MAXDIMS-1) - self.data_units = [d['wave_header']['dataUnits']] - self.axis_units = [d['wave_header']['xUnits']] + d = record.wave["wave"] + self.name = d["wave_header"]["bname"].decode(ENCODING) + self.data = d["wData"] + self.fs = d["wave_header"]["fsValid"] + self.fstop = d["wave_header"]["topFullScale"] + self.fsbottom = d["wave_header"]["botFullScale"] + version = record.wave["version"] + if version in [1, 2, 3]: + dims = [d["wave_header"]["npnts"]] + [0] * (_MAXDIMS - 1) + sfA = [d["wave_header"]["hsA"]] + [0] * (_MAXDIMS - 1) + sfB = [d["wave_header"]["hsB"]] + [0] * (_MAXDIMS - 1) + self.data_units = [d["wave_header"]["dataUnits"]] + self.axis_units = [d["wave_header"]["xUnits"]] else: - dims = d['wave_header']['nDim'] - sfA = d['wave_header']['sfA'] - sfB = d['wave_header']['sfB'] + dims = d["wave_header"]["nDim"] + sfA = d["wave_header"]["sfA"] + sfB = d["wave_header"]["sfB"] # TODO find example with multiple data units if version == 5: - self.data_units = [d['data_units'].decode(ENCODING)] - self.axis_units = [b''.join(d).decode(ENCODING) - for d in d['wave_header']['dimUnits']] + self.data_units = [d["data_units"].decode(ENCODING)] + self.axis_units = [ + b"".join(d).decode(ENCODING) for d in d["wave_header"]["dimUnits"] + ] else: - self.data_units = [d['data_units'].decode(ENCODING)] - self.axis_units = [d['dimension_units'].decode(ENCODING)] + self.data_units = [d["data_units"].decode(ENCODING)] + self.axis_units = [d["dimension_units"].decode(ENCODING)] - self.data_units.extend(['']*(_MAXDIMS-len(self.data_units))) + self.data_units.extend([""] * (_MAXDIMS - len(self.data_units))) self.data_units = tuple(self.data_units) - self.axis_units.extend(['']*(_MAXDIMS-len(self.axis_units))) + self.axis_units.extend([""] * (_MAXDIMS - len(self.axis_units))) self.axis_units = tuple(self.axis_units) - self.axis = [_numpy.linspace(b,b + a * (c - 1),c) for a,b,c in zip(sfA, sfB, dims)] - self.formula = d.get('formula', '') - self.notes = d.get('note', '') + self.axis = [ + _numpy.linspace(b, b + a * (c - 1), c) for a, b, c in zip(sfA, sfB, dims) + ] + self.formula = d.get("formula", "") + self.notes = d.get("note", "") + def format(self, indent=0): if isinstance(self.data, list): - type,size = "text", "%d"%len(self.data) + type, size = "text", "%d" % len(self.data) else: - type,size = "data", "x".join(str(d) for d in self.data.shape) - return " "*indent+"%s %s (%s)"%(self.name, type, size) + type, size = "data", "x".join(str(d) for d in self.data.shape) + return " " * indent + "%s %s (%s)" % (self.name, type, size) def __array__(self): return self.data __repr__ = __str__ = lambda s: "" % s.format() + class Recreation(IgorObject): """ Contains the experiment's recreation procedures as plain text. """ + def __init__(self, data): self.data = data + def format(self, indent=0): - return " "*indent + "" + return " " * indent + "" + + class Procedure(IgorObject): """ Contains the experiment's main procedure window text as plain text. """ + def __init__(self, data): self.data = data + def format(self, indent=0): - return " "*indent + "" + return " " * indent + "" + + class GetHistory(IgorObject): """ Not a real record but rather, a message to go back and read the history text. @@ -155,33 +205,44 @@ class GetHistory(IgorObject): GetHistory entry simply says that the Recreation has run, and the History can be restored from the previously saved value. """ + def __init__(self, data): self.data = data + def format(self, indent=0): - return " "*indent + "" + return " " * indent + "" + + class PackedFile(IgorObject): """ Contains the data for a procedure file or notebook in packed form. """ + def __init__(self, data): self.data = data + def format(self, indent=0): - return " "*indent + "" + return " " * indent + "" + + class Unknown(IgorObject): """ Record type not documented in PTN003/TN003. """ + def __init__(self, data, type): self.data = data self.type = type + def format(self, indent=0): - return " "*indent + ""%self.type + return " " * indent + "" % self.type class Folder(IgorObject): """ Hierarchical record container. """ + def __init__(self, path): self.name = path[-1] self.path = path @@ -192,9 +253,9 @@ def __getitem__(self, key): return self.children[key] else: for r in self.children: - if isinstance(r, (Folder,Wave)) and r.name == key: + if isinstance(r, (Folder, Wave)) and r.name == key: return r - raise KeyError("Folder %s does not exist"%key) + raise KeyError("Folder %s does not exist" % key) def __str__(self): return "" % "/".join(self.path) @@ -217,9 +278,9 @@ def append(self, record): pass def format(self, indent=0): - parent = " "*indent+self.name - children = [r.format(indent=indent+2) for r in self.children] - return "\n".join([parent]+children) + parent = " " * indent + self.name + children = [r.format(indent=indent + 2) for r in self.children] + return "\n".join([parent] + children) def loads(s, **kwargs): @@ -227,28 +288,31 @@ def loads(s, **kwargs): stream = _io.BytesIO(s) return load(stream, **kwargs) + def load(filename, **kwargs): """Load an igor file""" try: packed_experiment = _load( - filename, initial_byte_order=kwargs.pop('initial_byte_order', '=')) + filename, initial_byte_order=kwargs.pop("initial_byte_order", "=") + ) except ValueError as e: - if e.args[0].startswith('not enough data for the next record header'): - raise IOError('invalid record header; bad pxp file?') - elif e.args[0].startswith('not enough data for the next record'): - raise IOError('final record too long; bad pxp file?') + if e.args[0].startswith("not enough data for the next record header"): + raise IOError("invalid record header; bad pxp file?") + elif e.args[0].startswith("not enough data for the next record"): + raise IOError("final record too long; bad pxp file?") raise return _convert(packed_experiment, **kwargs) + def _convert(packed_experiment, ignore_unknown=True): records, filesystem = packed_experiment - stack = [Folder(path=['root'])] + stack = [Folder(path=["root"])] for record in records: if isinstance(record, _UnknownRecord): if ignore_unknown: continue else: - r = Unknown(record.data, type=record.header['recordType']) + r = Unknown(record.data, type=record.header["recordType"]) elif isinstance(record, _GetHistoryRecord): r = GetHistory(record.text) elif isinstance(record, _HistoryRecord): @@ -267,8 +331,7 @@ def _convert(packed_experiment, ignore_unknown=True): r = None if isinstance(record, _FolderStartRecord): - path = stack[-1].path + [ - record.null_terminated_text.decode(ENCODING)] + path = stack[-1].path + [record.null_terminated_text.decode(ENCODING)] folder = Folder(path) stack[-1].append(folder) stack.append(folder) diff --git a/igor/packed.py b/igor/packed.py index 2035410..0c60270 100644 --- a/igor/packed.py +++ b/igor/packed.py @@ -41,31 +41,38 @@ # listed above. PackedFileRecordHeader = _Structure( - name='PackedFileRecordHeader', + name="PackedFileRecordHeader", fields=[ - _Field('H', 'recordType', help='Record type plus superceded flag.'), - _Field('h', 'version', help='Version information depends on the type of record.'), - _Field('l', 'numDataBytes', help='Number of data bytes in the record following this record header.'), - ]) - -#CR_STR = '\x15' (\r) + _Field("H", "recordType", help="Record type plus superceded flag."), + _Field( + "h", "version", help="Version information depends on the type of record." + ), + _Field( + "l", + "numDataBytes", + help="Number of data bytes in the record following this record header.", + ), + ], +) + +# CR_STR = '\x15' (\r) PACKEDRECTYPE_MASK = 0x7FFF # Record type = (recordType & PACKEDREC_TYPE_MASK) SUPERCEDED_MASK = 0x8000 # Bit is set if the record is superceded by - # a later record in the packed file. +# a later record in the packed file. -def load(filename, strict=True, ignore_unknown=True, initial_byte_order='='): +def load(filename, strict=True, ignore_unknown=True, initial_byte_order="="): """ Probably better to actually infer the initial_byte_order, as can be done from the header. For now though we will let the user deal with this. """ - _LOG.debug('loading a packed experiment file from {}'.format(filename)) + _LOG.debug("loading a packed experiment file from {}".format(filename)) records = [] - if hasattr(filename, 'read'): + if hasattr(filename, "read"): f = filename # filename is actually a stream object else: - f = open(filename, 'rb') + f = open(filename, "rb") byte_order = None try: while True: @@ -76,46 +83,53 @@ def load(filename, strict=True, ignore_unknown=True, initial_byte_order='='): break if len(b) < PackedFileRecordHeader.size: raise ValueError( - ('not enough data for the next record header ({} < {})' - ).format(len(b), PackedFileRecordHeader.size)) - _LOG.debug('reading a new packed experiment file record') + ("not enough data for the next record header ({} < {})").format( + len(b), PackedFileRecordHeader.size + ) + ) + _LOG.debug("reading a new packed experiment file record") header = PackedFileRecordHeader.unpack_from(b) - if header['version'] and not byte_order: - need_to_reorder = _need_to_reorder_bytes(header['version']) + if header["version"] and not byte_order: + need_to_reorder = _need_to_reorder_bytes(header["version"]) byte_order = initial_byte_order = _byte_order(need_to_reorder) _LOG.debug( - 'get byte order from version: {} (reorder? {})'.format( - byte_order, need_to_reorder)) + "get byte order from version: {} (reorder? {})".format( + byte_order, need_to_reorder + ) + ) if need_to_reorder: PackedFileRecordHeader.byte_order = byte_order PackedFileRecordHeader.setup() header = PackedFileRecordHeader.unpack_from(b) - _LOG.debug( - 'reordered version: {}'.format(header['version'])) - data = bytes(f.read(header['numDataBytes'])) - if len(data) < header['numDataBytes']: + _LOG.debug("reordered version: {}".format(header["version"])) + data = bytes(f.read(header["numDataBytes"])) + if len(data) < header["numDataBytes"]: raise ValueError( - ('not enough data for the next record ({} < {})' - ).format(len(b), header['numDataBytes'])) + ("not enough data for the next record ({} < {})").format( + len(b), header["numDataBytes"] + ) + ) record_type = _RECORD_TYPE.get( - header['recordType'] & PACKEDRECTYPE_MASK, _UnknownRecord) - _LOG.debug('the new record has type {} ({}).'.format( - record_type, header['recordType'])) - if record_type in [_UnknownRecord, _UnusedRecord - ] and not ignore_unknown: - raise KeyError('unkown record type {}'.format( - header['recordType'])) + header["recordType"] & PACKEDRECTYPE_MASK, _UnknownRecord + ) + _LOG.debug( + "the new record has type {} ({}).".format( + record_type, header["recordType"] + ) + ) + if record_type in [_UnknownRecord, _UnusedRecord] and not ignore_unknown: + raise KeyError("unkown record type {}".format(header["recordType"])) records.append(record_type(header, data, byte_order=byte_order)) finally: - _LOG.debug('finished loading {} records from {}'.format( - len(records), filename)) - if not hasattr(filename, 'read'): + _LOG.debug("finished loading {} records from {}".format(len(records), filename)) + if not hasattr(filename, "read"): f.close() filesystem = _build_filesystem(records) return (records, filesystem) + def _build_filesystem(records): # From PTN003: """The name must be a valid Igor data folder name. See Object @@ -131,7 +145,7 @@ def _build_filesystem(records): """Like the Macintosh file system, Igor Pro's data folders use the colon character (:) to separate components of a path to an object. This is analogous to Unix which uses / and Windows which - uses \. (Reminder: Igor's data folders exist wholly in memory + uses \\. (Reminder: Igor's data folders exist wholly in memory while an experiment is open. It is not a disk file system!) A data folder named "root" always exists and contains all other @@ -151,8 +165,8 @@ def _build_filesystem(records): " ' : ; """ - filesystem = {'root': {}} - dir_stack = [('root', filesystem['root'])] + filesystem = {"root": {}} + dir_stack = [("root", filesystem["root"])] for record in records: cwd = dir_stack[-1][-1] if isinstance(record, _FolderStartRecord): @@ -163,8 +177,8 @@ def _build_filesystem(records): dir_stack.pop() elif isinstance(record, (_VariablesRecord, _WaveRecord)): if isinstance(record, _VariablesRecord): - sys_vars = record.variables['variables']['sysVars'].keys() - for filename,value in record.namespace.items(): + sys_vars = record.variables["variables"]["sysVars"].keys() + for filename, value in record.namespace.items(): if len(dir_stack) > 1 and filename in sys_vars: # From PTN003: """When reading a packed file, any system @@ -175,23 +189,27 @@ def _build_filesystem(records): _check_filename(dir_stack, filename) cwd[filename] = value else: # WaveRecord - filename = record.wave['wave']['wave_header']['bname'] + filename = record.wave["wave"]["wave_header"]["bname"] _check_filename(dir_stack, filename) cwd[filename] = record return filesystem + def _check_filename(dir_stack, filename): cwd = dir_stack[-1][-1] if filename in cwd: - raise ValueError('collision on name {} in {}'.format( - filename, ':'.join(d for d,cwd in dir_stack))) + raise ValueError( + "collision on name {} in {}".format( + filename, ":".join(d for d, cwd in dir_stack) + ) + ) + def walk(filesystem, callback, dirpath=None): - """Walk a packed experiment filesystem, operating on each key,value pair. - """ + """Walk a packed experiment filesystem, operating on each key,value pair.""" if dirpath is None: dirpath = [] - for key,value in sorted((_bytes(k),v) for k,v in filesystem.items()): + for key, value in sorted((_bytes(k), v) for k, v in filesystem.items()): callback(dirpath, key, value) if isinstance(value, dict): - walk(filesystem=value, callback=callback, dirpath=dirpath+[key]) + walk(filesystem=value, callback=callback, dirpath=dirpath + [key]) diff --git a/igor/script.py b/igor/script.py index 83fde93..127bf1b 100644 --- a/igor/script.py +++ b/igor/script.py @@ -32,36 +32,44 @@ from . import LOG as _LOG -class Script (object): +class Script(object): log_levels = [_logging.ERROR, _logging.WARNING, _logging.INFO, _logging.DEBUG] - def __init__(self, description=None, filetype='IGOR Binary Wave (.ibw) file'): + def __init__(self, description=None, filetype="IGOR Binary Wave (.ibw) file"): self.parser = _argparse.ArgumentParser(description=description) self.parser.add_argument( - '--version', action='version', - version='%(prog)s {}'.format(__version__)) + "--version", action="version", version="%(prog)s {}".format(__version__) + ) self.parser.add_argument( - '-f', '--infile', metavar='FILE', default='-', - help='input {}'.format(filetype)) + "-f", + "--infile", + metavar="FILE", + default="-", + help="input {}".format(filetype), + ) self.parser.add_argument( - '-o', '--outfile', metavar='FILE', default='-', - help='file for ASCII output') + "-o", "--outfile", metavar="FILE", default="-", help="file for ASCII output" + ) self.parser.add_argument( - '-p', '--plot', action='store_const', const=True, - help='use Matplotlib to plot any IGOR waves') + "-p", + "--plot", + action="store_const", + const=True, + help="use Matplotlib to plot any IGOR waves", + ) self.parser.add_argument( - '-V', '--verbose', action='count', default=0, - help='increment verbosity') + "-V", "--verbose", action="count", default=0, help="increment verbosity" + ) self._num_plots = 0 def run(self, *args, **kwargs): args = self.parser.parse_args(*args, **kwargs) - if args.infile == '-': + if args.infile == "-": args.infile = _sys.stdin - if args.outfile == '-': + if args.outfile == "-": args.outfile = _sys.stdout if args.verbose > 1: - log_level = self.log_levels[min(args.verbose-1, len(self.log_levels)-1)] + log_level = self.log_levels[min(args.verbose - 1, len(self.log_levels) - 1)] _LOG.setLevel(log_level) self._run(args) self.display_plots() @@ -75,14 +83,14 @@ def plot_wave(self, args, wave, title=None): if not _matplotlib: raise _matplotlib_import_error if title is None: - title = wave['wave']['wave_header']['bname'] + title = wave["wave"]["wave_header"]["bname"] figure = _matplotlib_pyplot.figure() axes = figure.add_subplot(1, 1, 1) axes.set_title(title) try: - axes.plot(wave['wave']['wData'], 'r.') + axes.plot(wave["wave"]["wData"], "r.") except ValueError as error: - _LOG.error('error plotting {}: {}'.format(title, error)) + _LOG.error("error plotting {}: {}".format(title, error)) pass self._num_plots += 1 diff --git a/igor/struct.py b/igor/struct.py index a50ede5..11f4e79 100644 --- a/igor/struct.py +++ b/igor/struct.py @@ -34,7 +34,7 @@ from . import LOG as _LOG -class Field (object): +class Field(object): """Represent a Structure field. The format argument can be a format character from the ``struct`` @@ -148,8 +148,8 @@ class Field (object): -------- Structure """ - def __init__(self, format, name, default=None, help=None, count=1, - array=False): + + def __init__(self, format, name, default=None, help=None, count=1, array=False): self.format = format self.name = name self.default = default @@ -164,17 +164,18 @@ def setup(self): Use this method to recalculate dynamic properities after changing the basic properties set during initialization. """ - _LOG.debug('setup {}'.format(self)) + _LOG.debug("setup {}".format(self)) self.item_count = _numpy.prod(self.count) # number of item repeats if not self.array and self.item_count != 1: raise ValueError( - '{} must be an array field to have a count of {}'.format( - self, self.count)) + "{} must be an array field to have a count of {}".format( + self, self.count + ) + ) if isinstance(self.format, Structure): - self.structure_count = sum( - f.arg_count for f in self.format.fields) + self.structure_count = sum(f.arg_count for f in self.format.fields) self.arg_count = self.item_count * self.structure_count - elif self.format == 'x': + elif self.format == "x": self.arg_count = 0 # no data in padding bytes else: self.arg_count = self.item_count # struct.Struct format args @@ -183,8 +184,7 @@ def __str__(self): return self.__repr__() def __repr__(self): - return '<{} {} {}>'.format( - self.__class__.__name__, self.name, id(self)) + return "<{} {} {}>".format(self.__class__.__name__, self.name, id(self)) def indexes(self): """Iterate through indexes to a possibly multi-dimensional array""" @@ -197,7 +197,7 @@ def indexes(self): else: for i in range(self.item_count): index = [] - for j,c in enumerate(reversed(self.count)): + for j, c in enumerate(reversed(self.count)): index.insert(0, i % c) i //= c yield index @@ -211,18 +211,17 @@ def pack_data(self, data=None): if self.array: if data is None: data = [] - if hasattr(data, 'flat'): # take advantage of numpy's ndarray.flat + if hasattr(data, "flat"): # take advantage of numpy's ndarray.flat items = 0 for item in data.flat: items += 1 for arg in self.pack_item(item): yield arg - if items < self.item_count: - if f.default is None: - raise ValueError( - 'no default for {}.{}'.format(self, f)) - for i in range(self.item_count - items): - yield f.default + # if items < self.item_count: + # if f.default is None: + # raise ValueError("no default for {}.{}".format(self, f)) + # for i in range(self.item_count - items): + # yield f.default else: for index in self.indexes(): try: @@ -241,32 +240,31 @@ def pack_data(self, data=None): yield arg def pack_item(self, item=None): - """Linearize a single count of the field's data to a flat iterable - """ + """Linearize a single count of the field's data to a flat iterable""" if isinstance(self.format, Structure): for i in self.format._pack_item(item): yield i elif item is None: if self.default is None: - raise ValueError('no default for {}'.format(self)) + raise ValueError("no default for {}".format(self)) yield self.default else: yield item def unpack_data(self, data): """Inverse of .pack_data""" - _LOG.debug('unpack {} for {} {}'.format(data, self, self.format)) + _LOG.debug("unpack {} for {} {}".format(data, self, self.format)) iterator = iter(data) try: items = [next(iterator) for i in range(self.arg_count)] except StopIteration: - raise ValueError('not enough data to unpack {}'.format(self)) + raise ValueError("not enough data to unpack {}".format(self)) try: next(iterator) except StopIteration: pass else: - raise ValueError('too much data to unpack {}'.format(self)) + raise ValueError("too much data to unpack {}".format(self)) if isinstance(self.format, Structure): # break into per-structure clumps s = self.structure_count @@ -287,11 +285,12 @@ def unpack_data(self, data): except TypeError: pass else: - raise NotImplementedError('reshape Structure field') + raise NotImplementedError("reshape Structure field") else: unpacked = _numpy.array(unpacked) - _LOG.debug('reshape {} data from {} to {}'.format( - self, unpacked.shape, count)) + _LOG.debug( + "reshape {} data from {} to {}".format(self, unpacked.shape, count) + ) unpacked = unpacked.reshape(count) return unpacked @@ -304,14 +303,14 @@ def unpack_item(self, item): return item[0] -class DynamicField (Field): +class DynamicField(Field): """Represent a DynamicStructure field with a dynamic definition. Adds the methods ``.pre_pack``, ``pre_unpack``, and ``post_unpack``, all of which are called when a ``DynamicField`` is used by a ``DynamicStructure``. Each method takes the arguments ``(parents, data)``, where ``parents`` is a list of - ``DynamicStructure``\s that own the field and ``data`` is a dict + ``DynamicStructure``s that own the field and ``data`` is a dict hierarchy of the structure data. See the ``DynamicStructure`` docstring for the exact timing of the @@ -321,6 +320,7 @@ class DynamicField (Field): -------- Field, DynamicStructure """ + def pre_pack(self, parents, data): "Prepare to pack." pass @@ -334,8 +334,7 @@ def post_unpack(self, parents, data): pass def _get_structure_data(self, parents, data, structure): - """Extract the data belonging to a particular ancestor structure. - """ + """Extract the data belonging to a particular ancestor structure.""" d = data s = parents[0] if s == structure: @@ -352,7 +351,7 @@ def _get_structure_data(self, parents, data, structure): return d -class Structure (_struct.Struct): +class Structure(_struct.Struct): r"""Represent a C structure. A convenient wrapper around struct.Struct that uses Fields and @@ -479,9 +478,10 @@ class Structure (_struct.Struct): >>> b2 == b True """ - _byte_order_symbols = '@=<>!' - def __init__(self, name, fields, byte_order='@'): + _byte_order_symbols = "@=<>!" + + def __init__(self, name, fields, byte_order="@"): # '=' for native byte order, standard size and alignment # See http://docs.python.org/library/struct for details self.name = name @@ -493,8 +493,7 @@ def __str__(self): return self.name def __repr__(self): - return '<{} {} {}>'.format( - self.__class__.__name__, self.name, id(self)) + return "<{} {} {}>".format(self.__class__.__name__, self.name, id(self)) def setup(self): """Setup any dynamic properties of a structure. @@ -502,24 +501,23 @@ def setup(self): Use this method to recalculate dynamic properities after changing the basic properties set during initialization. """ - _LOG.debug('setup {!r}'.format(self)) + _LOG.debug("setup {!r}".format(self)) self.set_byte_order(self.byte_order) self.get_format() def set_byte_order(self, byte_order): - """Allow changing the format byte_order on the fly. - """ - _LOG.debug('set byte order for {!r} to {}'.format(self, byte_order)) + """Allow changing the format byte_order on the fly.""" + _LOG.debug("set byte order for {!r} to {}".format(self, byte_order)) self.byte_order = byte_order for field in self.fields: if isinstance(field.format, Structure): field.format.set_byte_order(byte_order) def get_format(self): - format = self.byte_order + ''.join(self.sub_format()) + format = self.byte_order + "".join(self.sub_format()) # P format only allowed for native byte ordering # Convert P to I for ILP32 compatibility when running on a LP64. - format = format.replace('P', 'I') + format = format.replace("P", "I") try: super(Structure, self).__init__(format=format) except _struct.error as e: @@ -527,19 +525,17 @@ def get_format(self): return format def sub_format(self): - _LOG.debug('calculate sub-format for {!r}'.format(self)) + _LOG.debug("calculate sub-format for {!r}".format(self)) for field in self.fields: if isinstance(field.format, Structure): - field_format = list( - field.format.sub_format()) * field.item_count + field_format = list(field.format.sub_format()) * field.item_count else: - field_format = [field.format]*field.item_count + field_format = [field.format] * field.item_count for fmt in field_format: yield fmt def _pack_item(self, item=None): - """Linearize a single count of the structure's data to a flat iterable - """ + """Linearize a single count of the structure's data to a flat iterable""" if item is None: item = {} for f in self.fields: @@ -560,15 +556,14 @@ def _unpack_item(self, args): try: items = [next(iterator) for i in range(f.arg_count)] except StopIteration: - raise ValueError('not enough data to unpack {}.{}'.format( - self, f)) + raise ValueError("not enough data to unpack {}.{}".format(self, f)) data[f.name] = f.unpack_data(items) try: next(iterator) except StopIteration: pass else: - raise ValueError('too much data to unpack {}'.format(self)) + raise ValueError("too much data to unpack {}".format(self)) return data def pack(self, data): @@ -580,8 +575,7 @@ def pack(self, data): def pack_into(self, buffer, offset=0, data={}): args = list(self._pack_item(data)) - return super(Structure, self).pack_into( - buffer, offset, *args) + return super(Structure, self).pack_into(buffer, offset, *args) def unpack(self, *args, **kwargs): args = super(Structure, self).unpack(*args, **kwargs) @@ -589,28 +583,30 @@ def unpack(self, *args, **kwargs): def unpack_from(self, buffer, offset=0, *args, **kwargs): _LOG.debug( - 'unpack {!r} for {!r} ({}, offset={}) with {} ({})'.format( - buffer, self, len(buffer), offset, self.format, self.size)) - args = super(Structure, self).unpack_from( - buffer, offset, *args, **kwargs) + "unpack {!r} for {!r} ({}, offset={}) with {} ({})".format( + buffer, self, len(buffer), offset, self.format, self.size + ) + ) + args = super(Structure, self).unpack_from(buffer, offset, *args, **kwargs) return self._unpack_item(args) def get_field(self, name): return [f for f in self.fields if f.name == name][0] -class DebuggingStream (object): +class DebuggingStream(object): def __init__(self, stream): self.stream = stream def read(self, size): data = self.stream.read(size) - _LOG.debug('read {} from {}: ({}) {!r}'.format( - size, self.stream, len(data), data)) + _LOG.debug( + "read {} from {}: ({}) {!r}".format(size, self.stream, len(data), data) + ) return data -class DynamicStructure (Structure): +class DynamicStructure(Structure): r"""Represent a C structure field with a dynamic definition. Any dynamic fields have their ``.pre_pack`` called before any @@ -698,7 +694,8 @@ class DynamicStructure (Structure): for ``Structure``, because we must make multiple calls to ``struct.Struct.unpack`` to unpack the data. """ - #def __init__(self, *args, **kwargs): + + # def __init__(self, *args, **kwargs): # pass #self.parent = .. def _pre_pack(self, parents=None, data=None): @@ -707,11 +704,11 @@ def _pre_pack(self, parents=None, data=None): else: parents = parents + [self] for f in self.fields: - if hasattr(f, 'pre_pack'): - _LOG.debug('pre-pack {}'.format(f)) + if hasattr(f, "pre_pack"): + _LOG.debug("pre-pack {}".format(f)) f.pre_pack(parents=parents, data=data) if isinstance(f.format, DynamicStructure): - _LOG.debug('pre-pack {!r}'.format(f.format)) + _LOG.debug("pre-pack {!r}".format(f.format)) f._pre_pack(parents=parents, data=data) def pack(self, data): @@ -723,7 +720,8 @@ def pack_into(self, buffer, offset=0, data={}): self._pre_pack(data=data) self.setup() return super(DynamicStructure, self).pack_into( - buffer=buffer, offset=offset, data=data) + buffer=buffer, offset=offset, data=data + ) def unpack_stream(self, stream, parents=None, data=None, d=None): # `d` is the working data directory @@ -736,16 +734,19 @@ def unpack_stream(self, stream, parents=None, data=None, d=None): parents = parents + [self] for f in self.fields: - _LOG.debug('parsing {!r}.{} (count={}, item_count={})'.format( - self, f, f.count, f.item_count)) + _LOG.debug( + "parsing {!r}.{} (count={}, item_count={})".format( + self, f, f.count, f.item_count + ) + ) if _LOG.level <= _logging.DEBUG: - _LOG.debug('data:\n{}'.format(_pprint.pformat(data))) - if hasattr(f, 'pre_unpack'): - _LOG.debug('pre-unpack {}'.format(f)) + _LOG.debug("data:\n{}".format(_pprint.pformat(data))) + if hasattr(f, "pre_unpack"): + _LOG.debug("pre-unpack {}".format(f)) f.pre_unpack(parents=parents, data=data) - if hasattr(f, 'unpack'): # override default unpacking - _LOG.debug('override unpack for {}'.format(f)) + if hasattr(f, "unpack"): # override default unpacking + _LOG.debug("override unpack for {}".format(f)) d[f.name] = f.unpack(stream) continue @@ -761,23 +762,28 @@ def unpack_stream(self, stream, parents=None, data=None, d=None): x = {} d[f.name].append(x) f.format.unpack_stream( - stream, parents=parents, data=data, d=x) + stream, parents=parents, data=data, d=x + ) else: assert f.item_count == 1, (f, f.count) d[f.name] = {} f.format.unpack_stream( - stream, parents=parents, data=data, d=d[f.name]) - if hasattr(f, 'post_unpack'): - _LOG.debug('post-unpack {}'.format(f)) + stream, parents=parents, data=data, d=d[f.name] + ) + if hasattr(f, "post_unpack"): + _LOG.debug("post-unpack {}".format(f)) repeat = f.post_unpack(parents=parents, data=data) if repeat: raise NotImplementedError( - 'cannot repeat unpack for dynamic structures') + "cannot repeat unpack for dynamic structures" + ) continue if isinstance(f.format, Structure): - _LOG.debug('parsing {} bytes for {}'.format( - f.format.size, f.format.format)) + _LOG.debug( + "parsing {} bytes for {}".format(f.format.size, f.format.format) + ) bs = [stream.read(f.format.size) for i in range(f.item_count)] + def unpack(): f.format.set_byte_order(self.byte_order) f.setup() @@ -787,27 +793,31 @@ def unpack(): assert len(x) == 1, (f, f.count, x) x = x[0] return x + else: - field_format = self.byte_order + f.format*f.item_count - field_format = field_format.replace('P', 'I') + field_format = self.byte_order + f.format * f.item_count + field_format = field_format.replace("P", "I") try: size = _struct.calcsize(field_format) except _struct.error as e: _LOG.error(e) - _LOG.error('{}.{}: {}'.format(self, f, field_format)) + _LOG.error("{}.{}: {}".format(self, f, field_format)) raise - _LOG.debug('parsing {} bytes for preliminary {}'.format( - size, field_format)) + _LOG.debug( + "parsing {} bytes for preliminary {}".format(size, field_format) + ) raw = stream.read(size) if len(raw) < size: raise ValueError( - 'not enough data to unpack {}.{} ({} < {})'.format( - self, f, len(raw), size)) + "not enough data to unpack {}.{} ({} < {})".format( + self, f, len(raw), size + ) + ) + def unpack(): - field_format = self.byte_order + f.format*f.item_count - field_format = field_format.replace('P', 'I') - _LOG.debug('parse previous bytes using {}'.format( - field_format)) + field_format = self.byte_order + f.format * f.item_count + field_format = field_format.replace("P", "I") + _LOG.debug("parse previous bytes using {}".format(field_format)) struct = _struct.Struct(field_format) items = struct.unpack(raw) return f.unpack_data(items) @@ -816,13 +826,13 @@ def unpack(): repeat = True while repeat: d[f.name] = unpack() - if hasattr(f, 'post_unpack'): - _LOG.debug('post-unpack {}'.format(f)) + if hasattr(f, "post_unpack"): + _LOG.debug("post-unpack {}".format(f)) repeat = f.post_unpack(parents=parents, data=data) else: repeat = False if repeat: - _LOG.debug('repeat unpack for {}'.format(f)) + _LOG.debug("repeat unpack for {}".format(f)) return data @@ -831,6 +841,5 @@ def unpack(self, string): return self.unpack_stream(stream) def unpack_from(self, buffer, offset=0, *args, **kwargs): - args = super(Structure, self).unpack_from( - buffer, offset, *args, **kwargs) + args = super(Structure, self).unpack_from(buffer, offset, *args, **kwargs) return self._unpack_item(args) diff --git a/igor/util.py b/igor/util.py index ecc783a..3550336 100644 --- a/igor/util.py +++ b/igor/util.py @@ -34,6 +34,7 @@ def _ord(byte): else: return ord(byte) + def hex_bytes(buffer, spaces=None): r"""Pretty-printing for binary buffers. @@ -48,14 +49,15 @@ def hex_bytes(buffer, spaces=None): >>> hex_bytes(b'\x00\x01\x02\x03\x04\x05\x06', spaces=3) '000102 030405 06' """ - hex_bytes = ['{:02x}'.format(_ord(x)) for x in buffer] + hex_bytes = ["{:02x}".format(_ord(x)) for x in buffer] if spaces is None: - return ''.join(hex_bytes) - elif spaces is 1: - return ' '.join(hex_bytes) - for i in range(len(hex_bytes)//spaces): - hex_bytes.insert((spaces+1)*(i+1)-1, ' ') - return ''.join(hex_bytes) + return "".join(hex_bytes) + elif spaces == 1: + return " ".join(hex_bytes) + for i in range(len(hex_bytes) // spaces): + hex_bytes.insert((spaces + 1) * (i + 1) - 1, " ") + return "".join(hex_bytes) + def assert_null(buffer, strict=True): r"""Ensure an input buffer is entirely zero. @@ -79,16 +81,19 @@ def assert_null(buffer, strict=True): raise ValueError(hex_string) else: _sys.stderr.write( - 'warning: post-data padding not zero: {}\n'.format(hex_string)) + "warning: post-data padding not zero: {}\n".format(hex_string) + ) + # From ReadWave.c def byte_order(needToReorderBytes): - little_endian = _sys.byteorder == 'little' + little_endian = _sys.byteorder == "little" if needToReorderBytes: little_endian = not little_endian if little_endian: - return '<' # little-endian - return '>' # big-endian + return "<" # little-endian + return ">" # big-endian + # From ReadWave.c def need_to_reorder_bytes(version): @@ -98,20 +103,23 @@ def need_to_reorder_bytes(version): # reordered. return version & 0xFF == 0 + # From ReadWave.c def checksum(buffer, byte_order, oldcksum, numbytes): x = _numpy.ndarray( - (numbytes/2,), # 2 bytes to a short -- ignore trailing odd byte - dtype=_numpy.dtype(byte_order+'h'), - buffer=buffer) + (numbytes / 2,), # 2 bytes to a short -- ignore trailing odd byte + dtype=_numpy.dtype(byte_order + "h"), + buffer=buffer, + ) oldcksum += x.sum() if oldcksum > 2**31: # fake the C implementation's int rollover oldcksum %= 2**32 if oldcksum > 2**31: oldcksum -= 2**31 - return oldcksum & 0xffff + return oldcksum & 0xFFFF + -def _bytes(obj, encoding='utf-8'): +def _bytes(obj, encoding="utf-8"): """Convert bytes or strings into bytes >>> _bytes(b'123') From 5dacde36f3fd91dc12ba9a6ecf3b486dbb76138e Mon Sep 17 00:00:00 2001 From: jgobbo Date: Tue, 12 Aug 2025 09:18:51 -0700 Subject: [PATCH 3/3] uv, renaming, and publishing --- .gitignore | 6 +- .python-version | 1 + COPYING.LESSER | 165 --- MANIFEST.in | 2 - README | 138 -- README.md | 21 + bin/igorbinarywave.py | 42 - bin/igorpackedexperiment.py | 54 - igor/record/variables.py | 319 ----- igor/script.py | 99 -- pyproject.toml | 22 + setup.py | 67 - {igor => src/pygor}/__init__.py | 21 +- {igor => src/pygor}/binarywave.py | 343 +++-- igor/igorpy.py => src/pygor/core.py | 92 +- {igor => src/pygor}/packed.py | 80 +- {igor => src/pygor}/record/__init__.py | 10 +- {igor => src/pygor}/record/base.py | 28 +- {igor => src/pygor}/record/folder.py | 12 +- {igor => src/pygor}/record/history.py | 14 +- {igor => src/pygor}/record/packedfile.py | 10 +- {igor => src/pygor}/record/procedure.py | 10 +- src/pygor/record/variables.py | 371 ++++++ {igor => src/pygor}/record/wave.py | 16 +- {igor => src/pygor}/struct.py | 78 +- {igor => src/pygor}/util.py | 54 +- test/__init__.py | 0 test/test-igorpy.py | 202 --- test/test.py | 1500 ---------------------- test/test_windows.py | 30 + uv.lock | 120 ++ 31 files changed, 881 insertions(+), 3046 deletions(-) create mode 100644 .python-version delete mode 100644 COPYING.LESSER delete mode 100644 MANIFEST.in delete mode 100644 README create mode 100644 README.md delete mode 100755 bin/igorbinarywave.py delete mode 100755 bin/igorpackedexperiment.py delete mode 100644 igor/record/variables.py delete mode 100644 igor/script.py create mode 100644 pyproject.toml delete mode 100644 setup.py rename {igor => src/pygor}/__init__.py (51%) rename {igor => src/pygor}/binarywave.py (76%) rename igor/igorpy.py => src/pygor/core.py (81%) rename {igor => src/pygor}/packed.py (72%) rename {igor => src/pygor}/record/__init__.py (82%) rename {igor => src/pygor}/record/base.py (55%) rename {igor => src/pygor}/record/folder.py (62%) rename {igor => src/pygor}/record/history.py (60%) rename {igor => src/pygor}/record/packedfile.py (64%) rename {igor => src/pygor}/record/procedure.py (64%) create mode 100644 src/pygor/record/variables.py rename {igor => src/pygor}/record/wave.py (61%) rename {igor => src/pygor}/struct.py (90%) rename {igor => src/pygor}/util.py (72%) create mode 100644 test/__init__.py delete mode 100644 test/test-igorpy.py delete mode 100644 test/test.py create mode 100644 test/test_windows.py create mode 100644 uv.lock diff --git a/.gitignore b/.gitignore index 04fd334..ad3b40d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,8 @@ __pycache__ *.pyc build/ -igor.egg-info/ \ No newline at end of file +pygor.egg-info/ + +dist/ +.venv/ +.pytest_cache/ \ No newline at end of file diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..24ee5b1 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.13 diff --git a/COPYING.LESSER b/COPYING.LESSER deleted file mode 100644 index fc8a5de..0000000 --- a/COPYING.LESSER +++ /dev/null @@ -1,165 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index f6725a8..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,2 +0,0 @@ -include COPYING -include COPYING.LESSER diff --git a/README b/README deleted file mode 100644 index dc21b62..0000000 --- a/README +++ /dev/null @@ -1,138 +0,0 @@ -==== -Igor -==== - -:Authors: W. Trevor King ; - Paul Kienzle -:License: GNU General Public License, version 3+ - -Python parsers for Igor Binary Waves (.ibw) and Packed Experiment -(.pxp) files written by WaveMetrics' IGOR Pro software. - -Installation -============ - -Packages --------- - -If `igor` already exists in your package manager's repository, you -should install `igor` in the usual way. - -Gentoo -~~~~~~ - -I've packaged `igor` for Gentoo. You need layman_ and my `wtk -overlay`_. Install with:: - - # emerge -av app-portage/layman - # layman --add wtk - # emerge -av sci-misc/igor - -Dependencies ------------- - -If you're installing by hand or packaging `igor` for another -distribution, you'll need the following dependencies: - -=========== ================= ============================ -Package Debian_ Gentoo_ -=========== ================= ============================ -Numpy_ python-numpy dev-python/numpy -Matplotlib_ python-matplotlib dev-python/matplotlib -Nose_ python-nose dev-python/nose -=========== ================= ============================ - -Installing by hand ------------------- - -`igor` is available as a Git_ repository:: - - $ git clone git://tremily.us/igor.git - -See the homepage_ for details. To install the checkout, run the -standard:: - - $ python setup.py install - -You can also automate this installation with pip_:: - - $ pip install igor - -Usage -===== - -See the docstrings and unit tests for examples using the Python API. -The package also installs to scripts, ``igorbinarywave.py`` and -``igorpackedexperiment.py`` which can be used to dump files to stdout. -For details on their usage, use the ``--help`` option. For example:: - - $ igorbinarywave.py --help - -For users transitioning from igor.py_, there's a compatibility module -exposing the old interface. Just change:: - - import igor - -to:: - - import igor.igorpy as igor - -in your calling code. - -Testing -======= - -Run internal unit tests with:: - - $ nosetests --with-doctest --doctest-tests igor test - -The data in the ``test/data`` directory is in the Git repository, but -it is not bundled with the source code. If you want the test data, -you'll have to clone the Git repository or download a snapshot. - -Licence -======= - -This project is distributed under the `GNU Lesser General Public -License Version 3`_ or greater, see the ``COPYING`` file distributed -with the project for details. - -Maintenance -=========== - -Maintainer ----------- - -W. Trevor King -wking@tremily.us -Copyright 2008-2012 - -Release procedure ------------------ - -When a new version of the package is ready, increment __version__ -in ``igor/__init__.py`` and run update-copyright_:: - - $ update-copyright.py - -to update the copyright blurbs. Then run:: - - $ python setup.py sdist upload - -This will place a new version on PyPI. - - -.. _layman: http://layman.sourceforge.net/ -.. _wtk overlay: http://blog.tremily.us/posts/Gentoo_overlay/ -.. _Debian: http://www.debian.org/ -.. _Gentoo: http://www.gentoo.org/ -.. _NumPy: http://numpy.scipy.org/ -.. _Matplotlib: http://matplotlib.sourceforge.net/ -.. _Nose: http://somethingaboutorange.com/mrl/projects/nose/ -.. _Git: http://git-scm.com/ -.. _homepage: http://blog.tremily.us/posts/igor/ -.. _pip: http://pypi.python.org/pypi/pip -.. _igor.py: http://pypi.python.org/pypi/igor.py -.. _GNU Lesser General Public License Version 3: - http://www.gnu.org/licenses/lgpl.txt -.. _update-copyright: http://blog.tremily.us/posts/update-copyright/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..2de0aa3 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +Pygor is a package for loading binary IGOR files. It's forked from [chstan's igorpy](https://github.com/chstan/igorpy) which is forked from [igor](https://github.com/wking/igor). The main purpose of pygor is to facilitate the loading of pxt files for [arpes-lite](https://github.com/jgobbo/arpes-lite). + +Installation +------------ + +pygor is most easily installed with pip: `pip install pygor` + +you can also clone the repo and install with [uv](https://docs.astral.sh/uv/) + +Usage +----- + +Standard usage is straightforward: + +```python +from pygor import load + +byte_order = "<" # options are "<", "=", ">" +data = load(IGOR_FILE_NAME, initial_byte_order=byte_order) +``` + diff --git a/bin/igorbinarywave.py b/bin/igorbinarywave.py deleted file mode 100755 index 70c1bc3..0000000 --- a/bin/igorbinarywave.py +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2012 W. Trevor King -# -# This file is part of igor. -# -# igor is free software: you can redistribute it and/or modify it under the -# terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 3 of the License, or (at your option) any -# later version. -# -# igor is distributed in the hope that it will be useful, but WITHOUT ANY -# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR -# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with igor. If not, see . - -"IBW -> ASCII conversion" - -import pprint - -import numpy - -from igor.binarywave import load -from igor.script import Script - - -class WaveScript (Script): - def _run(self, args): - wave = load(args.infile) - numpy.savetxt( - args.outfile, wave['wave']['wData'], fmt='%g', delimiter='\t') - self.plot_wave(args, wave) - if args.verbose > 0: - wave['wave'].pop('wData') - pprint.pprint(wave) - - -s = WaveScript(description=__doc__) -s.run() diff --git a/bin/igorpackedexperiment.py b/bin/igorpackedexperiment.py deleted file mode 100755 index 0a08444..0000000 --- a/bin/igorpackedexperiment.py +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2012 W. Trevor King -# -# This file is part of igor. -# -# igor is free software: you can redistribute it and/or modify it under the -# terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 3 of the License, or (at your option) any -# later version. -# -# igor is distributed in the hope that it will be useful, but WITHOUT ANY -# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR -# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with igor. If not, see . - -"PXP -> ASCII conversion" - -import pprint - -from igor.packed import load, walk -from igor.record.wave import WaveRecord -from igor.script import Script - - -class PackedScript (Script): - def _run(self, args): - self.args = args - records,filesystem = load(args.infile) - if hasattr(args.outfile, 'write'): - f = args.outfile # filename is actually a stream object - else: - f = open(args.outfile, 'w') - try: - f.write(pprint.pformat(records)) - f.write('\n') - finally: - if f != args.outfile: - f.close() - if args.verbose > 0: - pprint.pprint(filesystem) - walk(filesystem, self._plot_wave_callback) - - def _plot_wave_callback(self, dirpath, key, value): - if isinstance(value, WaveRecord): - self.plot_wave(self.args, value.wave, title=dirpath + [key]) - - -s = PackedScript( - description=__doc__, filetype='IGOR Packed Experiment (.pxp) file') -s.run() diff --git a/igor/record/variables.py b/igor/record/variables.py deleted file mode 100644 index a8eaccf..0000000 --- a/igor/record/variables.py +++ /dev/null @@ -1,319 +0,0 @@ -# Copyright (C) 2012 W. Trevor King -# -# This file is part of igor. -# -# igor is free software: you can redistribute it and/or modify it under the -# terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 3 of the License, or (at your option) any -# later version. -# -# igor is distributed in the hope that it will be useful, but WITHOUT ANY -# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR -# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with igor. If not, see . - -import io as _io - -from .. import LOG as _LOG -from ..binarywave import TYPE_TABLE as _TYPE_TABLE -from ..binarywave import NullStaticStringField as _NullStaticStringField -from ..binarywave import DynamicStringField as _DynamicStringField -from ..struct import Structure as _Structure -from ..struct import DynamicStructure as _DynamicStructure -from ..struct import Field as _Field -from ..struct import DynamicField as _DynamicField -from ..util import byte_order as _byte_order -from ..util import need_to_reorder_bytes as _need_to_reorder_bytes -from .base import Record - - -class ListedStaticStringField (_NullStaticStringField): - """Handle string conversions for multi-count dynamic parents. - - If a field belongs to a multi-count dynamic parent, the parent is - called multiple times to parse each count, and the field's - post-unpack hook gets called after the field is unpacked during - each iteration. This requires alternative logic for getting and - setting the string data. The actual string formatting code is not - affected. - """ - def post_unpack(self, parents, data): - parent_structure = parents[-1] - parent_data = self._get_structure_data(parents, data, parent_structure) - d = self._normalize_string(parent_data[-1][self.name]) - parent_data[-1][self.name] = d - - -class ListedStaticStringField (_NullStaticStringField): - """Handle string conversions for multi-count dynamic parents. - - If a field belongs to a multi-count dynamic parent, the parent is - called multiple times to parse each count, and the field's - post-unpack hook gets called after the field is unpacked during - each iteration. This requires alternative logic for getting and - setting the string data. The actual string formatting code is not - affected. - """ - def post_unpack(self, parents, data): - parent_structure = parents[-1] - parent_data = self._get_structure_data(parents, data, parent_structure) - d = self._normalize_string(parent_data[-1][self.name]) - parent_data[-1][self.name] = d - - -class ListedDynamicStrDataField (_DynamicStringField, ListedStaticStringField): - _size_field = 'strLen' - _null_terminated = False - - def _get_size_data(self, parents, data): - parent_structure = parents[-1] - parent_data = self._get_structure_data(parents, data, parent_structure) - return parent_data[-1][self._size_field] - - -class DynamicVarDataField (_DynamicField): - def __init__(self, *args, **kwargs): - if 'array' not in kwargs: - kwargs['array'] = True - super(DynamicVarDataField, self).__init__(*args, **kwargs) - - def pre_pack(self, parents, data): - raise NotImplementedError() - - def post_unpack(self, parents, data): - var_structure = parents[-1] - var_data = self._get_structure_data(parents, data, var_structure) - data = var_data[self.name] - d = {} - for i,value in enumerate(data): - key,value = self._normalize_item(i, value) - d[key] = value - var_data[self.name] = d - - def _normalize_item(self, index, value): - raise NotImplementedError() - - -class DynamicSysVarField (DynamicVarDataField): - def _normalize_item(self, index, value): - name = 'K{}'.format(index) - return (name, value) - - -class DynamicUserVarField (DynamicVarDataField): - def _normalize_item(self, index, value): - name = value['name'] - value = value['num'] - return (name, value) - - -class DynamicUserStrField (DynamicVarDataField): - def _normalize_item(self, index, value): - name = value['name'] - value = value['data'] - return (name, value) - - -class DynamicVarNumField (_DynamicField): - def post_unpack(self, parents, data): - parent_structure = parents[-1] - parent_data = self._get_structure_data(parents, data, parent_structure) - d = self._normalize_numeric_variable(parent_data[-1][self.name]) - parent_data[-1][self.name] = d - - def _normalize_numeric_variable(self, num_var): - t = _TYPE_TABLE[num_var['numType']] - if num_var['numType'] % 2: # complex number - return t(complex(num_var['realPart'], num_var['imagPart'])) - else: - return t(num_var['realPart']) - - -class DynamicFormulaField (_DynamicStringField): - _size_field = 'formulaLen' - _null_terminated = True - - -# From Variables.h -VarHeader1 = _Structure( # `version` field pulled out into VariablesRecord - name='VarHeader1', - fields=[ - _Field('h', 'numSysVars', help='Number of system variables (K0, K1, ...).'), - _Field('h', 'numUserVars', help='Number of user numeric variables -- may be zero.'), - _Field('h', 'numUserStrs', help='Number of user string variables -- may be zero.'), - ]) - -# From Variables.h -VarHeader2 = _Structure( # `version` field pulled out into VariablesRecord - name='VarHeader2', - fields=[ - _Field('h', 'numSysVars', help='Number of system variables (K0, K1, ...).'), - _Field('h', 'numUserVars', help='Number of user numeric variables -- may be zero.'), - _Field('h', 'numUserStrs', help='Number of user string variables -- may be zero.'), - _Field('h', 'numDependentVars', help='Number of dependent numeric variables -- may be zero.'), - _Field('h', 'numDependentStrs', help='Number of dependent string variables -- may be zero.'), - ]) - -# From Variables.h -UserStrVarRec1 = _DynamicStructure( - name='UserStrVarRec1', - fields=[ - ListedStaticStringField('c', 'name', help='Name of the string variable.', count=32), - _Field('h', 'strLen', help='The real size of the following array.'), - ListedDynamicStrDataField('c', 'data'), - ]) - -# From Variables.h -UserStrVarRec2 = _DynamicStructure( - name='UserStrVarRec2', - fields=[ - ListedStaticStringField('c', 'name', help='Name of the string variable.', count=32), - _Field('l', 'strLen', help='The real size of the following array.'), - _Field('c', 'data'), - ]) - -# From Variables.h -VarNumRec = _Structure( - name='VarNumRec', - fields=[ - _Field('h', 'numType', help='Type from binarywave.TYPE_TABLE'), - _Field('d', 'realPart', help='The real part of the number.'), - _Field('d', 'imagPart', help='The imag part if the number is complex.'), - _Field('l', 'reserved', help='Reserved - set to zero.'), - ]) - -# From Variables.h -UserNumVarRec = _DynamicStructure( - name='UserNumVarRec', - fields=[ - ListedStaticStringField('c', 'name', help='Name of the string variable.', count=32), - _Field('h', 'type', help='0 = string, 1 = numeric.'), - DynamicVarNumField(VarNumRec, 'num', help='Type and value of the variable if it is numeric. Not used for string.'), - ]) - -# From Variables.h -UserDependentVarRec = _DynamicStructure( - name='UserDependentVarRec', - fields=[ - ListedStaticStringField('c', 'name', help='Name of the string variable.', count=32), - _Field('h', 'type', help='0 = string, 1 = numeric.'), - _Field(VarNumRec, 'num', help='Type and value of the variable if it is numeric. Not used for string.'), - _Field('h', 'formulaLen', help='The length of the dependency formula.'), - DynamicFormulaField('c', 'formula', help='Start of the dependency formula. A C string including null terminator.'), - ]) - - -class DynamicVarHeaderField (_DynamicField): - def pre_pack(self, parents, data): - raise NotImplementedError() - - def post_unpack(self, parents, data): - var_structure = parents[-1] - var_data = self._get_structure_data( - parents, data, var_structure) - var_header_structure = self.format - data = var_data['var_header'] - sys_vars_field = var_structure.get_field('sysVars') - sys_vars_field.count = data['numSysVars'] - sys_vars_field.setup() - user_vars_field = var_structure.get_field('userVars') - user_vars_field.count = data['numUserVars'] - user_vars_field.setup() - user_strs_field = var_structure.get_field('userStrs') - user_strs_field.count = data['numUserStrs'] - user_strs_field.setup() - if 'numDependentVars' in data: - dependent_vars_field = var_structure.get_field('dependentVars') - dependent_vars_field.count = data['numDependentVars'] - dependent_vars_field.setup() - dependent_strs_field = var_structure.get_field('dependentStrs') - dependent_strs_field.count = data['numDependentStrs'] - dependent_strs_field.setup() - var_structure.setup() - - -Variables1 = _DynamicStructure( - name='Variables1', - fields=[ - DynamicVarHeaderField(VarHeader1, 'var_header', help='Variables header'), - DynamicSysVarField('f', 'sysVars', help='System variables', count=0), - DynamicUserVarField(UserNumVarRec, 'userVars', help='User numeric variables', count=0), - DynamicUserStrField(UserStrVarRec1, 'userStrs', help='User string variables', count=0), - ]) - - -Variables2 = _DynamicStructure( - name='Variables2', - fields=[ - DynamicVarHeaderField(VarHeader2, 'var_header', help='Variables header'), - DynamicSysVarField('f', 'sysVars', help='System variables', count=0), - DynamicUserVarField(UserNumVarRec, 'userVars', help='User numeric variables', count=0), - DynamicUserStrField(UserStrVarRec2, 'userStrs', help='User string variables', count=0), - _Field(UserDependentVarRec, 'dependentVars', help='Dependent numeric variables.', count=0, array=True), - _Field(UserDependentVarRec, 'dependentStrs', help='Dependent string variables.', count=0, array=True), - ]) - - -class DynamicVersionField (_DynamicField): - def pre_pack(self, parents, byte_order): - raise NotImplementedError() - - def post_unpack(self, parents, data): - variables_structure = parents[-1] - variables_data = self._get_structure_data( - parents, data, variables_structure) - version = variables_data['version'] - if variables_structure.byte_order in '@=': - need_to_reorder_bytes = _need_to_reorder_bytes(version) - variables_structure.byte_order = _byte_order(need_to_reorder_bytes) - _LOG.debug( - 'get byte order from version: {} (reorder? {})'.format( - variables_structure.byte_order, need_to_reorder_bytes)) - else: - need_to_reorder_bytes = False - - old_format = variables_structure.fields[-1].format - if version == 1: - variables_structure.fields[-1].format = Variables1 - elif version == 2: - variables_structure.fields[-1].format = Variables2 - elif not need_to_reorder_bytes: - raise ValueError( - 'invalid variables record version: {}'.format(version)) - - if variables_structure.fields[-1].format != old_format: - _LOG.debug('change variables record from {} to {}'.format( - old_format, variables_structure.fields[-1].format)) - variables_structure.setup() - elif need_to_reorder_bytes: - variables_structure.setup() - - # we might need to unpack again with the new byte order - return need_to_reorder_bytes - - -VariablesRecordStructure = _DynamicStructure( - name='VariablesRecord', - fields=[ - DynamicVersionField('h', 'version', help='Version number for this header.'), - _Field(Variables1, 'variables', help='The rest of the variables data.'), - ]) - - -class VariablesRecord (Record): - def __init__(self, *args, **kwargs): - super(VariablesRecord, self).__init__(*args, **kwargs) - # self.header['version'] # record version always 0? - VariablesRecordStructure.byte_order = '=' - VariablesRecordStructure.setup() - stream = _io.BytesIO(bytes(self.data)) - self.variables = VariablesRecordStructure.unpack_stream(stream) - self.namespace = {} - for key,value in self.variables['variables'].items(): - if key not in ['var_header']: - _LOG.debug('update namespace {} with {} for {}'.format( - self.namespace, value, key)) - self.namespace.update(value) diff --git a/igor/script.py b/igor/script.py deleted file mode 100644 index 127bf1b..0000000 --- a/igor/script.py +++ /dev/null @@ -1,99 +0,0 @@ -# Copyright (C) 2012-2016 W. Trevor King -# -# This file is part of igor. -# -# igor is free software: you can redistribute it and/or modify it under the -# terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 3 of the License, or (at your option) any -# later version. -# -# igor is distributed in the hope that it will be useful, but WITHOUT ANY -# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR -# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with igor. If not, see . - -"Common code for scripts distributed with the `igor` package." - -from __future__ import absolute_import -import argparse as _argparse -import logging as _logging -import sys as _sys - -try: - import matplotlib as _matplotlib - import matplotlib.pyplot as _matplotlib_pyplot -except ImportError as _matplotlib_import_error: - _matplotlib = None - -from . import __version__ -from . import LOG as _LOG - - -class Script(object): - log_levels = [_logging.ERROR, _logging.WARNING, _logging.INFO, _logging.DEBUG] - - def __init__(self, description=None, filetype="IGOR Binary Wave (.ibw) file"): - self.parser = _argparse.ArgumentParser(description=description) - self.parser.add_argument( - "--version", action="version", version="%(prog)s {}".format(__version__) - ) - self.parser.add_argument( - "-f", - "--infile", - metavar="FILE", - default="-", - help="input {}".format(filetype), - ) - self.parser.add_argument( - "-o", "--outfile", metavar="FILE", default="-", help="file for ASCII output" - ) - self.parser.add_argument( - "-p", - "--plot", - action="store_const", - const=True, - help="use Matplotlib to plot any IGOR waves", - ) - self.parser.add_argument( - "-V", "--verbose", action="count", default=0, help="increment verbosity" - ) - self._num_plots = 0 - - def run(self, *args, **kwargs): - args = self.parser.parse_args(*args, **kwargs) - if args.infile == "-": - args.infile = _sys.stdin - if args.outfile == "-": - args.outfile = _sys.stdout - if args.verbose > 1: - log_level = self.log_levels[min(args.verbose - 1, len(self.log_levels) - 1)] - _LOG.setLevel(log_level) - self._run(args) - self.display_plots() - - def _run(self, args): - raise NotImplementedError() - - def plot_wave(self, args, wave, title=None): - if not args.plot: - return # no-op - if not _matplotlib: - raise _matplotlib_import_error - if title is None: - title = wave["wave"]["wave_header"]["bname"] - figure = _matplotlib_pyplot.figure() - axes = figure.add_subplot(1, 1, 1) - axes.set_title(title) - try: - axes.plot(wave["wave"]["wData"], "r.") - except ValueError as error: - _LOG.error("error plotting {}: {}".format(title, error)) - pass - self._num_plots += 1 - - def display_plots(self): - if self._num_plots: - _matplotlib_pyplot.show() diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..adbb2db --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,22 @@ +[project] +name = "pygor" +version = "1.0.0" +description = "Package for loading binary IGOR binary" +readme = "README.md" +authors = [ + { name = "W. Trevor King", email = 'wking@tremily.us'}, + { name = "Jacob Gobbo", email = "gobbo.jacob@gmail.com" } +] +requires-python = ">=3.12" +dependencies = [ + "numpy>=2.0.0", +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[dependency-groups] +dev = [ + "pytest>=8.4.1", +] diff --git a/setup.py b/setup.py deleted file mode 100644 index 0f3a04a..0000000 --- a/setup.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (C) 2011-2016 Paul Kienzle -# W. Trevor King -# -# This file is part of igor. -# -# igor is free software: you can redistribute it and/or modify it under the -# terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 3 of the License, or (at your option) any -# later version. -# -# igor is distributed in the hope that it will be useful, but WITHOUT ANY -# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR -# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with igor. If not, see . - -"igor: interface for reading binary IGOR files." - -from distutils.core import setup -import os.path - -from igor import __version__ - - -package_name = 'igor' -_this_dir = os.path.dirname(__file__) - -setup(name=package_name, - version=__version__, - author='W. Trevor King', - author_email='wking@tremily.us', - maintainer='Conrad Stansbury', - maintainer_email='chstan@berkeley.edu', - url='https://github.com/chstan/igorpy', - download_url='https://github.com/chstan/igorpy/tarball/master', - license='GNU Lesser General Public License v3 or later (LGPLv3+)', - platforms=['all'], - description=__doc__, - long_description=open(os.path.join(_this_dir, 'README'), 'r').read(), - classifiers=[ - 'Development Status :: 3 - Alpha', - 'Environment :: Console', - 'Intended Audience :: Developers', - 'Intended Audience :: Science/Research', - 'Operating System :: OS Independent', - 'License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.2', - 'Programming Language :: Python :: 3.3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Topic :: Scientific/Engineering', - 'Topic :: Software Development :: Libraries :: Python Modules', - ], - packages=[ - 'igor', - 'igor.record', - ], - scripts=[ - 'bin/igorbinarywave.py', - 'bin/igorpackedexperiment.py', - ], - provides=['igor ({})'.format(__version__)], - ) diff --git a/igor/__init__.py b/src/pygor/__init__.py similarity index 51% rename from igor/__init__.py rename to src/pygor/__init__.py index 2e24491..a37918d 100644 --- a/igor/__init__.py +++ b/src/pygor/__init__.py @@ -1,31 +1,20 @@ # Copyright (C) 2012-2016 W. Trevor King # -# This file is part of igor. +# This file is part of pygor. # -# igor is free software: you can redistribute it and/or modify it under the +# pygor is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # -# igor is distributed in the hope that it will be useful, but WITHOUT ANY +# pygor is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License -# along with igor. If not, see . +# along with pygor. If not, see . "Interface for reading binary IGOR files." -__version__ = "0.3.1" - - -import logging as _logging - - -LOG = _logging.getLogger("igor") -LOG.setLevel(_logging.ERROR) -LOG.addHandler(_logging.StreamHandler()) -LOG.handlers[-1].setFormatter( - _logging.Formatter("%(name)s - %(levelname)s - %(message)s") -) +from .core import * diff --git a/igor/binarywave.py b/src/pygor/binarywave.py similarity index 76% rename from igor/binarywave.py rename to src/pygor/binarywave.py index 5daf468..432d99c 100644 --- a/igor/binarywave.py +++ b/src/pygor/binarywave.py @@ -1,19 +1,19 @@ # Copyright (C) 2010-2012 W. Trevor King # -# This file is part of igor. +# This file is part of pygor. # -# igor is free software: you can redistribute it and/or modify it under the +# pygor is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # -# igor is distributed in the hope that it will be useful, but WITHOUT ANY +# pygor is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License -# along with igor. If not, see . +# along with pygor. If not, see . "Read IGOR Binary Wave files into Numpy arrays." @@ -25,23 +25,10 @@ # share. We hope IGOR Technical Notes will provide you with lots of # valuable information while you are developing IGOR applications. -from __future__ import absolute_import -import array as _array -import struct as _struct -import sys as _sys -import types as _types +import numpy as np -import numpy as _numpy - -from . import LOG as _LOG -from .struct import Structure as _Structure -from .struct import DynamicStructure as _DynamicStructure -from .struct import Field as _Field -from .struct import DynamicField as _DynamicField -from .util import assert_null as _assert_null -from .util import byte_order as _byte_order -from .util import need_to_reorder_bytes as _need_to_reorder_bytes -from .util import checksum as _checksum +from .struct import Structure, DynamicStructure, Field, DynamicField +from .util import byte_order, need_to_reorder_bytes, checksum # Numpy doesn't support complex integers by default, see @@ -50,15 +37,15 @@ # So we roll our own types. See # http://docs.scipy.org/doc/numpy/user/basics.rec.html # http://docs.scipy.org/doc/numpy/reference/generated/numpy.dtype.html -complexInt8 = _numpy.dtype([("real", _numpy.int8), ("imag", _numpy.int8)]) -complexInt16 = _numpy.dtype([("real", _numpy.int16), ("imag", _numpy.int16)]) -complexInt32 = _numpy.dtype([("real", _numpy.int32), ("imag", _numpy.int32)]) -complexUInt8 = _numpy.dtype([("real", _numpy.uint8), ("imag", _numpy.uint8)]) -complexUInt16 = _numpy.dtype([("real", _numpy.uint16), ("imag", _numpy.uint16)]) -complexUInt32 = _numpy.dtype([("real", _numpy.uint32), ("imag", _numpy.uint32)]) +complexInt8 = np.dtype([("real", np.int8), ("imag", np.int8)]) +complexInt16 = np.dtype([("real", np.int16), ("imag", np.int16)]) +complexInt32 = np.dtype([("real", np.int32), ("imag", np.int32)]) +complexUInt8 = np.dtype([("real", np.uint8), ("imag", np.uint8)]) +complexUInt16 = np.dtype([("real", np.uint16), ("imag", np.uint16)]) +complexUInt32 = np.dtype([("real", np.uint32), ("imag", np.uint32)]) -class StaticStringField(_DynamicField): +class StaticStringField(DynamicField): _null_terminated = False _array_size_field = None @@ -106,27 +93,27 @@ class NullStaticStringField(StaticStringField): # From IgorMath.h TYPE_TABLE = { # (key: integer flag, value: numpy dtype) 0: None, # Text wave, not handled in ReadWave.c - 1: _numpy.complex128, # NT_CMPLX, makes number complex. - 2: _numpy.float32, # NT_FP32, 32 bit fp numbers. - 3: _numpy.complex64, - 4: _numpy.float64, # NT_FP64, 64 bit fp numbers. - 5: _numpy.complex128, - 8: _numpy.int8, # NT_I8, 8 bit signed integer. Requires Igor Pro + 1: np.complex128, # NT_CMPLX, makes number complex. + 2: np.float32, # NT_FP32, 32 bit fp numbers. + 3: np.complex64, + 4: np.float64, # NT_FP64, 64 bit fp numbers. + 5: np.complex128, + 8: np.int8, # NT_I8, 8 bit signed integer. Requires Igor Pro # 2.0 or later. 9: complexInt8, - 0x10: _numpy.int16, # NT_I16, 16 bit integer numbers. Requires Igor + 0x10: np.int16, # NT_I16, 16 bit integer numbers. Requires Igor # Pro 2.0 or later. 0x11: complexInt16, - 0x20: _numpy.int32, # NT_I32, 32 bit integer numbers. Requires Igor + 0x20: np.int32, # NT_I32, 32 bit integer numbers. Requires Igor # Pro 2.0 or later. 0x21: complexInt32, # 0x40:None, # NT_UNSIGNED, Makes above signed integers # # unsigned. Requires Igor Pro 3.0 or later. - 0x48: _numpy.uint8, + 0x48: np.uint8, 0x49: complexUInt8, - 0x50: _numpy.uint16, + 0x50: np.uint16, 0x51: complexUInt16, - 0x60: _numpy.uint32, + 0x60: np.uint32, 0x61: complexUInt32, } @@ -134,86 +121,82 @@ class NullStaticStringField(StaticStringField): MAXDIMS = 4 # From binary.h -BinHeader1 = _Structure( # `version` field pulled out into Wave +BinHeader1 = Structure( # `version` field pulled out into Wave name="BinHeader1", fields=[ - _Field( + Field( "l", "wfmSize", help="The size of the WaveHeader2 data structure plus the wave data plus 16 bytes of padding.", ), - _Field("h", "checksum", help="Checksum over this header and the wave header."), + Field("h", "checksum", help="Checksum over this header and the wave header."), ], ) -BinHeader2 = _Structure( # `version` field pulled out into Wave +BinHeader2 = Structure( # `version` field pulled out into Wave name="BinHeader2", fields=[ - _Field( + Field( "l", "wfmSize", help="The size of the WaveHeader2 data structure plus the wave data plus 16 bytes of padding.", ), - _Field("l", "noteSize", help="The size of the note text."), - _Field( - "l", "pictSize", default=0, help="Reserved. Write zero. Ignore on read." - ), - _Field("h", "checksum", help="Checksum over this header and the wave header."), + Field("l", "noteSize", help="The size of the note text."), + Field("l", "pictSize", default=0, help="Reserved. Write zero. Ignore on read."), + Field("h", "checksum", help="Checksum over this header and the wave header."), ], ) -BinHeader3 = _Structure( # `version` field pulled out into Wave +BinHeader3 = Structure( # `version` field pulled out into Wave name="BinHeader3", fields=[ - _Field( + Field( "l", "wfmSize", help="The size of the WaveHeader2 data structure plus the wave data plus 16 bytes of padding.", ), - _Field("l", "noteSize", help="The size of the note text."), - _Field("l", "formulaSize", help="The size of the dependency formula, if any."), - _Field( - "l", "pictSize", default=0, help="Reserved. Write zero. Ignore on read." - ), - _Field("h", "checksum", help="Checksum over this header and the wave header."), + Field("l", "noteSize", help="The size of the note text."), + Field("l", "formulaSize", help="The size of the dependency formula, if any."), + Field("l", "pictSize", default=0, help="Reserved. Write zero. Ignore on read."), + Field("h", "checksum", help="Checksum over this header and the wave header."), ], ) -BinHeader5 = _Structure( # `version` field pulled out into Wave +BinHeader5 = Structure( # `version` field pulled out into Wave name="BinHeader5", fields=[ - _Field("h", "checksum", help="Checksum over this header and the wave header."), - _Field( + Field("h", "checksum", help="Checksum over this header and the wave header."), + Field( "l", "wfmSize", help="The size of the WaveHeader5 data structure plus the wave data.", ), - _Field("l", "formulaSize", help="The size of the dependency formula, if any."), - _Field("l", "noteSize", help="The size of the note text."), - _Field("l", "dataEUnitsSize", help="The size of optional extended data units."), - _Field( + Field("l", "formulaSize", help="The size of the dependency formula, if any."), + Field("l", "noteSize", help="The size of the note text."), + Field("l", "dataEUnitsSize", help="The size of optional extended data units."), + Field( "l", "dimEUnitsSize", help="The size of optional extended dimension units.", count=MAXDIMS, array=True, ), - _Field( + Field( "l", "dimLabelsSize", help="The size of optional dimension labels.", count=MAXDIMS, array=True, ), - _Field( + Field( "l", "sIndicesSize", help="The size of string indicies if this is a text wave.", ), - _Field( + Field( "l", "optionsSize1", default=0, help="Reserved. Write zero. Ignore on read." ), - _Field( + Field( "l", "optionsSize2", default=0, help="Reserved. Write zero. Ignore on read." ), ], @@ -230,13 +213,11 @@ class NullStaticStringField(StaticStringField): # Header to an array of waveform data. # `wData` field pulled out into DynamicWaveDataField1 -WaveHeader2 = _DynamicStructure( +WaveHeader2 = DynamicStructure( name="WaveHeader2", fields=[ - _Field( - "h", "type", help="See types (e.g. NT_FP64) above. Zero for text waves." - ), - _Field( + Field("h", "type", help="See types (e.g. NT_FP64) above. Zero for text waves."), + Field( "P", "next", default=0, @@ -248,20 +229,20 @@ class NullStaticStringField(StaticStringField): help="Name of wave plus trailing null.", count=MAX_WAVE_NAME2 + 2, ), - _Field("h", "whVersion", default=0, help="Write 0. Ignore on read."), - _Field( + Field("h", "whVersion", default=0, help="Write 0. Ignore on read."), + Field( "h", "srcFldr", default=0, help="Used in memory only. Write zero. Ignore on read.", ), - _Field( + Field( "P", "fileName", default=0, help="Used in memory only. Write zero. Ignore on read.", ), - _Field( + Field( "c", "dataUnits", default=0, @@ -269,7 +250,7 @@ class NullStaticStringField(StaticStringField): count=MAX_UNIT_CHARS + 1, array=True, ), - _Field( + Field( "c", "xUnits", default=0, @@ -277,59 +258,57 @@ class NullStaticStringField(StaticStringField): count=MAX_UNIT_CHARS + 1, array=True, ), - _Field("l", "npnts", help="Number of data points in wave."), - _Field( + Field("l", "npnts", help="Number of data points in wave."), + Field( "h", "aModified", default=0, help="Used in memory only. Write zero. Ignore on read.", ), - _Field("d", "hsA", help="X value for point p = hsA*p + hsB"), - _Field("d", "hsB", help="X value for point p = hsA*p + hsB"), - _Field( + Field("d", "hsA", help="X value for point p = hsA*p + hsB"), + Field("d", "hsB", help="X value for point p = hsA*p + hsB"), + Field( "h", "wModified", default=0, help="Used in memory only. Write zero. Ignore on read.", ), - _Field( + Field( "h", "swModified", default=0, help="Used in memory only. Write zero. Ignore on read.", ), - _Field("h", "fsValid", help="True if full scale values have meaning."), - _Field( + Field("h", "fsValid", help="True if full scale values have meaning."), + Field( "d", "topFullScale", help="The min full scale value for wave." ), # sic, 'min' should probably be 'max' - _Field("d", "botFullScale", help="The min full scale value for wave."), - _Field( + Field("d", "botFullScale", help="The min full scale value for wave."), + Field( "c", "useBits", default=0, help="Used in memory only. Write zero. Ignore on read.", ), - _Field( - "c", "kindBits", default=0, help="Reserved. Write zero. Ignore on read." - ), - _Field( + Field("c", "kindBits", default=0, help="Reserved. Write zero. Ignore on read."), + Field( "P", "formula", default=0, help="Used in memory only. Write zero. Ignore on read.", ), - _Field( + Field( "l", "depID", default=0, help="Used in memory only. Write zero. Ignore on read.", ), - _Field( + Field( "L", "creationDate", help="DateTime of creation. Not used in version 1 files.", ), - _Field( + Field( "c", "wUnused", default=0, @@ -337,8 +316,8 @@ class NullStaticStringField(StaticStringField): count=2, array=True, ), - _Field("L", "modDate", help="DateTime of last modification."), - _Field( + Field("L", "modDate", help="DateTime of last modification."), + Field( "P", "waveNoteH", help="Used in memory only. Write zero. Ignore on read." ), ], @@ -347,22 +326,20 @@ class NullStaticStringField(StaticStringField): # `sIndices` pointer unset (use Wave5_data['sIndices'] instead). This # field is filled in by DynamicStringIndicesDataField. # `wData` field pulled out into DynamicWaveDataField5 -WaveHeader5 = _DynamicStructure( +WaveHeader5 = DynamicStructure( name="WaveHeader5", fields=[ - _Field("P", "next", help="link to next wave in linked list."), - _Field("L", "creationDate", help="DateTime of creation."), - _Field("L", "modDate", help="DateTime of last modification."), - _Field( + Field("P", "next", help="link to next wave in linked list."), + Field("L", "creationDate", help="DateTime of creation."), + Field("L", "modDate", help="DateTime of last modification."), + Field( "l", "npnts", help="Total number of points (multiply dimensions up to first zero).", ), - _Field( - "h", "type", help="See types (e.g. NT_FP64) above. Zero for text waves." - ), - _Field("h", "dLock", default=0, help="Reserved. Write zero. Ignore on read."), - _Field( + Field("h", "type", help="See types (e.g. NT_FP64) above. Zero for text waves."), + Field("h", "dLock", default=0, help="Reserved. Write zero. Ignore on read."), + Field( "c", "whpad1", default=0, @@ -370,36 +347,36 @@ class NullStaticStringField(StaticStringField): count=6, array=True, ), - _Field("h", "whVersion", default=1, help="Write 1. Ignore on read."), + Field("h", "whVersion", default=1, help="Write 1. Ignore on read."), NullStaticStringField( "c", "bname", help="Name of wave plus trailing null.", count=MAX_WAVE_NAME5 + 1, ), - _Field("l", "whpad2", default=0, help="Reserved. Write zero. Ignore on read."), - _Field( + Field("l", "whpad2", default=0, help="Reserved. Write zero. Ignore on read."), + Field( "P", "dFolder", default=0, help="Used in memory only. Write zero. Ignore on read.", ), # Dimensioning info. [0] == rows, [1] == cols etc - _Field( + Field( "l", "nDim", help="Number of of items in a dimension -- 0 means no data.", count=MAXDIMS, array=True, ), - _Field( + Field( "d", "sfA", help="Index value for element e of dimension d = sfA[d]*e + sfB[d].", count=MAXDIMS, array=True, ), - _Field( + Field( "d", "sfB", help="Index value for element e of dimension d = sfA[d]*e + sfB[d].", @@ -407,7 +384,7 @@ class NullStaticStringField(StaticStringField): array=True, ), # SI units - _Field( + Field( "c", "dataUnits", default=0, @@ -415,7 +392,7 @@ class NullStaticStringField(StaticStringField): count=MAX_UNIT_CHARS + 1, array=True, ), - _Field( + Field( "c", "dimUnits", default=0, @@ -423,21 +400,21 @@ class NullStaticStringField(StaticStringField): count=(MAXDIMS, MAX_UNIT_CHARS + 1), array=True, ), - _Field("h", "fsValid", help="TRUE if full scale values have meaning."), - _Field("h", "whpad3", default=0, help="Reserved. Write zero. Ignore on read."), - _Field( + Field("h", "fsValid", help="TRUE if full scale values have meaning."), + Field("h", "whpad3", default=0, help="Reserved. Write zero. Ignore on read."), + Field( "d", "topFullScale", help="The max and max full scale value for wave" ), # sic, probably "max and min" - _Field( + Field( "d", "botFullScale", help="The max and max full scale value for wave." ), # sic, probably "max and min" - _Field( + Field( "P", "dataEUnits", default=0, help="Used in memory only. Write zero. Ignore on read.", ), - _Field( + Field( "P", "dimEUnits", default=0, @@ -445,7 +422,7 @@ class NullStaticStringField(StaticStringField): count=MAXDIMS, array=True, ), - _Field( + Field( "P", "dimLabels", default=0, @@ -453,13 +430,13 @@ class NullStaticStringField(StaticStringField): count=MAXDIMS, array=True, ), - _Field( + Field( "P", "waveNoteH", default=0, help="Used in memory only. Write zero. Ignore on read.", ), - _Field( + Field( "l", "whUnused", default=0, @@ -468,59 +445,57 @@ class NullStaticStringField(StaticStringField): array=True, ), # The following stuff is considered private to Igor. - _Field( + Field( "h", "aModified", default=0, help="Used in memory only. Write zero. Ignore on read.", ), - _Field( + Field( "h", "wModified", default=0, help="Used in memory only. Write zero. Ignore on read.", ), - _Field( + Field( "h", "swModified", default=0, help="Used in memory only. Write zero. Ignore on read.", ), - _Field( + Field( "c", "useBits", default=0, help="Used in memory only. Write zero. Ignore on read.", ), - _Field( - "c", "kindBits", default=0, help="Reserved. Write zero. Ignore on read." - ), - _Field( + Field("c", "kindBits", default=0, help="Reserved. Write zero. Ignore on read."), + Field( "P", "formula", default=0, help="Used in memory only. Write zero. Ignore on read.", ), - _Field( + Field( "l", "depID", default=0, help="Used in memory only. Write zero. Ignore on read.", ), - _Field("h", "whpad4", default=0, help="Reserved. Write zero. Ignore on read."), - _Field( + Field("h", "whpad4", default=0, help="Reserved. Write zero. Ignore on read."), + Field( "h", "srcFldr", default=0, help="Used in memory only. Write zero. Ignore on read.", ), - _Field( + Field( "P", "fileName", default=0, help="Used in memory only. Write zero. Ignore on read.", ), - _Field( + Field( "P", "sIndices", default=0, @@ -530,7 +505,7 @@ class NullStaticStringField(StaticStringField): ) -class DynamicWaveDataField1(_DynamicField): +class DynamicWaveDataField1(DynamicField): def pre_pack(self, parents, data): raise NotImplementedError() @@ -550,13 +525,13 @@ def pre_unpack(self, parents, data): if type_: self.shape = self._get_shape(bin_header, wave_header) else: # text wave - type_ = _numpy.dtype("S1") + type_ = np.dtype("S1") self.shape = (self.data_size,) # dtype() wrapping to avoid numpy.generic and # getset_descriptor issues with the builtin numpy types # (e.g. int32). It has no effect on our local complex # integers. - self.dtype = _numpy.dtype(type_).newbyteorder(wave_structure.byte_order) + self.dtype = np.dtype(type_).newbyteorder(wave_structure.byte_order) if ( version == 3 and self.count > 0 @@ -604,18 +579,12 @@ def _get_shape(self, bin_header, wave_header): def unpack(self, stream): data_b = stream.read(self.data_size) - try: - data = _numpy.ndarray( - shape=self.shape, - dtype=self.dtype, - buffer=data_b, - order="F", - ) - except: - _LOG.error( - "could not reshape data from {} to {}".format(self.shape, data_b) - ) - raise + data = np.ndarray( + shape=self.shape, + dtype=self.dtype, + buffer=data_b, + order="F", + ) return data @@ -758,7 +727,7 @@ def post_unpack(self, parents, data): wave_data[self.name] = dim_labels -class DynamicStringIndicesDataField(_DynamicField): +class DynamicStringIndicesDataField(DynamicField): """String indices used for text waves only""" def pre_pack(self, parents, data): @@ -793,19 +762,13 @@ def post_unpack(self, parents, data): strings.append(b"") else: raise ValueError((offset, wave_data["sIndices"])) - wdata = _numpy.array(strings) + wdata = np.array(strings) shape = [n for n in wave_header["nDim"] if n > 0] or (0,) - try: - wdata = wdata.reshape(shape) - except ValueError: - _LOG.error( - "could not reshape strings from {} to {}".format(shape, wdata.shape) - ) - raise + wdata = wdata.reshape(shape) wave_data["wData"] = wdata -class DynamicVersionField(_DynamicField): +class DynamicVersionField(DynamicField): def pre_pack(self, parents, byte_order): raise NotImplementedError() @@ -814,15 +777,10 @@ def post_unpack(self, parents, data): wave_data = self._get_structure_data(parents, data, wave_structure) version = wave_data["version"] if wave_structure.byte_order in "@=": - need_to_reorder_bytes = _need_to_reorder_bytes(version) - wave_structure.byte_order = _byte_order(need_to_reorder_bytes) - _LOG.debug( - "get byte order from version: {} (reorder? {})".format( - wave_structure.byte_order, need_to_reorder_bytes - ) - ) + should_reorder_bytes = need_to_reorder_bytes(version) + wave_structure.byte_order = byte_order(should_reorder_bytes) else: - need_to_reorder_bytes = False + should_reorder_bytes = False old_format = wave_structure.fields[-1].format if version == 1: @@ -833,24 +791,19 @@ def post_unpack(self, parents, data): wave_structure.fields[-1].format = Wave3 elif version == 5: wave_structure.fields[-1].format = Wave5 - elif not need_to_reorder_bytes: + elif not should_reorder_bytes: raise ValueError("invalid binary wave version: {}".format(version)) if wave_structure.fields[-1].format != old_format: - _LOG.debug( - "change wave headers from {} to {}".format( - old_format, wave_structure.fields[-1].format - ) - ) wave_structure.setup() - elif need_to_reorder_bytes: + elif should_reorder_bytes: wave_structure.setup() # we might need to unpack again with the new byte order - return need_to_reorder_bytes + return should_reorder_bytes -class DynamicWaveField(_DynamicField): +class DynamicWaveField(DynamicField): def post_unpack(self, parents, data): return raise NotImplementedError() # TODO @@ -859,7 +812,7 @@ def post_unpack(self, parents, data): if version == 5: # Version 5 checksum does not include the wData field. checksum_size -= 4 - c = _checksum(b, parents[-1].byte_order, 0, checksum_size) + c = checksum(b, parents[-1].byte_order, 0, checksum_size) if c != 0: raise ValueError( ( @@ -869,11 +822,11 @@ def post_unpack(self, parents, data): ) -Wave1 = _DynamicStructure( +Wave1 = DynamicStructure( name="Wave1", fields=[ - _Field(BinHeader1, "bin_header", help="Binary wave header"), - _Field(WaveHeader2, "wave_header", help="Wave header"), + Field(BinHeader1, "bin_header", help="Binary wave header"), + Field(WaveHeader2, "wave_header", help="Wave header"), DynamicWaveDataField1( "f", "wData", @@ -884,11 +837,11 @@ def post_unpack(self, parents, data): ], ) -Wave2 = _DynamicStructure( +Wave2 = DynamicStructure( name="Wave2", fields=[ - _Field(BinHeader2, "bin_header", help="Binary wave header"), - _Field(WaveHeader2, "wave_header", help="Wave header"), + Field(BinHeader2, "bin_header", help="Binary wave header"), + Field(WaveHeader2, "wave_header", help="Wave header"), DynamicWaveDataField1( "f", "wData", @@ -896,7 +849,7 @@ def post_unpack(self, parents, data): count=0, array=True, ), - _Field( + Field( "x", "padding", help="16 bytes of padding in versions 2 and 3.", @@ -909,11 +862,11 @@ def post_unpack(self, parents, data): ], ) -Wave3 = _DynamicStructure( +Wave3 = DynamicStructure( name="Wave3", fields=[ - _Field(BinHeader3, "bin_header", help="Binary wave header"), - _Field(WaveHeader2, "wave_header", help="Wave header"), + Field(BinHeader3, "bin_header", help="Binary wave header"), + Field(WaveHeader2, "wave_header", help="Wave header"), DynamicWaveDataField1( "f", "wData", @@ -921,7 +874,7 @@ def post_unpack(self, parents, data): count=0, array=True, ), - _Field( + Field( "x", "padding", help="16 bytes of padding in versions 2 and 3.", @@ -937,11 +890,11 @@ def post_unpack(self, parents, data): ], ) -Wave5 = _DynamicStructure( +Wave5 = DynamicStructure( name="Wave5", fields=[ - _Field(BinHeader5, "bin_header", help="Binary wave header"), - _Field(WaveHeader5, "wave_header", help="Wave header"), + Field(BinHeader5, "bin_header", help="Binary wave header"), + Field(WaveHeader5, "wave_header", help="Wave header"), DynamicWaveDataField5( "f", "wData", @@ -986,7 +939,7 @@ def post_unpack(self, parents, data): ], ) -Wave = _DynamicStructure( +Wave = DynamicStructure( name="Wave", fields=[ DynamicVersionField( @@ -997,7 +950,7 @@ def post_unpack(self, parents, data): ) -def load(filename): +def load_ibw(filename): if hasattr(filename, "read"): f = filename # filename is actually a stream object else: diff --git a/igor/igorpy.py b/src/pygor/core.py similarity index 81% rename from igor/igorpy.py rename to src/pygor/core.py index efdd247..20c753f 100644 --- a/igor/igorpy.py +++ b/src/pygor/core.py @@ -1,7 +1,7 @@ # This program is in the public domain -"""`igor.py` compatibility layer on top of the `igor` package. +"""`pygor.py` compatibility layer on top of the `pygor` package. -igor.load('filename') or igor.loads('data') loads the content of an igore file +pygor.load('filename') or pygor.loads('data') loads the content of an igore file into memory as a folder structure. Returns the root folder. @@ -10,35 +10,41 @@ Children can be indexed by folder[i] or by folder['name']. To see the whole tree, use: print folder.format() -The usual igor folder types are given in the technical reports +The usual pygor folder types are given in the technical reports PTN003.ifn and TN003.ifn. """ -from __future__ import absolute_import -import io as _io -import locale as _locale -import re as _re -import sys as _sys +import io, locale, re, sys -import numpy as _numpy +import numpy as np from .binarywave import MAXDIMS as _MAXDIMS -from .packed import load as _load -from .record.base import UnknownRecord as _UnknownRecord -from .record.folder import FolderStartRecord as _FolderStartRecord -from .record.folder import FolderEndRecord as _FolderEndRecord -from .record.history import HistoryRecord as _HistoryRecord -from .record.history import GetHistoryRecord as _GetHistoryRecord -from .record.history import RecreationRecord as _RecreationRecord -from .record.packedfile import PackedFileRecord as _PackedFileRecord -from .record.procedure import ProcedureRecord as _ProcedureRecord -from .record.wave import WaveRecord as _WaveRecord -from .record.variables import VariablesRecord as _VariablesRecord - - -__version__ = "0.10" - +from .packed import load_pack +from .record import ( + UnknownRecord, + FolderStartRecord, + FolderEndRecord, + HistoryRecord, + GetHistoryRecord, + RecreationRecord, + PackedFileRecord, + ProcedureRecord, + WaveRecord, + VariablesRecord, +) -ENCODING = _locale.getpreferredencoding() or _sys.getdefaultencoding() +__all__ = [ + "load", + "Variables", + "History", + "Wave", + "Recreation", + "Procedure", + "GetHistory", + "PackedFile", + "Folder", +] + +ENCODING = locale.getpreferredencoding() or sys.getdefaultencoding() PYKEYWORDS = set( ( "and", @@ -70,7 +76,7 @@ "yield", ) ) -PYID = _re.compile(r"^[^\d\W]\w*$", _re.UNICODE) +PYID = re.compile(r"^[^\d\W]\w*$", re.UNICODE) def valid_identifier(s): @@ -154,7 +160,7 @@ def __init__(self, record): self.axis_units.extend([""] * (_MAXDIMS - len(self.axis_units))) self.axis_units = tuple(self.axis_units) self.axis = [ - _numpy.linspace(b, b + a * (c - 1), c) for a, b, c in zip(sfA, sfB, dims) + np.linspace(b, b + a * (c - 1), c) for a, b, c in zip(sfA, sfB, dims) ] self.formula = d.get("formula", "") self.notes = d.get("note", "") @@ -169,7 +175,7 @@ def format(self, indent=0): def __array__(self): return self.data - __repr__ = __str__ = lambda s: "" % s.format() + __repr__ = __str__ = lambda s: "" % s.format() class Recreation(IgorObject): @@ -258,7 +264,7 @@ def __getitem__(self, key): raise KeyError("Folder %s does not exist" % key) def __str__(self): - return "" % "/".join(self.path) + return "" % "/".join(self.path) __repr__ = __str__ @@ -284,15 +290,15 @@ def format(self, indent=0): def loads(s, **kwargs): - """Load an igor file from string""" - stream = _io.BytesIO(s) + """Load an pygor file from string""" + stream = io.BytesIO(s) return load(stream, **kwargs) def load(filename, **kwargs): - """Load an igor file""" + """Load an pygor file""" try: - packed_experiment = _load( + packed_experiment = load_pack( filename, initial_byte_order=kwargs.pop("initial_byte_order", "=") ) except ValueError as e: @@ -308,34 +314,34 @@ def _convert(packed_experiment, ignore_unknown=True): records, filesystem = packed_experiment stack = [Folder(path=["root"])] for record in records: - if isinstance(record, _UnknownRecord): + if isinstance(record, UnknownRecord): if ignore_unknown: continue else: r = Unknown(record.data, type=record.header["recordType"]) - elif isinstance(record, _GetHistoryRecord): + elif isinstance(record, GetHistoryRecord): r = GetHistory(record.text) - elif isinstance(record, _HistoryRecord): + elif isinstance(record, HistoryRecord): r = History(record.text) - elif isinstance(record, _PackedFileRecord): + elif isinstance(record, PackedFileRecord): r = PackedFile(record.text) - elif isinstance(record, _ProcedureRecord): + elif isinstance(record, ProcedureRecord): r = Procedure(record.text) - elif isinstance(record, _RecreationRecord): + elif isinstance(record, RecreationRecord): r = Recreation(record.text) - elif isinstance(record, _VariablesRecord): + elif isinstance(record, VariablesRecord): r = Variables(record) - elif isinstance(record, _WaveRecord): + elif isinstance(record, WaveRecord): r = Wave(record) else: r = None - if isinstance(record, _FolderStartRecord): + if isinstance(record, FolderStartRecord): path = stack[-1].path + [record.null_terminated_text.decode(ENCODING)] folder = Folder(path) stack[-1].append(folder) stack.append(folder) - elif isinstance(record, _FolderEndRecord): + elif isinstance(record, FolderEndRecord): stack.pop() elif r is None: raise NotImplementedError(record) diff --git a/igor/packed.py b/src/pygor/packed.py similarity index 72% rename from igor/packed.py rename to src/pygor/packed.py index 0c60270..785a868 100644 --- a/igor/packed.py +++ b/src/pygor/packed.py @@ -1,36 +1,34 @@ # Copyright (C) 2012 W. Trevor King # -# This file is part of igor. +# This file is part of pygor. # -# igor is free software: you can redistribute it and/or modify it under the +# pygor is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # -# igor is distributed in the hope that it will be useful, but WITHOUT ANY +# pygor is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License -# along with igor. If not, see . +# along with pygor. If not, see . "Read IGOR Packed Experiment files files into records." -from . import LOG as _LOG -from .struct import Structure as _Structure -from .struct import Field as _Field -from .util import byte_order as _byte_order -from .util import need_to_reorder_bytes as _need_to_reorder_bytes -from .util import _bytes -from .record import RECORD_TYPE as _RECORD_TYPE -from .record.base import UnknownRecord as _UnknownRecord -from .record.base import UnusedRecord as _UnusedRecord -from .record.folder import FolderStartRecord as _FolderStartRecord -from .record.folder import FolderEndRecord as _FolderEndRecord -from .record.variables import VariablesRecord as _VariablesRecord -from .record.wave import WaveRecord as _WaveRecord - +from .struct import Structure, Field +from .util import byte_order as get_byte_order +from .util import need_to_reorder_bytes +from .record import ( + RECORD_TYPE, + UnknownRecord, + UnusedRecord, + FolderStartRecord, + FolderEndRecord, + VariablesRecord, + WaveRecord, +) # From PTN003: # Igor writes other kinds of records in a packed experiment file, for @@ -40,14 +38,14 @@ # files, you must skip any record with a record type that is not # listed above. -PackedFileRecordHeader = _Structure( +PackedFileRecordHeader = Structure( name="PackedFileRecordHeader", fields=[ - _Field("H", "recordType", help="Record type plus superceded flag."), - _Field( + Field("H", "recordType", help="Record type plus superceded flag."), + Field( "h", "version", help="Version information depends on the type of record." ), - _Field( + Field( "l", "numDataBytes", help="Number of data bytes in the record following this record header.", @@ -62,12 +60,11 @@ # a later record in the packed file. -def load(filename, strict=True, ignore_unknown=True, initial_byte_order="="): +def load_pack(filename, strict=True, ignore_unknown=True, initial_byte_order="="): """ Probably better to actually infer the initial_byte_order, as can be done from the header. For now though we will let the user deal with this. """ - _LOG.debug("loading a packed experiment file from {}".format(filename)) records = [] if hasattr(filename, "read"): f = filename # filename is actually a stream object @@ -87,21 +84,14 @@ def load(filename, strict=True, ignore_unknown=True, initial_byte_order="="): len(b), PackedFileRecordHeader.size ) ) - _LOG.debug("reading a new packed experiment file record") header = PackedFileRecordHeader.unpack_from(b) if header["version"] and not byte_order: - need_to_reorder = _need_to_reorder_bytes(header["version"]) - byte_order = initial_byte_order = _byte_order(need_to_reorder) - _LOG.debug( - "get byte order from version: {} (reorder? {})".format( - byte_order, need_to_reorder - ) - ) - if need_to_reorder: + should_reorder = need_to_reorder_bytes(header["version"]) + byte_order = initial_byte_order = get_byte_order(should_reorder) + if should_reorder: PackedFileRecordHeader.byte_order = byte_order PackedFileRecordHeader.setup() header = PackedFileRecordHeader.unpack_from(b) - _LOG.debug("reordered version: {}".format(header["version"])) data = bytes(f.read(header["numDataBytes"])) if len(data) < header["numDataBytes"]: raise ValueError( @@ -109,19 +99,13 @@ def load(filename, strict=True, ignore_unknown=True, initial_byte_order="="): len(b), header["numDataBytes"] ) ) - record_type = _RECORD_TYPE.get( - header["recordType"] & PACKEDRECTYPE_MASK, _UnknownRecord - ) - _LOG.debug( - "the new record has type {} ({}).".format( - record_type, header["recordType"] - ) + record_type = RECORD_TYPE.get( + header["recordType"] & PACKEDRECTYPE_MASK, UnknownRecord ) - if record_type in [_UnknownRecord, _UnusedRecord] and not ignore_unknown: + if record_type in [UnknownRecord, UnusedRecord] and not ignore_unknown: raise KeyError("unkown record type {}".format(header["recordType"])) records.append(record_type(header, data, byte_order=byte_order)) finally: - _LOG.debug("finished loading {} records from {}".format(len(records), filename)) if not hasattr(filename, "read"): f.close() @@ -169,14 +153,14 @@ def _build_filesystem(records): dir_stack = [("root", filesystem["root"])] for record in records: cwd = dir_stack[-1][-1] - if isinstance(record, _FolderStartRecord): + if isinstance(record, FolderStartRecord): name = record.null_terminated_text cwd[name] = {} dir_stack.append((name, cwd[name])) - elif isinstance(record, _FolderEndRecord): + elif isinstance(record, FolderEndRecord): dir_stack.pop() - elif isinstance(record, (_VariablesRecord, _WaveRecord)): - if isinstance(record, _VariablesRecord): + elif isinstance(record, (VariablesRecord, WaveRecord)): + if isinstance(record, VariablesRecord): sys_vars = record.variables["variables"]["sysVars"].keys() for filename, value in record.namespace.items(): if len(dir_stack) > 1 and filename in sys_vars: @@ -209,7 +193,7 @@ def walk(filesystem, callback, dirpath=None): """Walk a packed experiment filesystem, operating on each key,value pair.""" if dirpath is None: dirpath = [] - for key, value in sorted((_bytes(k), v) for k, v in filesystem.items()): + for key, value in sorted((bytes(k), v) for k, v in filesystem.items()): callback(dirpath, key, value) if isinstance(value, dict): walk(filesystem=value, callback=callback, dirpath=dirpath + [key]) diff --git a/igor/record/__init__.py b/src/pygor/record/__init__.py similarity index 82% rename from igor/record/__init__.py rename to src/pygor/record/__init__.py index eafebfb..9bf2d83 100644 --- a/igor/record/__init__.py +++ b/src/pygor/record/__init__.py @@ -1,19 +1,19 @@ # Copyright (C) 2012 W. Trevor King # -# This file is part of igor. +# This file is part of pygor. # -# igor is free software: you can redistribute it and/or modify it under the +# pygor is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # -# igor is distributed in the hope that it will be useful, but WITHOUT ANY +# pygor is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License -# along with igor. If not, see . +# along with pygor. If not, see . "Record parsers for IGOR's packed experiment files." @@ -40,4 +40,4 @@ 8: PackedFileRecord, 9: FolderStartRecord, 10: FolderEndRecord, - } +} diff --git a/igor/record/base.py b/src/pygor/record/base.py similarity index 55% rename from igor/record/base.py rename to src/pygor/record/base.py index 6b168cf..003996c 100644 --- a/igor/record/base.py +++ b/src/pygor/record/base.py @@ -1,22 +1,22 @@ # Copyright (C) 2012 W. Trevor King # -# This file is part of igor. +# This file is part of pygor. # -# igor is free software: you can redistribute it and/or modify it under the +# pygor is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # -# igor is distributed in the hope that it will be useful, but WITHOUT ANY +# pygor is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License -# along with igor. If not, see . +# along with pygor. If not, see . -class Record (object): +class Record(object): def __init__(self, header, data, byte_order=None): self.header = header self.data = data @@ -26,22 +26,22 @@ def __str__(self): return self.__repr__() def __repr__(self): - return '<{} {}>'.format(self.__class__.__name__, id(self)) + return "<{} {}>".format(self.__class__.__name__, id(self)) -class UnknownRecord (Record): +class UnknownRecord(Record): def __repr__(self): - return '<{}-{} {}>'.format( - self.__class__.__name__, self.header['recordType'], id(self)) + return "<{}-{} {}>".format( + self.__class__.__name__, self.header["recordType"], id(self) + ) -class UnusedRecord (Record): +class UnusedRecord(Record): pass -class TextRecord (Record): +class TextRecord(Record): def __init__(self, *args, **kwargs): super(TextRecord, self).__init__(*args, **kwargs) - self.text = bytes(self.data).replace( - b'\r\n', b'\n').replace(b'\r', b'\n') - self.null_terminated_text = self.text.split(b'\x00', 1)[0] + self.text = bytes(self.data).replace(b"\r\n", b"\n").replace(b"\r", b"\n") + self.null_terminated_text = self.text.split(b"\x00", 1)[0] diff --git a/igor/record/folder.py b/src/pygor/record/folder.py similarity index 62% rename from igor/record/folder.py rename to src/pygor/record/folder.py index caaeb54..c8a71bb 100644 --- a/igor/record/folder.py +++ b/src/pygor/record/folder.py @@ -1,26 +1,26 @@ # Copyright (C) 2012 W. Trevor King # -# This file is part of igor. +# This file is part of pygor. # -# igor is free software: you can redistribute it and/or modify it under the +# pygor is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # -# igor is distributed in the hope that it will be useful, but WITHOUT ANY +# pygor is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License -# along with igor. If not, see . +# along with pygor. If not, see . from .base import TextRecord -class FolderStartRecord (TextRecord): +class FolderStartRecord(TextRecord): pass -class FolderEndRecord (TextRecord): +class FolderEndRecord(TextRecord): pass diff --git a/igor/record/history.py b/src/pygor/record/history.py similarity index 60% rename from igor/record/history.py rename to src/pygor/record/history.py index e5d2199..05bc309 100644 --- a/igor/record/history.py +++ b/src/pygor/record/history.py @@ -1,30 +1,30 @@ # Copyright (C) 2012 W. Trevor King # -# This file is part of igor. +# This file is part of pygor. # -# igor is free software: you can redistribute it and/or modify it under the +# pygor is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # -# igor is distributed in the hope that it will be useful, but WITHOUT ANY +# pygor is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License -# along with igor. If not, see . +# along with pygor. If not, see . from .base import TextRecord -class HistoryRecord (TextRecord): +class HistoryRecord(TextRecord): pass -class RecreationRecord (TextRecord): +class RecreationRecord(TextRecord): pass -class GetHistoryRecord (TextRecord): +class GetHistoryRecord(TextRecord): pass diff --git a/igor/record/packedfile.py b/src/pygor/record/packedfile.py similarity index 64% rename from igor/record/packedfile.py rename to src/pygor/record/packedfile.py index b457f20..c4ddaf5 100644 --- a/igor/record/packedfile.py +++ b/src/pygor/record/packedfile.py @@ -1,22 +1,22 @@ # Copyright (C) 2012 W. Trevor King # -# This file is part of igor. +# This file is part of pygor. # -# igor is free software: you can redistribute it and/or modify it under the +# pygor is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # -# igor is distributed in the hope that it will be useful, but WITHOUT ANY +# pygor is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License -# along with igor. If not, see . +# along with pygor. If not, see . from .base import Record -class PackedFileRecord (Record): +class PackedFileRecord(Record): pass diff --git a/igor/record/procedure.py b/src/pygor/record/procedure.py similarity index 64% rename from igor/record/procedure.py rename to src/pygor/record/procedure.py index de00e6e..b3a5629 100644 --- a/igor/record/procedure.py +++ b/src/pygor/record/procedure.py @@ -1,22 +1,22 @@ # Copyright (C) 2012 W. Trevor King # -# This file is part of igor. +# This file is part of pygor. # -# igor is free software: you can redistribute it and/or modify it under the +# pygor is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # -# igor is distributed in the hope that it will be useful, but WITHOUT ANY +# pygor is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License -# along with igor. If not, see . +# along with pygor. If not, see . from .base import TextRecord -class ProcedureRecord (TextRecord): +class ProcedureRecord(TextRecord): pass diff --git a/src/pygor/record/variables.py b/src/pygor/record/variables.py new file mode 100644 index 0000000..31420f9 --- /dev/null +++ b/src/pygor/record/variables.py @@ -0,0 +1,371 @@ +# Copyright (C) 2012 W. Trevor King +# +# This file is part of pygor. +# +# pygor is free software: you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) any +# later version. +# +# pygor is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with pygor. If not, see . + +import io + +from ..binarywave import TYPE_TABLE as TYPE_TABLE +from ..binarywave import NullStaticStringField, DynamicStringField +from ..struct import Structure, DynamicStructure, Field, DynamicField +from ..util import byte_order, need_to_reorder_bytes +from .base import Record + + +class ListedStaticStringField(NullStaticStringField): + """Handle string conversions for multi-count dynamic parents. + + If a field belongs to a multi-count dynamic parent, the parent is + called multiple times to parse each count, and the field's + post-unpack hook gets called after the field is unpacked during + each iteration. This requires alternative logic for getting and + setting the string data. The actual string formatting code is not + affected. + """ + + def post_unpack(self, parents, data): + parent_structure = parents[-1] + parent_data = self._get_structure_data(parents, data, parent_structure) + d = self._normalize_string(parent_data[-1][self.name]) + parent_data[-1][self.name] = d + + +class ListedStaticStringField(NullStaticStringField): + """Handle string conversions for multi-count dynamic parents. + + If a field belongs to a multi-count dynamic parent, the parent is + called multiple times to parse each count, and the field's + post-unpack hook gets called after the field is unpacked during + each iteration. This requires alternative logic for getting and + setting the string data. The actual string formatting code is not + affected. + """ + + def post_unpack(self, parents, data): + parent_structure = parents[-1] + parent_data = self._get_structure_data(parents, data, parent_structure) + d = self._normalize_string(parent_data[-1][self.name]) + parent_data[-1][self.name] = d + + +class ListedDynamicStrDataField(DynamicStringField, ListedStaticStringField): + _size_field = "strLen" + _null_terminated = False + + def _get_size_data(self, parents, data): + parent_structure = parents[-1] + parent_data = self._get_structure_data(parents, data, parent_structure) + return parent_data[-1][self._size_field] + + +class DynamicVarDataField(DynamicField): + def __init__(self, *args, **kwargs): + if "array" not in kwargs: + kwargs["array"] = True + super(DynamicVarDataField, self).__init__(*args, **kwargs) + + def pre_pack(self, parents, data): + raise NotImplementedError() + + def post_unpack(self, parents, data): + var_structure = parents[-1] + var_data = self._get_structure_data(parents, data, var_structure) + data = var_data[self.name] + d = {} + for i, value in enumerate(data): + key, value = self._normalize_item(i, value) + d[key] = value + var_data[self.name] = d + + def _normalize_item(self, index, value): + raise NotImplementedError() + + +class DynamicSysVarField(DynamicVarDataField): + def _normalize_item(self, index, value): + name = "K{}".format(index) + return (name, value) + + +class DynamicUserVarField(DynamicVarDataField): + def _normalize_item(self, index, value): + name = value["name"] + value = value["num"] + return (name, value) + + +class DynamicUserStrField(DynamicVarDataField): + def _normalize_item(self, index, value): + name = value["name"] + value = value["data"] + return (name, value) + + +class DynamicVarNumField(DynamicField): + def post_unpack(self, parents, data): + parent_structure = parents[-1] + parent_data = self._get_structure_data(parents, data, parent_structure) + d = self._normalize_numeric_variable(parent_data[-1][self.name]) + parent_data[-1][self.name] = d + + def _normalize_numeric_variable(self, num_var): + t = TYPE_TABLE[num_var["numType"]] + if num_var["numType"] % 2: # complex number + return t(complex(num_var["realPart"], num_var["imagPart"])) + else: + return t(num_var["realPart"]) + + +class DynamicFormulaField(DynamicStringField): + _size_field = "formulaLen" + _null_terminated = True + + +# From Variables.h +VarHeader1 = Structure( # `version` field pulled out into VariablesRecord + name="VarHeader1", + fields=[ + Field("h", "numSysVars", help="Number of system variables (K0, K1, ...)."), + Field( + "h", "numUserVars", help="Number of user numeric variables -- may be zero." + ), + Field( + "h", "numUserStrs", help="Number of user string variables -- may be zero." + ), + ], +) + +# From Variables.h +VarHeader2 = Structure( # `version` field pulled out into VariablesRecord + name="VarHeader2", + fields=[ + Field("h", "numSysVars", help="Number of system variables (K0, K1, ...)."), + Field( + "h", "numUserVars", help="Number of user numeric variables -- may be zero." + ), + Field( + "h", "numUserStrs", help="Number of user string variables -- may be zero." + ), + Field( + "h", + "numDependentVars", + help="Number of dependent numeric variables -- may be zero.", + ), + Field( + "h", + "numDependentStrs", + help="Number of dependent string variables -- may be zero.", + ), + ], +) + +# From Variables.h +UserStrVarRec1 = DynamicStructure( + name="UserStrVarRec1", + fields=[ + ListedStaticStringField( + "c", "name", help="Name of the string variable.", count=32 + ), + Field("h", "strLen", help="The real size of the following array."), + ListedDynamicStrDataField("c", "data"), + ], +) + +# From Variables.h +UserStrVarRec2 = DynamicStructure( + name="UserStrVarRec2", + fields=[ + ListedStaticStringField( + "c", "name", help="Name of the string variable.", count=32 + ), + Field("l", "strLen", help="The real size of the following array."), + Field("c", "data"), + ], +) + +# From Variables.h +VarNumRec = Structure( + name="VarNumRec", + fields=[ + Field("h", "numType", help="Type from binarywave.TYPE_TABLE"), + Field("d", "realPart", help="The real part of the number."), + Field("d", "imagPart", help="The imag part if the number is complex."), + Field("l", "reserved", help="Reserved - set to zero."), + ], +) + +# From Variables.h +UserNumVarRec = DynamicStructure( + name="UserNumVarRec", + fields=[ + ListedStaticStringField( + "c", "name", help="Name of the string variable.", count=32 + ), + Field("h", "type", help="0 = string, 1 = numeric."), + DynamicVarNumField( + VarNumRec, + "num", + help="Type and value of the variable if it is numeric. Not used for string.", + ), + ], +) + +# From Variables.h +UserDependentVarRec = DynamicStructure( + name="UserDependentVarRec", + fields=[ + ListedStaticStringField( + "c", "name", help="Name of the string variable.", count=32 + ), + Field("h", "type", help="0 = string, 1 = numeric."), + Field( + VarNumRec, + "num", + help="Type and value of the variable if it is numeric. Not used for string.", + ), + Field("h", "formulaLen", help="The length of the dependency formula."), + DynamicFormulaField( + "c", + "formula", + help="Start of the dependency formula. A C string including null terminator.", + ), + ], +) + + +class DynamicVarHeaderField(DynamicField): + def pre_pack(self, parents, data): + raise NotImplementedError() + + def post_unpack(self, parents, data): + var_structure = parents[-1] + var_data = self._get_structure_data(parents, data, var_structure) + var_header_structure = self.format + data = var_data["var_header"] + sys_vars_field = var_structure.get_field("sysVars") + sys_vars_field.count = data["numSysVars"] + sys_vars_field.setup() + user_vars_field = var_structure.get_field("userVars") + user_vars_field.count = data["numUserVars"] + user_vars_field.setup() + user_strs_field = var_structure.get_field("userStrs") + user_strs_field.count = data["numUserStrs"] + user_strs_field.setup() + if "numDependentVars" in data: + dependent_vars_field = var_structure.get_field("dependentVars") + dependent_vars_field.count = data["numDependentVars"] + dependent_vars_field.setup() + dependent_strs_field = var_structure.get_field("dependentStrs") + dependent_strs_field.count = data["numDependentStrs"] + dependent_strs_field.setup() + var_structure.setup() + + +Variables1 = DynamicStructure( + name="Variables1", + fields=[ + DynamicVarHeaderField(VarHeader1, "var_header", help="Variables header"), + DynamicSysVarField("f", "sysVars", help="System variables", count=0), + DynamicUserVarField( + UserNumVarRec, "userVars", help="User numeric variables", count=0 + ), + DynamicUserStrField( + UserStrVarRec1, "userStrs", help="User string variables", count=0 + ), + ], +) + + +Variables2 = DynamicStructure( + name="Variables2", + fields=[ + DynamicVarHeaderField(VarHeader2, "var_header", help="Variables header"), + DynamicSysVarField("f", "sysVars", help="System variables", count=0), + DynamicUserVarField( + UserNumVarRec, "userVars", help="User numeric variables", count=0 + ), + DynamicUserStrField( + UserStrVarRec2, "userStrs", help="User string variables", count=0 + ), + Field( + UserDependentVarRec, + "dependentVars", + help="Dependent numeric variables.", + count=0, + array=True, + ), + Field( + UserDependentVarRec, + "dependentStrs", + help="Dependent string variables.", + count=0, + array=True, + ), + ], +) + + +class DynamicVersionField(DynamicField): + def pre_pack(self, parents, byte_order): + raise NotImplementedError() + + def post_unpack(self, parents, data): + variables_structure = parents[-1] + variables_data = self._get_structure_data(parents, data, variables_structure) + version = variables_data["version"] + if variables_structure.byte_order in "@=": + should_reorder_bytes = need_to_reorder_bytes(version) + variables_structure.byte_order = byte_order(should_reorder_bytes) + else: + should_reorder_bytes = False + + old_format = variables_structure.fields[-1].format + if version == 1: + variables_structure.fields[-1].format = Variables1 + elif version == 2: + variables_structure.fields[-1].format = Variables2 + elif not should_reorder_bytes: + raise ValueError("invalid variables record version: {}".format(version)) + + if variables_structure.fields[-1].format != old_format: + variables_structure.setup() + elif should_reorder_bytes: + variables_structure.setup() + + # we might need to unpack again with the new byte order + return should_reorder_bytes + + +VariablesRecordStructure = DynamicStructure( + name="VariablesRecord", + fields=[ + DynamicVersionField("h", "version", help="Version number for this header."), + Field(Variables1, "variables", help="The rest of the variables data."), + ], +) + + +class VariablesRecord(Record): + def __init__(self, *args, **kwargs): + super(VariablesRecord, self).__init__(*args, **kwargs) + # self.header['version'] # record version always 0? + VariablesRecordStructure.byte_order = "=" + VariablesRecordStructure.setup() + stream = io.BytesIO(bytes(self.data)) + self.variables = VariablesRecordStructure.unpack_stream(stream) + self.namespace = {} + for key, value in self.variables["variables"].items(): + if key not in ["var_header"]: + self.namespace.update(value) diff --git a/igor/record/wave.py b/src/pygor/record/wave.py similarity index 61% rename from igor/record/wave.py rename to src/pygor/record/wave.py index 49ed20e..ceb2914 100644 --- a/igor/record/wave.py +++ b/src/pygor/record/wave.py @@ -1,30 +1,30 @@ # Copyright (C) 2012 W. Trevor King # -# This file is part of igor. +# This file is part of pygor. # -# igor is free software: you can redistribute it and/or modify it under the +# pygor is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # -# igor is distributed in the hope that it will be useful, but WITHOUT ANY +# pygor is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License -# along with igor. If not, see . +# along with pygor. If not, see . -from io import BytesIO as _BytesIO +from io import BytesIO -from ..binarywave import load as _loadibw +from ..binarywave import load_ibw from . import Record -class WaveRecord (Record): +class WaveRecord(Record): def __init__(self, *args, **kwargs): super(WaveRecord, self).__init__(*args, **kwargs) - self.wave = _loadibw(_BytesIO(bytes(self.data))) + self.wave = load_ibw(BytesIO(bytes(self.data))) def __str__(self): return str(self.wave) diff --git a/igor/struct.py b/src/pygor/struct.py similarity index 90% rename from igor/struct.py rename to src/pygor/struct.py index 11f4e79..6f16e41 100644 --- a/igor/struct.py +++ b/src/pygor/struct.py @@ -1,19 +1,19 @@ # Copyright (C) 2012 W. Trevor King # -# This file is part of igor. +# This file is part of pygor. # -# igor is free software: you can redistribute it and/or modify it under the +# pygor is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # -# igor is distributed in the hope that it will be useful, but WITHOUT ANY +# pygor is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License -# along with igor. If not, see . +# along with pygor. If not, see . """Structure and Field classes for declaring structures @@ -23,15 +23,9 @@ with each field in a hierarchy of Python dictionaries. """ -from __future__ import absolute_import -import io as _io -import logging as _logging -import pprint as _pprint -import struct as _struct +import io, struct -import numpy as _numpy - -from . import LOG as _LOG +import numpy as np class Field(object): @@ -164,8 +158,7 @@ def setup(self): Use this method to recalculate dynamic properities after changing the basic properties set during initialization. """ - _LOG.debug("setup {}".format(self)) - self.item_count = _numpy.prod(self.count) # number of item repeats + self.item_count = np.prod(self.count) # number of item repeats if not self.array and self.item_count != 1: raise ValueError( "{} must be an array field to have a count of {}".format( @@ -253,7 +246,6 @@ def pack_item(self, item=None): def unpack_data(self, data): """Inverse of .pack_data""" - _LOG.debug("unpack {} for {} {}".format(data, self, self.format)) iterator = iter(data) try: items = [next(iterator) for i in range(self.arg_count)] @@ -287,10 +279,7 @@ def unpack_data(self, data): else: raise NotImplementedError("reshape Structure field") else: - unpacked = _numpy.array(unpacked) - _LOG.debug( - "reshape {} data from {} to {}".format(self, unpacked.shape, count) - ) + unpacked = np.array(unpacked) unpacked = unpacked.reshape(count) return unpacked @@ -351,7 +340,7 @@ def _get_structure_data(self, parents, data, structure): return d -class Structure(_struct.Struct): +class Structure(struct.Struct): r"""Represent a C structure. A convenient wrapper around struct.Struct that uses Fields and @@ -501,13 +490,11 @@ def setup(self): Use this method to recalculate dynamic properities after changing the basic properties set during initialization. """ - _LOG.debug("setup {!r}".format(self)) self.set_byte_order(self.byte_order) self.get_format() def set_byte_order(self, byte_order): """Allow changing the format byte_order on the fly.""" - _LOG.debug("set byte order for {!r} to {}".format(self, byte_order)) self.byte_order = byte_order for field in self.fields: if isinstance(field.format, Structure): @@ -520,12 +507,11 @@ def get_format(self): format = format.replace("P", "I") try: super(Structure, self).__init__(format=format) - except _struct.error as e: + except struct.error as e: raise ValueError((e, format)) return format def sub_format(self): - _LOG.debug("calculate sub-format for {!r}".format(self)) for field in self.fields: if isinstance(field.format, Structure): field_format = list(field.format.sub_format()) * field.item_count @@ -582,11 +568,6 @@ def unpack(self, *args, **kwargs): return self._unpack_item(args) def unpack_from(self, buffer, offset=0, *args, **kwargs): - _LOG.debug( - "unpack {!r} for {!r} ({}, offset={}) with {} ({})".format( - buffer, self, len(buffer), offset, self.format, self.size - ) - ) args = super(Structure, self).unpack_from(buffer, offset, *args, **kwargs) return self._unpack_item(args) @@ -600,9 +581,6 @@ def __init__(self, stream): def read(self, size): data = self.stream.read(size) - _LOG.debug( - "read {} from {}: ({}) {!r}".format(size, self.stream, len(data), data) - ) return data @@ -705,10 +683,8 @@ def _pre_pack(self, parents=None, data=None): parents = parents + [self] for f in self.fields: if hasattr(f, "pre_pack"): - _LOG.debug("pre-pack {}".format(f)) f.pre_pack(parents=parents, data=data) if isinstance(f.format, DynamicStructure): - _LOG.debug("pre-pack {!r}".format(f.format)) f._pre_pack(parents=parents, data=data) def pack(self, data): @@ -728,25 +704,14 @@ def unpack_stream(self, stream, parents=None, data=None, d=None): if data is None: parents = [self] data = d = {} - if _LOG.level <= _logging.DEBUG: - stream = DebuggingStream(stream) else: parents = parents + [self] for f in self.fields: - _LOG.debug( - "parsing {!r}.{} (count={}, item_count={})".format( - self, f, f.count, f.item_count - ) - ) - if _LOG.level <= _logging.DEBUG: - _LOG.debug("data:\n{}".format(_pprint.pformat(data))) if hasattr(f, "pre_unpack"): - _LOG.debug("pre-unpack {}".format(f)) f.pre_unpack(parents=parents, data=data) if hasattr(f, "unpack"): # override default unpacking - _LOG.debug("override unpack for {}".format(f)) d[f.name] = f.unpack(stream) continue @@ -771,7 +736,6 @@ def unpack_stream(self, stream, parents=None, data=None, d=None): stream, parents=parents, data=data, d=d[f.name] ) if hasattr(f, "post_unpack"): - _LOG.debug("post-unpack {}".format(f)) repeat = f.post_unpack(parents=parents, data=data) if repeat: raise NotImplementedError( @@ -779,9 +743,6 @@ def unpack_stream(self, stream, parents=None, data=None, d=None): ) continue if isinstance(f.format, Structure): - _LOG.debug( - "parsing {} bytes for {}".format(f.format.size, f.format.format) - ) bs = [stream.read(f.format.size) for i in range(f.item_count)] def unpack(): @@ -798,14 +759,9 @@ def unpack(): field_format = self.byte_order + f.format * f.item_count field_format = field_format.replace("P", "I") try: - size = _struct.calcsize(field_format) - except _struct.error as e: - _LOG.error(e) - _LOG.error("{}.{}: {}".format(self, f, field_format)) + size = struct.calcsize(field_format) + except struct.error as e: raise - _LOG.debug( - "parsing {} bytes for preliminary {}".format(size, field_format) - ) raw = stream.read(size) if len(raw) < size: raise ValueError( @@ -817,9 +773,8 @@ def unpack(): def unpack(): field_format = self.byte_order + f.format * f.item_count field_format = field_format.replace("P", "I") - _LOG.debug("parse previous bytes using {}".format(field_format)) - struct = _struct.Struct(field_format) - items = struct.unpack(raw) + struct_instance = struct.Struct(field_format) + items = struct_instance.unpack(raw) return f.unpack_data(items) # unpacking loop @@ -827,17 +782,14 @@ def unpack(): while repeat: d[f.name] = unpack() if hasattr(f, "post_unpack"): - _LOG.debug("post-unpack {}".format(f)) repeat = f.post_unpack(parents=parents, data=data) else: repeat = False - if repeat: - _LOG.debug("repeat unpack for {}".format(f)) return data def unpack(self, string): - stream = _io.BytesIO(string) + stream = io.BytesIO(string) return self.unpack_stream(stream) def unpack_from(self, buffer, offset=0, *args, **kwargs): diff --git a/igor/util.py b/src/pygor/util.py similarity index 72% rename from igor/util.py rename to src/pygor/util.py index 3550336..c3fad47 100644 --- a/igor/util.py +++ b/src/pygor/util.py @@ -1,38 +1,25 @@ # Copyright (C) 2012 W. Trevor King # -# This file is part of igor. +# This file is part of pygor. # -# igor is free software: you can redistribute it and/or modify it under the +# pygor is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # -# igor is distributed in the hope that it will be useful, but WITHOUT ANY +# pygor is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License -# along with igor. If not, see . +# along with pygor. If not, see . "Utility functions for handling buffers" -import sys as _sys +import sys -import numpy as _numpy - - -def _ord(byte): - r"""Convert a byte to an integer. - - >>> buffer = b'\x00\x01\x02' - >>> [_ord(b) for b in buffer] - [0, 1, 2] - """ - if _sys.version_info >= (3,): - return byte - else: - return ord(byte) +import numpy as np def hex_bytes(buffer, spaces=None): @@ -49,7 +36,7 @@ def hex_bytes(buffer, spaces=None): >>> hex_bytes(b'\x00\x01\x02\x03\x04\x05\x06', spaces=3) '000102 030405 06' """ - hex_bytes = ["{:02x}".format(_ord(x)) for x in buffer] + hex_bytes = ["{:02x}".format(x) for x in buffer] if spaces is None: return "".join(hex_bytes) elif spaces == 1: @@ -75,19 +62,19 @@ def assert_null(buffer, strict=True): warning: post-data padding not zero: 00 01 02 03 >>> sys.stderr = stderr """ - if buffer and _ord(max(buffer)) != 0: + if buffer and max(buffer) != 0: hex_string = hex_bytes(buffer, spaces=1) if strict: raise ValueError(hex_string) else: - _sys.stderr.write( + sys.stderr.write( "warning: post-data padding not zero: {}\n".format(hex_string) ) # From ReadWave.c def byte_order(needToReorderBytes): - little_endian = _sys.byteorder == "little" + little_endian = sys.byteorder == "little" if needToReorderBytes: little_endian = not little_endian if little_endian: @@ -106,9 +93,9 @@ def need_to_reorder_bytes(version): # From ReadWave.c def checksum(buffer, byte_order, oldcksum, numbytes): - x = _numpy.ndarray( + x = np.ndarray( (numbytes / 2,), # 2 bytes to a short -- ignore trailing odd byte - dtype=_numpy.dtype(byte_order + "h"), + dtype=np.dtype(byte_order + "h"), buffer=buffer, ) oldcksum += x.sum() @@ -117,20 +104,3 @@ def checksum(buffer, byte_order, oldcksum, numbytes): if oldcksum > 2**31: oldcksum -= 2**31 return oldcksum & 0xFFFF - - -def _bytes(obj, encoding="utf-8"): - """Convert bytes or strings into bytes - - >>> _bytes(b'123') - '123' - >>> _bytes('123') - '123' - """ - if _sys.version_info >= (3,): - if isinstance(obj, bytes): - return obj - else: - return bytes(obj, encoding) - else: - return bytes(obj) diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/test-igorpy.py b/test/test-igorpy.py deleted file mode 100644 index c419311..0000000 --- a/test/test-igorpy.py +++ /dev/null @@ -1,202 +0,0 @@ -# Copyright (C) 2012 W. Trevor King -# -# This file is part of %(project)s. -# -# %(project)s is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 3 of the License, or (at your option) any -# later version. -# -# %(project)s is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with %(project)s. If not, see . - -r"""Test the igor.igorpy compatibility layer by loading sample files. - ->>> from pprint import pprint ->>> import igor.igorpy as igor ->>> igor.ENCODING = 'UTF-8' - -Load a packed experiment: - ->>> path = data_path('polar-graphs-demo.pxp') ->>> d = igor.load(path) ->>> print(d) - ->>> dir(d) # doctest: +ELLIPSIS -['Packages', 'W_plrX5', 'W_plrX6', ..., 'radiusData', 'radiusQ1'] - - -Navigation: - ->>> print(d.Packages) - ->>> print(d[0]) # doctest: +ELLIPSIS - - - -Variables: - ->>> v = d[0] ->>> dir(v) # doctest: +ELLIPSIS -['__class__', ..., 'depstr', 'depvar', 'format', 'sysvar', 'userstr', 'uservar'] ->>> v.depstr -{} ->>> v.depvar -{} ->>> v.format() -'' ->>> pprint(v.sysvar) # doctest: +REPORT_UDIFF -{'K0': 0.0, - 'K1': 0.0, - 'K10': 0.0, - 'K11': 0.0, - 'K12': 0.0, - 'K13': 0.0, - 'K14': 0.0, - 'K15': 0.0, - 'K16': 0.0, - 'K17': 0.0, - 'K18': 0.0, - 'K19': 0.0, - 'K2': 0.0, - 'K20': 128.0, - 'K3': 0.0, - 'K4': 0.0, - 'K5': 0.0, - 'K6': 0.0, - 'K7': 0.0, - 'K8': 0.0, - 'K9': 0.0} ->>> v.userstr -{} ->>> v.uservar -{} - - -Waves: - ->>> d.W_plrX5 - ->>> dir(d.W_plrX5) # doctest: +ELLIPSIS -['__array__', ..., 'axis', 'axis_units', 'data', ..., 'name', 'notes'] ->>> d.W_plrX5.axis # doctest: +ELLIPSIS -[array([ 0.04908739, 0.04870087, 0.04831436, 0.04792784, 0.04754133, - 0.04715481, 0.0467683 , 0.04638178, 0.04599527, 0.04560875, - ... - 0.00077303, 0.00038651, 0. ]), array([], dtype=float64), array([], dtype=float64), array([], dtype=float64)] ->>> d.W_plrX5.data_units -(u'', '', '', '') ->>> d.W_plrX5.axis_units -(u'', '', '', '') ->>> d.W_plrX5.data # doctest: +ELLIPSIS -array([ 1.83690956e-17, 2.69450769e-02, 7.65399113e-02, - 1.44305170e-01, 2.23293692e-01, 3.04783821e-01, - ... - -2.72719120e-03, 5.24539061e-08], dtype=float32) - - -Dump the whole thing: - ->>> print(d.format()) -root - - - radiusData data (128) - angleData data (128) - W_plrX5 data (128) - W_plrY5 data (128) - angleQ1 data (64) - radiusQ1 data (64) - W_plrX6 data (64) - W_plrY6 data (64) - Packages - WMDataBase - - PolarGraphs - - - - - - -Load a packed experiment without ignoring unknown records: - ->>> d = igor.load(path, ignore_unknown=False) ->>> print(d.format()) -root - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - radiusData data (128) - angleData data (128) - W_plrX5 data (128) - W_plrY5 data (128) - angleQ1 data (64) - radiusQ1 data (64) - W_plrX6 data (64) - W_plrY6 data (64) - Packages - WMDataBase - - PolarGraphs - - - - - - -Try to load a binary wave: - ->>> path = data_path('mac-double.ibw') ->>> d = igor.load(path) -Traceback (most recent call last): - ... -IOError: final record too long; bad pxp file? -""" - -import os.path - -from igor import LOG - - -_this_dir = os.path.dirname(__file__) -_data_dir = os.path.join(_this_dir, 'data') - -def data_path(filename): - LOG.info('Testing igorpy compatibility {}\n'.format(filename)) - path = os.path.join(_data_dir, filename) - return path diff --git a/test/test.py b/test/test.py deleted file mode 100644 index 1b40812..0000000 --- a/test/test.py +++ /dev/null @@ -1,1500 +0,0 @@ -# Copyright (C) 2012-2015 W. Trevor King -# -# This file is part of %(project)s. -# -# %(project)s is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 3 of the License, or (at your option) any -# later version. -# -# %(project)s is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with %(project)s. If not, see . - -r"""Test the igor module by loading sample files. - ->>> dumpibw('mac-double.ibw') # doctest: +REPORT_UDIFF -{'version': 2, - 'wave': {'bin_header': {'checksum': 25137, - 'noteSize': 0, - 'pictSize': 0, - 'wfmSize': 166}, - 'note': '', - 'padding': array([], dtype=float64), - 'wData': array([ 5., 4., 3., 2., 1.]), - 'wave_header': {'aModified': 0, - 'bname': 'double', - 'botFullScale': 0.0, - 'creationDate': 3001587842, - 'dataUnits': array(['', '', '', ''], - dtype='|S1'), - 'depID': 0, - 'fileName': 0, - 'formula': 0, - 'fsValid': 0, - 'hsA': 1.0, - 'hsB': 0.0, - 'kindBits': '\x00', - 'modDate': 3001587842, - 'next': 0, - 'npnts': 5, - 'srcFldr': 0, - 'swModified': 0, - 'topFullScale': 0.0, - 'type': 4, - 'useBits': '\x00', - 'wModified': 0, - 'wUnused': array(['', ''], - dtype='|S1'), - 'waveNoteH': 0, - 'whVersion': 0, - 'xUnits': array(['', '', '', ''], - dtype='|S1')}}} - ->>> dumpibw('mac-textWave.ibw') # doctest: +REPORT_UDIFF -{'version': 5, - 'wave': {'bin_header': {'checksum': 5554, - 'dataEUnitsSize': 0, - 'dimEUnitsSize': array([0, 0, 0, 0]), - 'dimLabelsSize': array([0, 0, 0, 0]), - 'formulaSize': 0, - 'noteSize': 0, - 'optionsSize1': 0, - 'optionsSize2': 0, - 'sIndicesSize': 20, - 'wfmSize': 338}, - 'data_units': '', - 'dimension_units': '', - 'formula': '', - 'labels': [[], [], [], []], - 'note': '', - 'sIndices': array([ 4, 7, 8, 14, 18]), - 'wData': array(['Mary', 'had', 'a', 'little', 'lamb'], - dtype='|S6'), - 'wave_header': {'aModified': 0, - 'bname': 'text0', - 'botFullScale': 0.0, - 'creationDate': 3001571199, - 'dFolder': 69554896, - 'dLock': 0, - 'dataEUnits': 0, - 'dataUnits': array(['', '', '', ''], - dtype='|S1'), - 'depID': 22, - 'dimEUnits': array([0, 0, 0, 0]), - 'dimLabels': array([0, 0, 0, 0]), - 'dimUnits': array([['', '', '', ''], - ['', '', '', ''], - ['', '', '', ''], - ['', '', '', '']], - dtype='|S1'), - 'fileName': 0, - 'formula': 0, - 'fsValid': 0, - 'kindBits': '\x00', - 'modDate': 3001571215, - 'nDim': array([5, 0, 0, 0]), - 'next': 0, - 'npnts': 5, - 'sIndices': 69557296, - 'sfA': array([ 1., 1., 1., 1.]), - 'sfB': array([ 0., 0., 0., 0.]), - 'srcFldr': 0, - 'swModified': 1, - 'topFullScale': 0.0, - 'type': 0, - 'useBits': '\x00', - 'wModified': 0, - 'waveNoteH': 0, - 'whUnused': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - 'whVersion': 1, - 'whpad1': array(['', '', '', '', '', ''], - dtype='|S1'), - 'whpad2': 0, - 'whpad3': 0, - 'whpad4': 0}}} - ->>> dumpibw('mac-version2.ibw') # doctest: +REPORT_UDIFF -{'version': 2, - 'wave': {'bin_header': {'checksum': -16803, - 'noteSize': 15, - 'pictSize': 0, - 'wfmSize': 146}, - 'note': 'This is a test.', - 'padding': array([], dtype=float64), - 'wData': array([ 5., 4., 3., 2., 1.], dtype=float32), - 'wave_header': {'aModified': 0, - 'bname': 'version2', - 'botFullScale': 0.0, - 'creationDate': 3001251979, - 'dataUnits': array(['', '', '', ''], - dtype='|S1'), - 'depID': 0, - 'fileName': 0, - 'formula': 0, - 'fsValid': 0, - 'hsA': 1.0, - 'hsB': 0.0, - 'kindBits': '\x00', - 'modDate': 3001573594, - 'next': 0, - 'npnts': 5, - 'srcFldr': 0, - 'swModified': 0, - 'topFullScale': 0.0, - 'type': 2, - 'useBits': '\x00', - 'wModified': 0, - 'wUnused': array(['', ''], - dtype='|S1'), - 'waveNoteH': 0, - 'whVersion': 0, - 'xUnits': array(['', '', '', ''], - dtype='|S1')}}} - ->>> dumpibw('mac-version3Dependent.ibw') # doctest: +REPORT_UDIFF -{'version': 3, - 'wave': {'bin_header': {'checksum': -32334, - 'formulaSize': 4, - 'noteSize': 0, - 'pictSize': 0, - 'wfmSize': 126}, - 'formula': ' K0', - 'note': '', - 'padding': array([], dtype=float64), - 'wData': array([], dtype=float32), - 'wave_header': {'aModified': 3, - 'bname': 'version3Dependent', - 'botFullScale': 0.0, - 'creationDate': 0, - 'dataUnits': array(['', '', '', ''], - dtype='|S1'), - 'depID': 23, - 'fileName': 0, - 'formula': 103408364, - 'fsValid': 0, - 'hsA': 1.0, - 'hsB': 0.0, - 'kindBits': '\x00', - 'modDate': 3001672861, - 'next': 0, - 'npnts': 10, - 'srcFldr': 0, - 'swModified': 1, - 'topFullScale': 0.0, - 'type': 2, - 'useBits': '\x00', - 'wModified': 1, - 'wUnused': array(['', ''], - dtype='|S1'), - 'waveNoteH': 0, - 'whVersion': 0, - 'xUnits': array(['', '', '', ''], - dtype='|S1')}}} - ->>> dumpibw('mac-version5.ibw') # doctest: +REPORT_UDIFF -{'version': 5, - 'wave': {'bin_header': {'checksum': -12033, - 'dataEUnitsSize': 0, - 'dimEUnitsSize': array([0, 0, 0, 0]), - 'dimLabelsSize': array([64, 0, 0, 0]), - 'formulaSize': 0, - 'noteSize': 15, - 'optionsSize1': 0, - 'optionsSize2': 0, - 'sIndicesSize': 0, - 'wfmSize': 340}, - 'data_units': '', - 'dimension_units': '', - 'formula': '', - 'labels': [['', 'Column0'], [], [], []], - 'note': 'This is a test.', - 'sIndices': array([], dtype=float64), - 'wData': array([ 5., 4., 3., 2., 1.], dtype=float32), - 'wave_header': {'aModified': 0, - 'bname': 'version5', - 'botFullScale': 0.0, - 'creationDate': 3001252180, - 'dFolder': 69554896, - 'dLock': 0, - 'dataEUnits': 0, - 'dataUnits': array(['', '', '', ''], - dtype='|S1'), - 'depID': 27, - 'dimEUnits': array([0, 0, 0, 0]), - 'dimLabels': array([69554136, 0, 0, 0]), - 'dimUnits': array([['', '', '', ''], - ['', '', '', ''], - ['', '', '', ''], - ['', '', '', '']], - dtype='|S1'), - 'fileName': 69554292, - 'formula': 0, - 'fsValid': 0, - 'kindBits': '\x00', - 'modDate': 3001573601, - 'nDim': array([5, 0, 0, 0]), - 'next': 69555212, - 'npnts': 5, - 'sIndices': 0, - 'sfA': array([ 1., 1., 1., 1.]), - 'sfB': array([ 0., 0., 0., 0.]), - 'srcFldr': -32349, - 'swModified': 1, - 'topFullScale': 0.0, - 'type': 2, - 'useBits': '\x00', - 'wModified': 0, - 'waveNoteH': 69554032, - 'whUnused': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - 'whVersion': 1, - 'whpad1': array(['', '', '', '', '', ''], - dtype='|S1'), - 'whpad2': 0, - 'whpad3': 0, - 'whpad4': 0}}} - ->>> dumpibw('mac-zeroPointWave.ibw') # doctest: +REPORT_UDIFF -{'version': 5, - 'wave': {'bin_header': {'checksum': -15649, - 'dataEUnitsSize': 0, - 'dimEUnitsSize': array([0, 0, 0, 0]), - 'dimLabelsSize': array([0, 0, 0, 0]), - 'formulaSize': 0, - 'noteSize': 0, - 'optionsSize1': 0, - 'optionsSize2': 0, - 'sIndicesSize': 0, - 'wfmSize': 320}, - 'data_units': '', - 'dimension_units': '', - 'formula': '', - 'labels': [[], [], [], []], - 'note': '', - 'sIndices': array([], dtype=float64), - 'wData': array([], dtype=float32), - 'wave_header': {'aModified': 3, - 'bname': 'zeroWave', - 'botFullScale': 0.0, - 'creationDate': 3001573964, - 'dFolder': 69554896, - 'dLock': 0, - 'dataEUnits': 0, - 'dataUnits': array(['', '', '', ''], - dtype='|S1'), - 'depID': 29, - 'dimEUnits': array([0, 0, 0, 0]), - 'dimLabels': array([0, 0, 0, 0]), - 'dimUnits': array([['', '', '', ''], - ['', '', '', ''], - ['', '', '', ''], - ['', '', '', '']], - dtype='|S1'), - 'fileName': 0, - 'formula': 0, - 'fsValid': 0, - 'kindBits': '\x00', - 'modDate': 3001573964, - 'nDim': array([0, 0, 0, 0]), - 'next': 0, - 'npnts': 0, - 'sIndices': 0, - 'sfA': array([ 1., 1., 1., 1.]), - 'sfB': array([ 0., 0., 0., 0.]), - 'srcFldr': 0, - 'swModified': 1, - 'topFullScale': 0.0, - 'type': 2, - 'useBits': '\x00', - 'wModified': 1, - 'waveNoteH': 0, - 'whUnused': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - 'whVersion': 1, - 'whpad1': array(['', '', '', '', '', ''], - dtype='|S1'), - 'whpad2': 0, - 'whpad3': 0, - 'whpad4': 0}}} - ->>> dumpibw('win-double.ibw') # doctest: +REPORT_UDIFF -{'version': 2, - 'wave': {'bin_header': {'checksum': 28962, - 'noteSize': 0, - 'pictSize': 0, - 'wfmSize': 166}, - 'note': '', - 'padding': array([], dtype=float64), - 'wData': array([ 5., 4., 3., 2., 1.]), - 'wave_header': {'aModified': 0, - 'bname': 'double', - 'botFullScale': 0.0, - 'creationDate': 3001587842, - 'dataUnits': array(['', '', '', ''], - dtype='|S1'), - 'depID': 0, - 'fileName': 0, - 'formula': 0, - 'fsValid': 0, - 'hsA': 1.0, - 'hsB': 0.0, - 'kindBits': '\x00', - 'modDate': 3001587842, - 'next': 0, - 'npnts': 5, - 'srcFldr': 0, - 'swModified': 0, - 'topFullScale': 0.0, - 'type': 4, - 'useBits': '\x00', - 'wModified': 0, - 'wUnused': array(['', ''], - dtype='|S1'), - 'waveNoteH': 0, - 'whVersion': 0, - 'xUnits': array(['', '', '', ''], - dtype='|S1')}}} - ->>> dumpibw('win-textWave.ibw') # doctest: +REPORT_UDIFF -{'version': 5, - 'wave': {'bin_header': {'checksum': 184, - 'dataEUnitsSize': 0, - 'dimEUnitsSize': array([0, 0, 0, 0]), - 'dimLabelsSize': array([0, 0, 0, 0]), - 'formulaSize': 0, - 'noteSize': 0, - 'optionsSize1': 0, - 'optionsSize2': 0, - 'sIndicesSize': 20, - 'wfmSize': 338}, - 'data_units': '', - 'dimension_units': '', - 'formula': '', - 'labels': [[], [], [], []], - 'note': '', - 'sIndices': array([ 4, 7, 8, 14, 18]), - 'wData': array(['Mary', 'had', 'a', 'little', 'lamb'], - dtype='|S6'), - 'wave_header': {'aModified': 0, - 'bname': 'text0', - 'botFullScale': 0.0, - 'creationDate': 3001571199, - 'dFolder': 8108612, - 'dLock': 0, - 'dataEUnits': 0, - 'dataUnits': array(['', '', '', ''], - dtype='|S1'), - 'depID': 32, - 'dimEUnits': array([0, 0, 0, 0]), - 'dimLabels': array([0, 0, 0, 0]), - 'dimUnits': array([['', '', '', ''], - ['', '', '', ''], - ['', '', '', ''], - ['', '', '', '']], - dtype='|S1'), - 'fileName': 7814472, - 'formula': 0, - 'fsValid': 0, - 'kindBits': '\x00', - 'modDate': 3001571215, - 'nDim': array([5, 0, 0, 0]), - 'next': 0, - 'npnts': 5, - 'sIndices': 8133100, - 'sfA': array([ 1., 1., 1., 1.]), - 'sfB': array([ 0., 0., 0., 0.]), - 'srcFldr': -1007, - 'swModified': 0, - 'topFullScale': 0.0, - 'type': 0, - 'useBits': '\x00', - 'wModified': 1, - 'waveNoteH': 0, - 'whUnused': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - 'whVersion': 1, - 'whpad1': array(['', '', '', '', '', ''], - dtype='|S1'), - 'whpad2': 0, - 'whpad3': 0, - 'whpad4': 0}}} - ->>> dumpibw('win-version2.ibw') # doctest: +REPORT_UDIFF -{'version': 2, - 'wave': {'bin_header': {'checksum': 1047, - 'noteSize': 15, - 'pictSize': 0, - 'wfmSize': 146}, - 'note': 'This is a test.', - 'padding': array([], dtype=float64), - 'wData': array([ 5., 4., 3., 2., 1.], dtype=float32), - 'wave_header': {'aModified': 0, - 'bname': 'version2', - 'botFullScale': 0.0, - 'creationDate': 3001251979, - 'dataUnits': array(['', '', '', ''], - dtype='|S1'), - 'depID': 0, - 'fileName': 0, - 'formula': 0, - 'fsValid': 0, - 'hsA': 1.0, - 'hsB': 0.0, - 'kindBits': '\x00', - 'modDate': 3001573594, - 'next': 0, - 'npnts': 5, - 'srcFldr': 0, - 'swModified': 0, - 'topFullScale': 0.0, - 'type': 2, - 'useBits': '\x00', - 'wModified': 0, - 'wUnused': array(['', ''], - dtype='|S1'), - 'waveNoteH': 0, - 'whVersion': 0, - 'xUnits': array(['', '', '', ''], - dtype='|S1')}}} - ->>> dumpibw('win-version5.ibw') # doctest: +REPORT_UDIFF -{'version': 5, - 'wave': {'bin_header': {'checksum': 13214, - 'dataEUnitsSize': 0, - 'dimEUnitsSize': array([0, 0, 0, 0]), - 'dimLabelsSize': array([64, 0, 0, 0]), - 'formulaSize': 0, - 'noteSize': 15, - 'optionsSize1': 0, - 'optionsSize2': 0, - 'sIndicesSize': 0, - 'wfmSize': 340}, - 'data_units': '', - 'dimension_units': '', - 'formula': '', - 'labels': [['', 'Column0'], [], [], []], - 'note': 'This is a test.', - 'sIndices': array([], dtype=float64), - 'wData': array([ 5., 4., 3., 2., 1.], dtype=float32), - 'wave_header': {'aModified': 0, - 'bname': 'version5', - 'botFullScale': 0.0, - 'creationDate': 3001252180, - 'dFolder': 8108612, - 'dLock': 0, - 'dataEUnits': 0, - 'dataUnits': array(['', '', '', ''], - dtype='|S1'), - 'depID': 30, - 'dimEUnits': array([0, 0, 0, 0]), - 'dimLabels': array([8138784, 0, 0, 0]), - 'dimUnits': array([['', '', '', ''], - ['', '', '', ''], - ['', '', '', ''], - ['', '', '', '']], - dtype='|S1'), - 'fileName': 8131824, - 'formula': 0, - 'fsValid': 0, - 'kindBits': '\x00', - 'modDate': 3001573601, - 'nDim': array([5, 0, 0, 0]), - 'next': 8125236, - 'npnts': 5, - 'sIndices': 0, - 'sfA': array([ 1., 1., 1., 1.]), - 'sfB': array([ 0., 0., 0., 0.]), - 'srcFldr': -1007, - 'swModified': 0, - 'topFullScale': 0.0, - 'type': 2, - 'useBits': '\x00', - 'wModified': 1, - 'waveNoteH': 8131596, - 'whUnused': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - 'whVersion': 1, - 'whpad1': array(['', '', '', '', '', ''], - dtype='|S1'), - 'whpad2': 0, - 'whpad3': 0, - 'whpad4': 0}}} - ->>> dumpibw('win-zeroPointWave.ibw') # doctest: +REPORT_UDIFF -{'version': 5, - 'wave': {'bin_header': {'checksum': 27541, - 'dataEUnitsSize': 0, - 'dimEUnitsSize': array([0, 0, 0, 0]), - 'dimLabelsSize': array([0, 0, 0, 0]), - 'formulaSize': 0, - 'noteSize': 0, - 'optionsSize1': 0, - 'optionsSize2': 0, - 'sIndicesSize': 0, - 'wfmSize': 320}, - 'data_units': '', - 'dimension_units': '', - 'formula': '', - 'labels': [[], [], [], []], - 'note': '', - 'sIndices': array([], dtype=float64), - 'wData': array([], dtype=float32), - 'wave_header': {'aModified': 3, - 'bname': 'zeroWave', - 'botFullScale': 0.0, - 'creationDate': 3001573964, - 'dFolder': 8108612, - 'dLock': 0, - 'dataEUnits': 0, - 'dataUnits': array(['', '', '', ''], - dtype='|S1'), - 'depID': 31, - 'dimEUnits': array([0, 0, 0, 0]), - 'dimLabels': array([0, 0, 0, 0]), - 'dimUnits': array([['', '', '', ''], - ['', '', '', ''], - ['', '', '', ''], - ['', '', '', '']], - dtype='|S1'), - 'fileName': 8125252, - 'formula': 0, - 'fsValid': 0, - 'kindBits': '\x00', - 'modDate': 3001573964, - 'nDim': array([0, 0, 0, 0]), - 'next': 8133140, - 'npnts': 0, - 'sIndices': 0, - 'sfA': array([ 1., 1., 1., 1.]), - 'sfB': array([ 0., 0., 0., 0.]), - 'srcFldr': -1007, - 'swModified': 0, - 'topFullScale': 0.0, - 'type': 2, - 'useBits': '\x00', - 'wModified': 1, - 'waveNoteH': 0, - 'whUnused': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - 'whVersion': 1, - 'whpad1': array(['', '', '', '', '', ''], - dtype='|S1'), - 'whpad2': 0, - 'whpad3': 0, - 'whpad4': 0}}} - ->>> dumppxp('polar-graphs-demo.pxp') # doctest: +REPORT_UDIFF, +ELLIPSIS -record 0: - -record 1: - -record 2: - -record 3: - -record 4: - -record 5: - -record 6: - -record 7: - -record 8: - -record 9: - -record 10: - -record 11: - -record 12: - -record 13: - -record 14: - -record 15: - -record 16: - -record 17: - -record 18: - -record 19: - -record 20: - -record 21: - -record 22: - -record 23: - -record 24: - -record 25: - -record 26: - -record 27: - -record 28: - -record 29: - -record 30: -{'variables': {'sysVars': {'K0': 0.0, - 'K1': 0.0, - 'K10': 0.0, - 'K11': 0.0, - 'K12': 0.0, - 'K13': 0.0, - 'K14': 0.0, - 'K15': 0.0, - 'K16': 0.0, - 'K17': 0.0, - 'K18': 0.0, - 'K19': 0.0, - 'K2': 0.0, - 'K20': 128.0, - 'K3': 0.0, - 'K4': 0.0, - 'K5': 0.0, - 'K6': 0.0, - 'K7': 0.0, - 'K8': 0.0, - 'K9': 0.0}, - 'userStrs': {}, - 'userVars': {}, - 'var_header': {'numSysVars': 21, - 'numUserStrs': 0, - 'numUserVars': 0}}, - 'version': 1} -record 31: -'\x95 Polar Graphs Demo, v3.01\n\n' -record 32: -{'version': 2, - 'wave': {'bin_header': {'checksum': -25004, - 'noteSize': 0, - 'pictSize': 0, - 'wfmSize': 638}, - 'note': '', - 'padding': array([], dtype=float64), - 'wData': array([ 0.30000001, 0.5448544 , 0.77480197, 0.97584349, 1.13573945, - 1.24475539, 1.2962544 , 1.28710103, 1.21785283, 1.09272552, - 0.91933674, 0.7082426 , 0.47229454, 0.22585714, -0.01606643, - -0.23874778, -0.42862982, -0.57415301, -0.6664573 , -0.69992352, - -0.67251408, -0.58589762, -0.44534767, -0.25942117, -0.03943586, - 0.20121357, 0.44787762, 0.68553883, 0.89972788, 1.0774051 , - 1.20775461, 1.28283918, 1.29808831, 1.25257373, 1.14906585, - 0.99386656, 0.79642528, 0.56876069, 0.32473388, 0.07920124, - -0.15288824, -0.35740662, -0.52190179, -0.63635898, -0.69381076, - -0.69075894, -0.62739003, -0.5075599 , -0.3385666 , -0.13069656, - 0.10339352, 0.34945396, 0.59250361, 0.81774551, 1.01146686, - 1.16187334, 1.25980926, 1.29931164, 1.27797604, 1.1971004 , - 1.06160903, 0.87975079, 0.66259789, 0.42336911, 0.17663053, - -0.06259823, -0.2797519 , -0.46160996, -0.59710097, -0.67797607, - -0.69931161, -0.65980917, -0.56187314, -0.41146588, -0.21774435, - 0.00749773, 0.25054744, 0.49660596, 0.7306987 , 0.93856692, - 1.10756063, 1.22738981, 1.29075909, 1.29381061, 1.23635852, - 1.1219027 , 0.95740634, 0.7528879 , 0.52079749, 0.2752648 , - 0.03123802, -0.19642642, -0.39386547, -0.54906607, -0.6525743 , - -0.69808841, -0.68283898, -0.60775399, -0.47740453, -0.29972947, - -0.08553842, 0.15212469, 0.39878684, 0.63943672, 0.85942155, - 1.04534864, 1.18589854, 1.2725141 , 1.29992342, 1.2664578 , - 1.17415261, 1.0286293 , 0.83874667, 0.61606491, 0.37414294, - 0.12770344, -0.1082412 , -0.31933719, -0.49272597, -0.61785328, - -0.6871013 , -0.69625437, -0.64475471, -0.53574032, -0.37584305, - -0.17479956, 0.05514668, 0.30000135], dtype=float32), - 'wave_header': {'aModified': 0, - 'bname': 'radiusData', - 'botFullScale': 0.0, - 'creationDate': 0, - 'dataUnits': array(['', '', '', ''], - dtype='|S1'), - 'depID': 0, - 'fileName': 0, - 'formula': 0, - 'fsValid': 0, - 'hsA': 0.04908738521234052, - 'hsB': 0.0, - 'kindBits': '\x00', - 'modDate': 2845545774, - 'next': 0, - 'npnts': 128, - 'srcFldr': 0, - 'swModified': 0, - 'topFullScale': 0.0, - 'type': 2, - 'useBits': '\x00', - 'wModified': 0, - 'wUnused': array(['', ''], - dtype='|S1'), - 'waveNoteH': 0, - 'whVersion': 0, - 'xUnits': array(['', '', '', ''], - dtype='|S1')}}} -record 33: -{'version': 2, - 'wave': {'bin_header': {'checksum': 28621, - 'noteSize': 0, - 'pictSize': 0, - 'wfmSize': 638}, - 'note': '', - 'padding': array([], dtype=float64), - 'wData': array([ 0. , 0.0494739 , 0.0989478 , 0.1484217 , 0.1978956 , - 0.24736951, 0.29684341, 0.34631732, 0.3957912 , 0.44526511, - 0.49473903, 0.54421294, 0.59368682, 0.6431607 , 0.69263464, - 0.74210852, 0.79158241, 0.84105635, 0.89053023, 0.94000411, - 0.98947805, 1.03895199, 1.08842587, 1.13789964, 1.18737364, - 1.23684752, 1.2863214 , 1.3357954 , 1.38526928, 1.43474305, - 1.48421705, 1.53369093, 1.58316481, 1.63263881, 1.68211269, - 1.73158658, 1.78106046, 1.83053434, 1.88000822, 1.92948222, - 1.9789561 , 2.02842999, 2.07790399, 2.12737775, 2.17685175, - 2.22632551, 2.27579927, 2.32527351, 2.37474728, 2.42422128, - 2.47369504, 2.52316904, 2.5726428 , 2.6221168 , 2.67159081, - 2.72106457, 2.77053857, 2.82001233, 2.86948609, 2.91896009, - 2.9684341 , 3.0179081 , 3.06738186, 3.11685586, 3.16632962, - 3.21580338, 3.26527762, 3.31475139, 3.36422539, 3.41369915, - 3.46317315, 3.51264691, 3.56212091, 3.61159492, 3.66106868, - 3.71054268, 3.76001644, 3.8094902 , 3.85896444, 3.90843821, - 3.95791221, 4.00738621, 4.05685997, 4.10633373, 4.15580797, - 4.20528126, 4.2547555 , 4.30422926, 4.3537035 , 4.40317726, - 4.45265102, 4.50212526, 4.55159855, 4.60107279, 4.65054703, - 4.70002079, 4.74949455, 4.79896832, 4.84844255, 4.89791584, - 4.94739008, 4.99686432, 5.04633808, 5.09581184, 5.14528561, - 5.19475985, 5.24423361, 5.29370737, 5.34318161, 5.3926549 , - 5.44212914, 5.4916029 , 5.54107714, 5.5905509 , 5.64002466, - 5.6894989 , 5.73897219, 5.78844643, 5.83792019, 5.88739443, - 5.93686819, 5.98634195, 6.03581619, 6.08528948, 6.13476372, - 6.18423796, 6.23371172, 6.28318548], dtype=float32), - 'wave_header': {'aModified': 0, - 'bname': 'angleData', - 'botFullScale': 0.0, - 'creationDate': 0, - 'dataUnits': array(['', '', '', ''], - dtype='|S1'), - 'depID': 0, - 'fileName': 0, - 'formula': 0, - 'fsValid': 0, - 'hsA': 0.04908738521234052, - 'hsB': 0.0, - 'kindBits': '\x00', - 'modDate': 2845470039, - 'next': 0, - 'npnts': 128, - 'srcFldr': 0, - 'swModified': 0, - 'topFullScale': 0.0, - 'type': 2, - 'useBits': '\x00', - 'wModified': 0, - 'wUnused': array(['', ''], - dtype='|S1'), - 'waveNoteH': 0, - 'whVersion': 0, - 'xUnits': array(['', '', '', ''], - dtype='|S1')}}} -record 34: -{'version': 5, - 'wave': {'bin_header': {'checksum': 23021, - 'dataEUnitsSize': 0, - 'dimEUnitsSize': array([0, 0, 0, 0]), - 'dimLabelsSize': array([0, 0, 0, 0]), - 'formulaSize': 80, - 'noteSize': 0, - 'optionsSize1': 0, - 'optionsSize2': 0, - 'sIndicesSize': 0, - 'wfmSize': 832}, - 'data_units': '', - 'dimension_units': '', - 'formula': ' PolarRadiusFunction(radiusData,1,0) * cos(PolarAngleFunction(angleData,3,1,2))', - 'labels': [[], [], [], []], - 'note': '', - 'sIndices': array([], dtype=float64), - 'wData': array([ 1.83690956e-17, 2.69450769e-02, 7.65399113e-02, - 1.44305170e-01, 2.23293692e-01, 3.04783821e-01, - 3.79158467e-01, 4.36888516e-01, 4.69528973e-01, - 4.70633775e-01, 4.36502904e-01, 3.66688997e-01, - 2.64211357e-01, 1.35452762e-01, -1.02594923e-02, - -1.61356136e-01, -3.04955602e-01, -4.27943677e-01, - -5.18107474e-01, -5.65230608e-01, -5.62046587e-01, - -5.04969478e-01, -3.94532531e-01, -2.35490710e-01, - -3.65724117e-02, 1.90097600e-01, 4.29877043e-01, - 6.66696191e-01, 8.84287775e-01, 1.06744885e+00, - 1.20323074e+00, 1.28195620e+00, 1.29798901e+00, - 1.25017929e+00, 1.14195395e+00, 9.81046736e-01, - 7.78884649e-01, 5.49682915e-01, 3.09332967e-01, - 7.41607845e-02, -1.40328899e-01, -3.20629656e-01, - -4.56221938e-01, -5.40310800e-01, -5.70244014e-01, - -5.47582209e-01, -4.77826297e-01, -3.69823217e-01, - -2.34920204e-01, -8.59207287e-02, 6.40354082e-02, - 2.02596441e-01, 3.19209903e-01, 4.05949473e-01, - 4.58081126e-01, 4.74326164e-01, 4.56804305e-01, - 4.10668582e-01, 3.43470216e-01, 2.64317334e-01, - 1.82909429e-01, 1.08534366e-01, 4.91267964e-02, - 1.04717268e-02, -4.36885841e-03, 4.64119762e-03, - 3.45129520e-02, 7.95329511e-02, 1.31838784e-01, - 1.82213545e-01, 2.21028924e-01, 2.39245579e-01, - 2.29380637e-01, 1.86348081e-01, 1.08093813e-01, - -4.03938442e-03, -1.45255283e-01, -3.07566285e-01, - -4.80366081e-01, -6.51240766e-01, -8.07001889e-01, - -9.34792042e-01, -1.02321768e+00, -1.06338477e+00, - -1.04975033e+00, -9.80714381e-01, -8.58889818e-01, - -6.91040277e-01, -4.87653464e-01, -2.62210011e-01, - -3.01902127e-02, 1.92100301e-01, 3.88785005e-01, - 5.45667768e-01, 6.51326835e-01, 6.98035002e-01, - 6.82368934e-01, 6.05477571e-01, 4.72992837e-01, - 2.94585884e-01, 8.31873119e-02, -1.46010652e-01, - -3.76755983e-01, -5.93006968e-01, -7.80143738e-01, - -9.26071882e-01, -1.02209401e+00, -1.06349015e+00, - -1.04976654e+00, -9.84551251e-01, -8.75151932e-01, - -7.31834948e-01, -5.66861272e-01, -3.93398553e-01, - -2.24383846e-01, -7.14399144e-02, 5.60413450e-02, - 1.51621893e-01, 2.12215677e-01, 2.38205954e-01, - 2.33226836e-01, 2.03656554e-01, 1.57870770e-01, - 1.05330117e-01, 5.55786416e-02, 1.72677450e-02, - -2.72719120e-03, 5.24539061e-08], dtype=float32), - 'wave_header': {'aModified': 0, - 'bname': 'W_plrX5', - 'botFullScale': 0.0, - 'creationDate': 0, - 'dFolder': 7848580, - 'dLock': 0, - 'dataEUnits': 0, - 'dataUnits': array(['', '', '', ''], - dtype='|S1'), - 'depID': 24, - 'dimEUnits': array([0, 0, 0, 0]), - 'dimLabels': array([0, 0, 0, 0]), - 'dimUnits': array([['', '', '', ''], - ['', '', '', ''], - ['', '', '', ''], - ['', '', '', '']], - dtype='|S1'), - 'fileName': 0, - 'formula': 8054500, - 'fsValid': 0, - 'kindBits': '\x00', - 'modDate': 2985072242, - 'nDim': array([128, 0, 0, 0]), - 'next': 8054516, - 'npnts': 128, - 'sIndices': 0, - 'sfA': array([ 0.04908739, 1. , 1. , 1. ]), - 'sfB': array([ 0., 0., 0., 0.]), - 'srcFldr': 0, - 'swModified': 0, - 'topFullScale': 0.0, - 'type': 2, - 'useBits': '\x00', - 'wModified': 0, - 'waveNoteH': 0, - 'whUnused': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - 'whVersion': 1, - 'whpad1': array(['', '', '', '', '', ''], - dtype='|S1'), - 'whpad2': 0, - 'whpad3': 0, - 'whpad4': 0}}} -record 35: -{'version': 5, - 'wave': {'bin_header': {'checksum': -9146, - 'dataEUnitsSize': 0, - 'dimEUnitsSize': array([0, 0, 0, 0]), - 'dimLabelsSize': array([0, 0, 0, 0]), - 'formulaSize': 80, - 'noteSize': 82, - 'optionsSize1': 0, - 'optionsSize2': 0, - 'sIndicesSize': 0, - 'wfmSize': 832}, - 'data_units': '', - 'dimension_units': '', - 'formula': ' PolarRadiusFunction(radiusData,1,0) * sin(PolarAngleFunction(angleData,3,1,2))', - 'labels': [[], [], [], []], - 'note': 'shadowX=W_plrX5,appendRadius=radiusData,appendAngleData=angleData,angleDataUnits=2', - 'sIndices': array([], dtype=float64), - 'wData': array([ 0.30000001, 0.54418772, 0.77101213, 0.96511477, 1.1135726 , - 1.20686483, 1.23956215, 1.21068466, 1.12370288, 0.98618096, - 0.80910152, 0.60592639, 0.39147732, 0.18073183, -0.01236418, - -0.17596789, -0.30120692, -0.38277394, -0.41920158, -0.41280419, - -0.36929506, -0.29712263, -0.20658807, -0.10882771, -0.01475283, - 0.06595302, 0.12569843, 0.15962352, 0.16596791, 0.14613269, - 0.10443594, 0.04758934, -0.01605497, -0.0774129 , -0.12764584, - -0.15911636, -0.16622847, -0.14607331, -0.09881912, -0.02780312, - 0.06068454, 0.15791172, 0.25346208, 0.33617997, 0.3952153 , - 0.42107204, 0.40657136, 0.34763175, 0.24380288, 0.09848462, - -0.08117689, -0.28473276, -0.49916485, -0.70986813, -0.90179092, - -1.06064332, -1.17407382, -1.23270524, -1.23095524, -1.16755545, - -1.04573321, -0.87303019, -0.66077417, -0.42323959, -0.1765765 , - 0.06242594, 0.2776148 , 0.45470679, 0.58236426, 0.65303123, - 0.66346282, 0.61490625, 0.51291907, 0.36684951, 0.18901938, - -0.00631659, -0.20414437, -0.389898 , -0.55060786, -0.67586488, - -0.75857663, -0.79539269, -0.78681922, -0.73699296, -0.65315133, - -0.54485315, -0.42300734, -0.29883695, -0.18282266, -0.08376524, - -0.00802278, 0.0409977 , 0.06305727, 0.06099379, 0.04033075, - 0.00863387, -0.02533132, -0.05255322, -0.06475239, -0.05528941, - -0.01991711, 0.04269439, 0.13071296, 0.23921135, 0.36052904, - 0.48491719, 0.60139763, 0.69877088, 0.76667541, 0.79660165, - 0.78277934, 0.72283876, 0.6181944 , 0.47410288, 0.29939076, - 0.10585135, -0.09260413, -0.28104633, -0.44468346, -0.57008827, - -0.64630753, -0.66580337, -0.62512833, -0.52528399, -0.37171093, - -0.17394456, 0.0550792 , 0.30000135], dtype=float32), - 'wave_header': {'aModified': 0, - 'bname': 'W_plrY5', - 'botFullScale': 0.0, - 'creationDate': 0, - 'dFolder': 7848580, - 'dLock': 0, - 'dataEUnits': 0, - 'dataUnits': array(['', '', '', ''], - dtype='|S1'), - 'depID': 26, - 'dimEUnits': array([0, 0, 0, 0]), - 'dimLabels': array([0, 0, 0, 0]), - 'dimUnits': array([['', '', '', ''], - ['', '', '', ''], - ['', '', '', ''], - ['', '', '', '']], - dtype='|S1'), - 'fileName': 0, - 'formula': 8054532, - 'fsValid': 0, - 'kindBits': '\x00', - 'modDate': 2985072242, - 'nDim': array([128, 0, 0, 0]), - 'next': 8084972, - 'npnts': 128, - 'sIndices': 0, - 'sfA': array([ 0.04908739, 1. , 1. , 1. ]), - 'sfB': array([ 0., 0., 0., 0.]), - 'srcFldr': 0, - 'swModified': 0, - 'topFullScale': 0.0, - 'type': 2, - 'useBits': '\x00', - 'wModified': 0, - 'waveNoteH': 7996608, - 'whUnused': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - 'whVersion': 1, - 'whpad1': array(['', '', '', '', '', ''], - dtype='|S1'), - 'whpad2': 0, - 'whpad3': 0, - 'whpad4': 0}}} -record 36: -{'version': 2, - 'wave': {'bin_header': {'checksum': 14307, - 'noteSize': 0, - 'pictSize': 0, - 'wfmSize': 382}, - 'note': '', - 'padding': array([], dtype=float64), - 'wData': array([ 0.2617994 , 0.27842158, 0.29504377, 0.31166595, 0.32828814, - 0.34491032, 0.36153251, 0.3781547 , 0.39477688, 0.41139907, - 0.42802125, 0.44464344, 0.46126559, 0.47788778, 0.49450997, - 0.51113212, 0.52775431, 0.54437649, 0.56099868, 0.57762086, - 0.59424305, 0.61086524, 0.62748742, 0.64410961, 0.66073179, - 0.67735398, 0.69397616, 0.71059835, 0.72722054, 0.74384272, - 0.76046491, 0.77708709, 0.79370928, 0.81033146, 0.82695365, - 0.84357584, 0.86019802, 0.87682021, 0.89344239, 0.91006458, - 0.92668676, 0.94330889, 0.95993114, 0.97655326, 0.99317551, - 1.00979757, 1.02641988, 1.04304194, 1.05966425, 1.07628632, - 1.09290862, 1.10953069, 1.12615299, 1.14277506, 1.15939736, - 1.17601943, 1.19264174, 1.2092638 , 1.22588611, 1.24250817, - 1.25913048, 1.27575254, 1.29237485, 1.30899692], dtype=float32), - 'wave_header': {'aModified': 0, - 'bname': 'angleQ1', - 'botFullScale': 0.0, - 'creationDate': 0, - 'dataUnits': array(['', '', '', ''], - dtype='|S1'), - 'depID': 0, - 'fileName': 0, - 'formula': 0, - 'fsValid': 0, - 'hsA': 1.0, - 'hsB': 0.0, - 'kindBits': '\x00', - 'modDate': 2845473705, - 'next': 0, - 'npnts': 64, - 'srcFldr': 0, - 'swModified': 0, - 'topFullScale': 0.0, - 'type': 2, - 'useBits': '\x00', - 'wModified': 0, - 'wUnused': array(['', ''], - dtype='|S1'), - 'waveNoteH': 0, - 'whVersion': 0, - 'xUnits': array(['', '', '', ''], - dtype='|S1')}}} -record 37: -{'version': 2, - 'wave': {'bin_header': {'checksum': -12080, - 'noteSize': 0, - 'pictSize': 0, - 'wfmSize': 382}, - 'note': '', - 'padding': array([], dtype=float64), - 'wData': array([ -8.34064484, -7.66960144, -6.62294245, -6.82878971, - -8.6383152 , -11.20019722, -13.83398628, -15.95139503, - -16.18096733, -13.58062267, -9.26843071, -5.34649038, - -3.01010084, -2.30953455, -2.73682952, -3.72112942, - -4.85171413, -5.63053226, -5.48626232, -4.49401283, - -3.53216696, -3.34821796, -4.07400894, -5.87675714, - -9.11268425, -12.98700237, -15.06296921, -13.71571922, - -10.23535728, -7.01303005, -5.23288727, -5.71091986, - -9.24852943, -14.06335735, -15.846241 , -12.78800964, - -7.8465519 , -4.56293297, -3.54999399, -3.67789125, - -4.10172844, -4.78980875, -6.20238352, -8.17891598, - -9.2803278 , -8.36780167, -6.3059268 , -4.85605574, - -4.54975414, -4.52917624, -3.99160147, -3.1971693 , - -2.93472862, -3.47230864, -4.7322526 , -6.80173016, - -9.08601665, -10.00928402, -8.87677383, -6.88120317, - -5.61007977, -5.6351161 , -6.41880989, -6.8738699 ], dtype=float32), - 'wave_header': {'aModified': 0, - 'bname': 'radiusQ1', - 'botFullScale': 0.0, - 'creationDate': 0, - 'dataUnits': array(['', '', '', ''], - dtype='|S1'), - 'depID': 0, - 'fileName': 0, - 'formula': 0, - 'fsValid': 0, - 'hsA': 1.0, - 'hsB': 0.0, - 'kindBits': '\x00', - 'modDate': 2845473634, - 'next': 0, - 'npnts': 64, - 'srcFldr': 0, - 'swModified': 0, - 'topFullScale': 0.0, - 'type': 2, - 'useBits': '\x00', - 'wModified': 0, - 'wUnused': array(['', ''], - dtype='|S1'), - 'waveNoteH': 0, - 'whVersion': 0, - 'xUnits': array(['', '', '', ''], - dtype='|S1')}}} -record 38: -{'version': 5, - 'wave': {'bin_header': {'checksum': -5745, - 'dataEUnitsSize': 0, - 'dimEUnitsSize': array([0, 0, 0, 0]), - 'dimLabelsSize': array([0, 0, 0, 0]), - 'formulaSize': 78, - 'noteSize': 0, - 'optionsSize1': 0, - 'optionsSize2': 0, - 'sIndicesSize': 0, - 'wfmSize': 576}, - 'data_units': '', - 'dimension_units': '', - 'formula': ' PolarRadiusFunction(radiusQ1,1,-40) * cos(PolarAngleFunction(angleQ1,2,2,2))', - 'labels': [[], [], [], []], - 'note': '', - 'sIndices': array([], dtype=float64), - 'wData': array([ 30.58058929, 31.08536911, 31.93481636, 31.57315445, - 29.68683434, 27.10366058, 24.47453499, 22.3495121 , - 21.98692894, 24.21500397, 27.95923996, 31.28394508, - 33.12408066, 33.46794128, 32.79909515, 31.64211464, - 30.36601639, 29.40137291, 29.22361755, 29.74564171, - 30.21624565, 30.02338219, 29.0822773 , 27.28613091, - 24.38687515, 21.04944038, 19.16931915, 19.92274094, - 22.23493385, 24.27418709, 25.1893177 , 24.44671249, - 21.56310272, 17.87704659, 16.35500908, 18.09041786, - 20.97328949, 22.66550255, 22.84443283, 22.29068756, - 21.55643272, 20.67234993, 19.38551521, 17.81604385, - 16.77393341, 16.8293457 , 17.4496479 , 17.6982975 , - 17.34101677, 16.83446693, 16.56042671, 16.38027191, - 15.94310474, 15.16159916, 14.10328865, 12.76812935, - 11.41363049, 10.60795975, 10.52314186, 10.67826462, - 10.5454855 , 9.99268055, 9.22939587, 8.5736742 ], dtype=float32), - 'wave_header': {'aModified': 0, - 'bname': 'W_plrX6', - 'botFullScale': 0.0, - 'creationDate': 0, - 'dFolder': 7848580, - 'dLock': 0, - 'dataEUnits': 0, - 'dataUnits': array(['', '', '', ''], - dtype='|S1'), - 'depID': 30, - 'dimEUnits': array([0, 0, 0, 0]), - 'dimLabels': array([0, 0, 0, 0]), - 'dimUnits': array([['', '', '', ''], - ['', '', '', ''], - ['', '', '', ''], - ['', '', '', '']], - dtype='|S1'), - 'fileName': 0, - 'formula': 8052116, - 'fsValid': 0, - 'kindBits': '\x00', - 'modDate': 2985072242, - 'nDim': array([64, 0, 0, 0]), - 'next': 8324392, - 'npnts': 64, - 'sIndices': 0, - 'sfA': array([ 1., 1., 1., 1.]), - 'sfB': array([ 0., 0., 0., 0.]), - 'srcFldr': 0, - 'swModified': 0, - 'topFullScale': 0.0, - 'type': 2, - 'useBits': '\x00', - 'wModified': 0, - 'waveNoteH': 0, - 'whUnused': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - 'whVersion': 1, - 'whpad1': array(['', '', '', '', '', ''], - dtype='|S1'), - 'whpad2': 0, - 'whpad3': 0, - 'whpad4': 0}}} -record 39: -{'version': 5, - 'wave': {'bin_header': {'checksum': -16604, - 'dataEUnitsSize': 0, - 'dimEUnitsSize': array([0, 0, 0, 0]), - 'dimLabelsSize': array([0, 0, 0, 0]), - 'formulaSize': 78, - 'noteSize': 78, - 'optionsSize1': 0, - 'optionsSize2': 0, - 'sIndicesSize': 0, - 'wfmSize': 576}, - 'data_units': '', - 'dimension_units': '', - 'formula': ' PolarRadiusFunction(radiusQ1,1,-40) * sin(PolarAngleFunction(angleQ1,2,2,2))', - 'labels': [[], [], [], []], - 'note': 'shadowX=W_plrX6,appendRadius=radiusQ1,appendAngleData=angleQ1,angleDataUnits=2', - 'sIndices': array([], dtype=float64), - 'wData': array([ 8.19404411, 8.88563347, 9.70543861, 10.17177773, - 10.11173058, 9.73756695, 9.25513077, 8.8788929 , - 9.16085339, 10.56489944, 12.75579453, 14.90572262, - 16.46352959, 17.33401871, 17.68511391, 17.74635315, - 17.70048141, 17.79942513, 18.36241531, 19.38741684, - 20.41767311, 21.02259827, 21.09260368, 20.4905529 , - 18.95538521, 16.9299469 , 15.94969368, 17.14490509, - 19.78741264, 22.33615875, 23.96352196, 24.04369545, - 21.92454147, 18.79150391, 17.77407646, 20.32803917, - 24.37140465, 27.24079132, 28.40307808, 28.67787933, - 28.70550728, 28.50283432, 27.68538666, 26.36607552, - 25.73583984, 26.78374672, 28.8236084 , 30.36226463, - 30.91939545, 31.22146797, 31.97431755, 32.95656204, - 33.4611969 , 33.23248672, 32.3250885 , 30.64473915, - 28.72983551, 28.05199242, 29.29024887, 31.3501091 , - 32.7331543 , 32.87995529, 32.28799438, 31.99738503], dtype=float32), - 'wave_header': {'aModified': 0, - 'bname': 'W_plrY6', - 'botFullScale': 0.0, - 'creationDate': 0, - 'dFolder': 7848580, - 'dLock': 0, - 'dataEUnits': 0, - 'dataUnits': array(['', '', '', ''], - dtype='|S1'), - 'depID': 32, - 'dimEUnits': array([0, 0, 0, 0]), - 'dimLabels': array([0, 0, 0, 0]), - 'dimUnits': array([['', '', '', ''], - ['', '', '', ''], - ['', '', '', ''], - ['', '', '', '']], - dtype='|S1'), - 'fileName': 0, - 'formula': 7995612, - 'fsValid': 0, - 'kindBits': '\x00', - 'modDate': 2985072242, - 'nDim': array([64, 0, 0, 0]), - 'next': 0, - 'npnts': 64, - 'sIndices': 0, - 'sfA': array([ 1., 1., 1., 1.]), - 'sfB': array([ 0., 0., 0., 0.]), - 'srcFldr': 0, - 'swModified': 0, - 'topFullScale': 0.0, - 'type': 2, - 'useBits': '\x00', - 'wModified': 0, - 'waveNoteH': 7998208, - 'whUnused': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - 'whVersion': 1, - 'whpad1': array(['', '', '', '', '', ''], - dtype='|S1'), - 'whpad2': 0, - 'whpad3': 0, - 'whpad4': 0}}} -record 40: -'Packages' -record 41: -'WMDataBase' -record 42: -{'variables': {'sysVars': {'K0': 0.0, - 'K1': 0.0, - 'K10': 0.0, - 'K11': 0.0, - 'K12': 0.0, - 'K13': 0.0, - 'K14': 0.0, - 'K15': 0.0, - 'K16': 0.0, - 'K17': 0.0, - 'K18': 0.0, - 'K19': 0.0, - 'K2': 0.0, - 'K20': 128.0, - 'K3': 0.0, - 'K4': 0.0, - 'K5': 0.0, - 'K6': 0.0, - 'K7': 0.0, - 'K8': 0.0, - 'K9': 0.0}, - 'userStrs': {'u_dataBase': ';PolarGraph0:,...,useCircles=2,maxArcLine=6;', - 'u_dbBadStringChars': ',;=:', - 'u_dbCurrBag': 'PolarGraph1', - 'u_dbCurrContents': ',appendRadius=radiusQ1,...,useCircles=2,maxArcLine=6;', - 'u_dbReplaceBadChars': '\xa9\xae\x99\x9f', - 'u_str': '2'}, - 'userVars': {}, - 'var_header': {'numSysVars': 21, - 'numUserStrs': 6, - 'numUserVars': 0}}, - 'version': 1} -record 43: -'' -record 44: -'PolarGraphs' -record 45: -{'variables': {'sysVars': {'K0': 0.0, - 'K1': 0.0, - 'K10': 0.0, - 'K11': 0.0, - 'K12': 0.0, - 'K13': 0.0, - 'K14': 0.0, - 'K15': 0.0, - 'K16': 0.0, - 'K17': 0.0, - 'K18': 0.0, - 'K19': 0.0, - 'K2': 0.0, - 'K20': 128.0, - 'K3': 0.0, - 'K4': 0.0, - 'K5': 0.0, - 'K6': 0.0, - 'K7': 0.0, - 'K8': 0.0, - 'K9': 0.0}, - 'userStrs': {'u_colorList': 'black;blue;green;cyan;red;magenta;yellow;white;special', - 'u_debugStr': 'Turn Debugging On', - 'u_polAngleAxesWherePop': 'Off;Radius Start;Radius End;Radius Start and End;All Major Radii;At Listed Radii', - 'u_polAngleUnitsPop': 'deg;rad', - 'u_polLineStylePop': 'solid;dash 1;dash 2;dash 3;dash 4;dash 5;dash 6;dash 7;dash 8;dash 9;dash 10;dash 11;dash 12;dash 13;dash 14;dash 15;dash 16;dash 17;', - 'u_polOffOn': 'Off;On', - 'u_polRadAxesWherePop': ' Off; Angle Start; Angle Middle; Angle End; Angle Start and End; 0; 90; 180; -90; 0, 90; 90, 180; -180, -90; -90, 0; 0, 180; 90, -90; 0, 90, 180, -90; All Major Angles; At Listed Angles', - 'u_polRotPop': ' -90; 0; +90; +180', - 'u_popup': '', - 'u_prompt': ''}, - 'userVars': {'V_bottom': 232.0, - 'V_left': 1.0, - 'V_max': 2.4158518093414401, - 'V_min': -2.1848498883412, - 'V_right': 232.0, - 'V_top': 1.0, - 'u_UniqWaveNdx': 8.0, - 'u_UniqWinNdx': 3.0, - 'u_angle0': 0.0, - 'u_angleRange': 6.2831853071795862, - 'u_debug': 0.0, - 'u_majorDelta': 0.0, - 'u_numPlaces': 0.0, - 'u_polAngle0': 0.26179938779914941, - 'u_polAngleRange': 1.0471975511965976, - 'u_polInnerRadius': -20.0, - 'u_polMajorAngleInc': 0.26179938779914941, - 'u_polMajorRadiusInc': 10.0, - 'u_polMinorAngleTicks': 3.0, - 'u_polMinorRadiusTicks': 1.0, - 'u_polOuterRadius': 0.0, - 'u_segsPerMinorArc': 3.0, - 'u_tickDelta': 0.0, - 'u_var': 0.0, - 'u_x1': 11.450159535018935, - 'u_x2': 12.079591517721363, - 'u_y1': 42.732577139459856, - 'u_y2': 45.081649278814126}, - 'var_header': {'numSysVars': 21, - 'numUserStrs': 10, - 'numUserVars': 28}}, - 'version': 1} -record 46: -'' -record 47: -'' -record 48: -'| Platform=Windows95, IGORVersion=3.130\n\n\n\nMoveWindow/P 5.25,40.25,504.75,335\n...hook=PolarWindowHook\nEndMacro\n' -record 49: -'' -record 50: -'#include version >= 3.0\n' - -filesystem: -{'root': {'K0': 0.0, - 'K1': 0.0, - 'K10': 0.0, - 'K11': 0.0, - 'K12': 0.0, - 'K13': 0.0, - 'K14': 0.0, - 'K15': 0.0, - 'K16': 0.0, - 'K17': 0.0, - 'K18': 0.0, - 'K19': 0.0, - 'K2': 0.0, - 'K20': 128.0, - 'K3': 0.0, - 'K4': 0.0, - 'K5': 0.0, - 'K6': 0.0, - 'K7': 0.0, - 'K8': 0.0, - 'K9': 0.0, - 'Packages': {'PolarGraphs': {'V_bottom': 232.0, - 'V_left': 1.0, - 'V_max': 2.4158518093414401, - 'V_min': -2.1848498883412, - 'V_right': 232.0, - 'V_top': 1.0, - 'u_UniqWaveNdx': 8.0, - 'u_UniqWinNdx': 3.0, - 'u_angle0': 0.0, - 'u_angleRange': 6.2831853071795862, - 'u_colorList': 'black;blue;green;cyan;red;magenta;yellow;white;special', - 'u_debug': 0.0, - 'u_debugStr': 'Turn Debugging On', - 'u_majorDelta': 0.0, - 'u_numPlaces': 0.0, - 'u_polAngle0': 0.26179938779914941, - 'u_polAngleAxesWherePop': 'Off;Radius Start;Radius End;Radius Start and End;All Major Radii;At Listed Radii', - 'u_polAngleRange': 1.0471975511965976, - 'u_polAngleUnitsPop': 'deg;rad', - 'u_polInnerRadius': -20.0, - 'u_polLineStylePop': 'solid;dash 1;dash 2;dash 3;dash 4;dash 5;dash 6;dash 7;dash 8;dash 9;dash 10;dash 11;dash 12;dash 13;dash 14;dash 15;dash 16;dash 17;', - 'u_polMajorAngleInc': 0.26179938779914941, - 'u_polMajorRadiusInc': 10.0, - 'u_polMinorAngleTicks': 3.0, - 'u_polMinorRadiusTicks': 1.0, - 'u_polOffOn': 'Off;On', - 'u_polOuterRadius': 0.0, - 'u_polRadAxesWherePop': ' Off; Angle Start; Angle Middle; Angle End; Angle Start and End; 0; 90; 180; -90; 0, 90; 90, 180; -180, -90; -90, 0; 0, 180; 90, -90; 0, 90, 180, -90; All Major Angles; At Listed Angles', - 'u_polRotPop': ' -90; 0; +90; +180', - 'u_popup': '', - 'u_prompt': '', - 'u_segsPerMinorArc': 3.0, - 'u_tickDelta': 0.0, - 'u_var': 0.0, - 'u_x1': 11.450159535018935, - 'u_x2': 12.079591517721363, - 'u_y1': 42.732577139459856, - 'u_y2': 45.081649278814126}, - 'WMDataBase': {'u_dataBase': ';PolarGraph0:,appendRadius=radiusData,...,useCircles=2,maxArcLine=6;', - 'u_dbBadStringChars': ',;=:', - 'u_dbCurrBag': 'PolarGraph1', - 'u_dbCurrContents': ',appendRadius=radiusQ1,...,useCircles=2,maxArcLine=6;', - 'u_dbReplaceBadChars': '\xa9\xae\x99\x9f', - 'u_str': '2'}}, - 'W_plrX5': , - 'W_plrX6': , - 'W_plrY5': , - 'W_plrY6': , - 'angleData': , - 'angleQ1': , - 'radiusData': , - 'radiusQ1': }} - -walking filesystem: -walk callback on ([], root, {'K0': 0.0,...}) -walk callback on (['root'], K0, 0.0) -walk callback on (['root'], K1, 0.0) -walk callback on (['root'], K10, 0.0) -... -walk callback on (['root'], K9, 0.0) -walk callback on (['root'], Packages, {'PolarGraphs': ...}) -walk callback on (['root', 'Packages'], PolarGraphs, {...}) -walk callback on (['root', 'Packages', 'PolarGraphs'], V_bottom, 232.0) -... -walk callback on (['root', 'Packages'], WMDataBase, {...}) -... -walk callback on (['root'], radiusQ1, ) -""" - -import os.path -from pprint import pformat - -from igor import LOG -from igor.binarywave import load as loadibw -from igor.packed import load as loadpxp -from igor.packed import walk as _walk -from igor.record.base import TextRecord -from igor.record.folder import FolderStartRecord, FolderEndRecord -from igor.record.variables import VariablesRecord -from igor.record.wave import WaveRecord - - -_this_dir = os.path.dirname(__file__) -_data_dir = os.path.join(_this_dir, 'data') - -def dumpibw(filename): - LOG.info('Testing {}\n'.format(filename)) - path = os.path.join(_data_dir, filename) - data = loadibw(path) - pprint(data) - -def walk_callback(dirpath, key, value): - print('walk callback on ({}, {}, {})'.format( - dirpath, key, pformat(value))) - -def dumppxp(filename, walk=True): - LOG.info('Testing {}\n'.format(filename)) - path = os.path.join(_data_dir, filename) - records,filesystem = loadpxp(path) - for i,record in enumerate(records): - print('record {}:'.format(i)) - if isinstance(record, (FolderStartRecord, FolderEndRecord)): - pprint(record.null_terminated_text) - elif isinstance(record, TextRecord): - pprint(record.text) - elif isinstance(record, VariablesRecord): - pprint(record.variables) - elif isinstance(record, WaveRecord): - pprint(record.wave) - else: - pprint(record) - print('\nfilesystem:') - pprint(filesystem) - if walk: - print('\nwalking filesystem:') - _walk(filesystem, walk_callback) - -def pprint(data): - lines = pformat(data).splitlines() - print('\n'.join([line.rstrip() for line in lines])) diff --git a/test/test_windows.py b/test/test_windows.py new file mode 100644 index 0000000..324f53d --- /dev/null +++ b/test/test_windows.py @@ -0,0 +1,30 @@ +from pathlib import Path +from pygor.binarywave import load_ibw +from pygor import load + +TEST_DATA_DIRECTORY = Path(__file__).parent / "data" + + +def test_windows_ibw(): + try: + igor_waves = [ + load_ibw(str(TEST_DATA_DIRECTORY / filename)) + for filename in [ + "win-double.ibw", + "win-textWave.ibw", + "win-version2.ibw", + "win-version5.ibw", + "win-zeroPointWave.ibw", + ] + ] + assert True + except Exception: + assert False, "Loading Igor Binary Waves ran into an exception." + + +def test_load_pxp(): + try: + load(str(TEST_DATA_DIRECTORY / "polar-graphs-demo.pxp")) + assert True + except Exception: + assert False, "Loading PXP ran into an exception." diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..f67b52c --- /dev/null +++ b/uv.lock @@ -0,0 +1,120 @@ +version = 1 +requires-python = ">=3.12" + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "iniconfig" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050 }, +] + +[[package]] +name = "numpy" +version = "2.2.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348 }, + { url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362 }, + { url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103 }, + { url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382 }, + { url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462 }, + { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618 }, + { url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511 }, + { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783 }, + { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506 }, + { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190 }, + { url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828 }, + { url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006 }, + { url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765 }, + { url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736 }, + { url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719 }, + { url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072 }, + { url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213 }, + { url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632 }, + { url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532 }, + { url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885 }, + { url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467 }, + { url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144 }, + { url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217 }, + { url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014 }, + { url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935 }, + { url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122 }, + { url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143 }, + { url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260 }, + { url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225 }, + { url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374 }, +] + +[[package]] +name = "packaging" +version = "25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469 }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538 }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217 }, +] + +[[package]] +name = "pygor" +version = "1.0.0" +source = { editable = "." } +dependencies = [ + { name = "numpy" }, +] + +[package.dev-dependencies] +dev = [ + { name = "pytest" }, +] + +[package.metadata] +requires-dist = [{ name = "numpy", specifier = ">=2.0.0" }] + +[package.metadata.requires-dev] +dev = [{ name = "pytest", specifier = ">=8.4.1" }] + +[[package]] +name = "pytest" +version = "8.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474 }, +]