Much improved design of header definition with automagical generation of a header DTO from the format.

This commit is contained in:
Robert Smallshire
2015-04-18 07:29:13 +01:00
parent 283517219d
commit 25d2387d39
3 changed files with 342 additions and 419 deletions
-197
View File
@@ -1,197 +0,0 @@
from weakref import WeakKeyDictionary
from segpy.util import underscores_to_camelcase, first_sentence, lower_first, UNSET
def field_mangler(descriptor):
"""A factory function for creating a metaclass which detects descriptors which subclass a certain archetype.
Args:
descriptor: The type (class) of a descriptor used to identity descriptor instances which should be renamed.
"""
class NamedDescriptorMangler(type):
"""A metaclass for assigning descriptor names.
"""
def __new__(mcs, class_name, bases, class_dict):
for name, attr in class_dict.items():
# This shenanigans is necessary so we can have all the following work is a useful way
# help(class), help(instance), help(class.property) and help(instance.property)
# Set the _name attribute of the field instance if it hasn't already been set
if isinstance(attr, descriptor):
if attr._name is None:
attr._name = name
# We rename the *class* and set its docstring so help() works usefully
# when called with a class containing such fields.
attr_class = attr.__class__
if issubclass(attr_class, descriptor) and attr_class is not descriptor:
attr_class.__name__ = underscores_to_camelcase(name)
attr_class.__doc__ = attr.assemble_docstring()
return super(NamedDescriptorMangler, mcs).__new__(mcs, class_name, bases, class_dict)
return NamedDescriptorMangler
class NamedField(object):
"""Instances of NamedField can be detected by the NamedDescriptorResolver metaclass."""
def __init__(self, docstrings=None):
# These fields can be set by the NamedDescriptorResolver metaclass
self._name = None
self._docstrings = docstrings
def assemble_docstring(self):
if self._name is None:
raise RuntimeError("Field name is not set.")
return first_sentence(self._docstrings.get(self._name, '<unknown-field>'))
# ----------------------------------------------------------------------------------------------------------------------
class BinaryField(object):
"""Aggregates the name, type and offset of a field within a binary format.
"""
def __init__(self, field):
self._field = field
@property
def name(self):
"""The field name (read-only)."""
if self._field._name is not None:
return self._field._name
raise AttributeError("Field is unnamed")
@property
def ftype(self):
"""The type of the field described using the classes in segpy.types"""
return self._field._field_type
@property
def offset(self):
"""The one-based (IMPORTANT!) offset in bytes of the field from the beginning of the structure"""
return self._field._offset
class BinaryFieldDescriptor(NamedField):
"""A field of a binary format, specifying type, file offset, value and default.
Attributes:
ftype: The type of the field.
offset: The byte offset of the start of the field.
"""
def __init__(self, ftype, offset, docstrings):
super(BinaryFieldDescriptor, self).__init__(docstrings)
self._ftype = ftype
self._offset = offset
self._data = WeakKeyDictionary()
def __get__(self, instance, owner):
if instance is None:
return self
return self._data.setdefault(instance, BinaryField(self))
def __set__(self, instance, value):
raise AttributeError("Can't set binary field attribute. Did you intend to set field.ftype or field.offset instead?")
def __delete__(self, instance):
raise AttributeError("Can't delete field attribute. Did you intend to set field.ftype or field.offset instead?")
def assemble_docstring(self):
if self._name is None:
raise RuntimeError("Field name is not set.")
return "A BinaryField containing the type and byte offset for the {}".format(
first_sentence(lower_first(self._docstrings.get(self._name, '<unknown-field>'))))
#-----------------------------------------------------------------------------------------------------------------------
class ValueField(object):
"""Aggregates the name, type and offset of a field within a binary format.
"""
def __init__(self, field):
self._field = field
self._value = UNSET
@property
def name(self):
"""The field name (read-only)."""
if self._field._name is not None:
return self._field._name
raise AttributeError("Field is unnamed")
@property
def default(self):
return self._field._default
@property
def value(self):
"""The type of the field described using the classes in segpy.types"""
return self._value if self._value is not UNSET else self.default
@value.setter
def value(self, v):
self._value = v
class ValueFieldDescriptor(NamedField):
"""A field of a binary format, specifying type, file offset, value and default.
Attributes:
value: The value of the field.
"""
def __init__(self, default, docstrings):
super(ValueFieldDescriptor, self).__init__(docstrings)
self._default = default
self._data = WeakKeyDictionary()
def __get__(self, instance, owner):
if instance is None:
return self
return self._data.setdefault(instance, ValueField(self))
def __set__(self, instance, value):
raise AttributeError("Can't set field attribute. Did you intend to set field.value instead?")
def __delete__(self, instance):
raise AttributeError("Can't delete field attribute. Did you intend to set field.value instead?")
def assemble_docstring(self):
if self._name is None:
raise RuntimeError("Field name is not set.")
return "A ValueField containing the value and default for the {}".format(
first_sentence(lower_first(self._docstrings.get(self._name, '<unknown-field>'))))
# ----------------------------------------------------------------------------------------------------------------------
def field(descriptor, docstrings, **kwargs):
"""
Args:
descriptor: The type (class) of the descriptor be be created.
docstrings: A dictionary of strings where the docstring for the descriptor can be looked up using the
descriptor name. For this to work correctly, the class hosting the descriptors should use a metaclass
created with field_manger(descriptor). e.g. class Foo(metaclass=field_mangler(descriptor))
**kwargs: Any other arguments will be forwarded to the descriptor constructor.
Returns:
An instance of the descriptor class.
"""
# Create a class specifically for this field. This class will later get
# renamed when the NamedDescriptorMangler metaclass does its job, to
# a class name based on the field name.
class SpecificField(descriptor):
pass
return SpecificField(docstrings=docstrings, **kwargs)
+336 -217
View File
@@ -1,235 +1,354 @@
from segpy.binary_headers import BinaryFormat, Header
from segpy.field import field, BinaryFieldDescriptor, ValueFieldDescriptor
from segpy.types import Int32
from collections import OrderedDict
import textwrap
from weakref import WeakKeyDictionary
from segpy.docstring import docstring_property
from segpy.types import Int32, Int16
from segpy.util import underscores_to_camelcase, first_sentence
# Field descriptions use by both the TraceHeaderFormat and TraceHeader classes, which extract their documentation
# from this dictionary. The first sentence of each docstring (up to the first stop) must be usable as a brief
# description and make sense in the sentence "This field is the <brief>" when the first character is lower-cased.
TRACE_HEADER_DOCS = {
'line_sequence_num': "Trace sequence number within line. Numbers continue to increase if the same line continues "
"across multiple SEG Y files. Highly recommended for all types of data.",
'file_sequence_num': "Trace sequence number within SEG Y file. Each file starts with trace sequence one.",
'field_record_num': "Original field record number. Highly recommended for all types of data.",
'trace_num': "Trace number within the original field record. Highly recommended for all types of data."
}
def is_magic_name(name):
return len(name) > 4 and name.startswith('__') and name.endswith('__')
def trace_header_format_field(ftype, offset):
return field(descriptor=BinaryFieldDescriptor, docstrings=TRACE_HEADER_DOCS, ftype=ftype, offset=offset)
class FormatMeta(type):
"""A metaclass for header format classes.
"""
@classmethod
def __prepare__(mcs, name, bases):
return OrderedDict()
def __new__(mcs, name, bases, namespace):
# TODO: This is a good point to validate that the fields are in order and that the
# TODO: format specification is valid. We shouldn't even build the class otherwise.
namespace['ORDERED_FIELD_NAMES'] = tuple(name for name in namespace.keys()
if not is_magic_name(name))
for name, attr in namespace.items():
# This shenanigans is necessary so we can have all the following work is a useful way
# help(class), help(instance), help(class.property) and help(instance.property)
# Set the _name attribute of the field instance if it hasn't already been set
if isinstance(attr, NamedField):
if attr._name is None:
attr._name = name
# We rename the *class* and set its docstring so help() works usefully
# when called with a class containing such fields.
attr_class = attr.__class__
if issubclass(attr_class, NamedField) and attr_class is not NamedField:
attr_class.__name__ = underscores_to_camelcase(name)
attr_class.__doc__ = attr.documentation
return super().__new__(mcs, name, bases, namespace)
class TraceHeaderFormat(BinaryFormat):
class NamedField:
"""Instances of NamedField can be detected by the NamedDescriptorResolver metaclass."""
line_sequence_num = trace_header_format_field(Int32, offset=1)
file_sequence_num = trace_header_format_field(Int32, offset=5)
field_record_num = trace_header_format_field(Int32, offset=9)
trace_num = trace_header_format_field(Int32, offset=13)
def __init__(self, value_type, offset, default, documentation):
self._name = None # Set later by the metaclass
self._value_type = value_type
self._offset = offset
self._default = self._value_type(default)
self._documentation = documentation
@property
def name(self):
"The field name."
return self._name
@property
def value_type(self):
"The field value type (e.g. Int32)"
return self._value_type
@property
def offset(self):
"The offset it bytes from the beginning of the header."
return self._offset
@property
def default(self):
"The default value of the field. Must be convertible to value_type."
return self._default
@property
def documentation(self):
"A descriptive text string."
return self._documentation
@docstring_property(__doc__)
def __doc__(self):
return first_sentence(self._documentation)
def __repr__(self):
return first_sentence(self._documentation)
def trace_header_field(default):
return field(descriptor=ValueFieldDescriptor, docstrings=TRACE_HEADER_DOCS, default=default)
def field(value_type, offset, default, documentation):
"""
Args:
value_type: The type of the field (e.g. Int32)
offset: The offset in bytes for this field from the start of the header.
default: The default value for this field.
documentation: A docstring for the field. The first sentence should be usable
as a brief description.
Returns:
An instance of a subclass of NamedField class.
"""
# Create a class specifically for this field. This class will later get
# renamed when the NamedDescriptorMangler metaclass does its job, to
# a class name based on the field name.
class SpecificField(NamedField):
pass
return SpecificField(value_type, offset, default, documentation)
class TraceHeader(Header):
class TraceHeaderFormat(metaclass=FormatMeta):
line_sequence_num = trace_header_field(default=0)
file_sequence_num = trace_header_field(default=0)
field_record_num = trace_header_field(default=0)
trace_num = trace_header_field(default=0)
line_sequence_num = field(
Int32, offset=1, default=0, documentation=
"Trace sequence number within line — Numbers continue to increase if the same line "
"continues across multiple SEG Y files. Highly recommended for all types of data.")
file_sequence_num = field(
Int32, offset=5, default=0, documentation=
"Trace sequence number within SEG Y file — Each file starts with trace sequence one.")
field_record_num = field(
Int32, offset=9, default=0, documentation=
"Original field record number. Highly recommended for all types of data.")
trace_num = field(
Int32, offset=13, default=0, documentation=
"Trace number within the original field record. Highly recommended for all types of data.")
energy_source_point_num = field(
Int32, offset=17, default=0, documentation=
"Energy source point number — Used when more than one record occurs at the same "
"effective surface location. It is recommended that the new entry defined in Trace "
"Header bytes 197-202 be used for shotpoint number.")
ensemble_num = field(
Int32, offset=21, default=0, documentation=
"Ensemble number (i.e. CDP , CMP , CRP , etc)")
ensemble_trace_num = field(
Int32, offset=25, default=0, documentation=
"Trace number within the ensemble — Each ensemble starts with trace number one.")
trace_identification_code = field(
Int16, offset=29, default=0, documentation=
"Trace identification code")
num_vertically_summed_traces = field(
Int16, offset=31, default=1, documentation=
"Number of vertically summed traces yielding this trace. (1 is one trace, 2 is two summed traces, etc.)")
num_horizontally_stacked_traces = field(
Int16, offset=33, default=1, documentation=
"Number of horizontally stacked traces yielding this trace. (1 is one trace, 2 is two stacked traces, etc.)")
data_use = field(
Int16, offset=35, default=1, documentation=
"Data use: 1 = Production, 2 = Test")
source_receiver_offset = field(
Int32, offset=37, default=0, documentation=
"Distance from center of the source point to the center of the receiver group (negative if opposite to "
"direction in which line is shot).")
receiver_group_elevation = field(
Int32, offset=41, default=0, documentation=
"Receiver group elevation (all elevations above the Vertical datum are positive and below are negative). The "
"elevation_scalar applies to this value.")
surface_elevation_at_source = field(
Int32, offset=45, default=0, documentation=
"Surface elevation at source. The elevation_scalar applies to this value.")
source_depth_below_surface = field(
Int32, offset=49, default=0, documentation=
"Source depth below surface (a positive number). The elevation_scalar applies to this value.")
datum_elevation_at_receiver_group = field(
Int32, offset=53, default=0, documentation=
"Source depth below surface (a positive number). The elevation_scalar applies to this value.")
datum_elevation_at_source = field(
Int32, offset=57, default=0, documentation=
"Datum elevation at source. The elevation_scalar applies to this value.")
water_depth_at_source = field(
Int32, offset=61, default=0, documentation=
"Water depth at source. The elevation_scalar applies to this value.")
water_depth_at_group = field(
Int32, offset=65, default=0, documentation=
"Water depth at group. The elevation_scalar applies to this value."
)
elevation_scalar = field(
Int16, offset=69, default=1, documentation=
"Scalar to be applied to the elevations and depths specified in: receiver_group_elevation, "
"surface_elevation_at_source, source_depth_below_surface, datum_elevation_at_receiver_group, "
"datum_elevation_at_source, water_depth_at_source and water_depth_at_group, to give the real value. "
"Scalar = 1, +10, +100, +1000, or +10,000. If positive, scalar is used as a multiplier; if negative, scalar is "
"used as a divisor."
)
xy_scalar = field(
Int16, offset=71, default=1, documentation=
"Scalar to be applied to all coordinates specified in source_x, source_y, group_x, group_y, cdp_x and cdp_y to "
"give the real value. Scalar = 1, +10, +100, +1000, or +10,000. If positive, scalar is used as a multiplier; "
"if negative, scalar is used as divisor."
)
source_x = field(
Int32, offset=73, default=0, documentation=
"Source coordinate - X. The xy_scalar applies to this value. The coordinate reference system should be "
"identified through an extended header Location Data stanza. If the coordinate units are in seconds of arc, "
"decimal degrees or DMS, the X values represent longitude. A positive value designates east of Greenwich "
"Meridian and a negative value designates west."
)
source_y = field(
Int32, offset=77, default=0, documentation=
"Source coordinate - Y. The xy_scalar applies to this value. The coordinate reference system should be "
"identified through an extended header Location Data stanza. If the coordinate units are in seconds of arc, "
"decimal degrees or DMS, the Y values represent latitude. A positive value designates north of the equator and "
"a negative value designates south."
)
group_x = field(
Int32, offset=73, default=0, documentation=
"Group coordinate - X. The xy_scalar applies to this value. The coordinate reference system should be "
"identified through an extended header Location Data stanza. If the coordinate units are in seconds of arc, "
"decimal degrees or DMS, the X values represent longitude. A positive value designates east of Greenwich "
"Meridian and a negative value designates west."
)
group_y = field(
Int32, offset=77, default=0, documentation=
"Source coordinate - Y. The xy_scalar applies to this value. The coordinate reference system should be "
"identified through an extended header Location Data stanza. If the coordinate units are in seconds of arc, "
"decimal degrees or DMS, the Y values represent latitude. A positive value designates north of the equator and "
"a negative value designates south."
)
coordinate_units = field(
Int16, offset=89, default=0, documentation=
"Coordinate units: 1 = Length (meters or feet), 2 = Seconds of arc, 3 = Decimal degrees, 4 = Degrees, minutes, "
"seconds (DMS). Note: To encode ±DDDMMSS bytes this value equals ±DDD*104 + MM*102 + SS with xy_scalar set to "
"1; To encode ±DDDMMSS.ss this value equals ±DDD*106 + MM*104 + SS*102 with xy_scalar set to -100."
)
weathering_velocity = field(
Int16, offset=91, default=0, documentation=
"Weathering velocity. (ft/s or m/s as specified in Binary File Header bytes 3255- 3256)" # TODO
)
subweathering_velocity = field(
Int16, offset=93, default=0, documentation=
"Subweathering velocity. (ft/s or m/s as specified in Binary File Header bytes 3255-3256)" # TODO
)
uphole_time_at_source = field(
Int16, offset=95, default=0, documentation=
"Uphole time at source in milliseconds. The time_scalar applies to this value."
)
uphole_time_at_group = field(
Int16, offset=97, default=0, documentation=
"Uphole time at group in milliseconds. The time_scalar applies to this value."
)
source_static_correction = field(
Int16, offset=99, default=0, documentation=
"Source static correction in milliseconds. The time_scalar applies to this value."
)
group_static_correction = field(
Int16, offset=101, default=0, documentation=
"Group static correction in milliseconds. The time_scalar applies to this value."
)
# class TraceHeader(object):
#
# def __init__(self, trace_header_format=None):
# """Initialize a TraceHeader.
#
# Args:
# trace_header_format: An optional TraceHeaderFormat instance against which
# field values will be validated. If not provided, fields will not be validated.
# """
#
# line_sequence_num = field(default=0, documentation=
# "Trace sequence number within line — Numbers continue to increase if the same line "
# "continues across multiple SEG Y files. Highly recommended for all types of data.")
class ValueField:
def __init__(self, name, value_type, default, documentation):
self._name = name
self._value_type = value_type
self._default = default
self._documentation = documentation
self._instance_data = WeakKeyDictionary()
def __get__(self, instance, owner):
if owner is None:
return self
if instance not in self._instance_data:
return self._default
return self._instance_data[instance]
def __set__(self, instance, value):
try:
self._instance_data[instance] = self._value_type(value)
except ValueError as e:
raise ValueError("Assigned value {!r} for {} attribute must be convertible to {}: {}"
.format(value, self._name, self._value_type.__name__, e)) from e
def __delete__(self, instance):
raise AttributeError("Can't delete {} attribute".format(self._name))
@docstring_property(__doc__)
def __doc__(self):
return self._documentation
# TODO: Get documentation of these descriptors working correctly
class BuildFromFormat(type):
"""A metaclass for building a data transfer object from a format definition."""
def __new__(mcs, name, bases, namespace, format_class):
"""Create a new DTO class from a format class."""
if not format_class.__class__ is FormatMeta:
raise TypeError("Format class {} specified for class {} does not use the FormatMeta metaclass"
.format(format_class.__name__, name))
for name in format_class.ORDERED_FIELD_NAMES:
format_field = getattr(format_class, name)
namespace[name] = ValueField(name=name,
value_type=format_field.value_type,
default=format_field.default,
documentation=format_field.documentation)
return super().__new__(mcs, name, bases, namespace)
def __init__(mcs, name, bases, namespace, format_class):
super().__init__(mcs, name, bases)
pass
class TraceHeader(metaclass=BuildFromFormat, format_class=TraceHeaderFormat):
pass
# This would build the TraceHeader class from something like the original TraceHeader at the bottom of this file.
#
#
#
# class TraceHeader(object,
# #metaclass=NamedDescriptorResolver
# ):
#
# line_sequence_num = field(
# Int32, offset=1, default=0, documentation=
# "Trace sequence number within line — Numbers continue to increase if the same line "
# "continues across multiple SEG Y files. Highly recommended for all types of data.")
#
# file_sequence_num = field(
# Int32, offset=5, default=0, documentation=
# "Trace sequence number within SEG Y file — Each file starts with trace sequence one.")
#
# field_record_num = field(
# Int32, offset=9, default=0, documentation=
# "Original field record number. Highly recommended for all types of data.")
#
# trace_num = field(
# Int32, offset=13, default=0, documentation=
# "Trace number within the original field record. Highly recommended for all types of data.")
#
# energy_source_point_num = field(
# Int32, offset=17, default=0, documentation=
# "Energy source point number — Used when more than one record occurs at the same "
# "effective surface location. It is recommended that the new entry defined in Trace "
# "Header bytes 197-202 be used for shotpoint number.")
#
# ensemble_num = field(
# Int32, offset=21, default=0, documentation=
# "Ensemble number (i.e. CDP , CMP , CRP , etc)")
#
# ensemble_trace_num = field(
# Int32, offset=25, default=0, documentation=
# "Trace number within the ensemble — Each ensemble starts with trace number one.")
#
# trace_identification_code = field(
# Int16, offset=29, default=0, documentation=
# "Trace identification code")
#
# num_vertically_summed_traces = field(
# Int16, offset=31, default=1, documentation=
# "Number of vertically summed traces yielding this trace. (1 is one trace, 2 is two summed traces, etc.)")
#
# num_horizontally_stacked_traces = field(
# Int16, offset=33, default=1, documentation=
# "Number of horizontally stacked traces yielding this trace. (1 is one trace, 2 is two stacked traces, etc.)")
#
# data_use = field(
# Int16, offset=35, default=1, documentation=
# "Data use: 1 = Production, 2 = Test")
#
# source_receiver_offset = field(
# Int32, offset=37, default=0, documentation=
# "Distance from center of the source point to the center of the receiver group (negative if opposite to "
# "direction in which line is shot).")
#
# receiver_group_elevation = field(
# Int32, offset=41, default=0, documentation=
# "Receiver group elevation (all elevations above the Vertical datum are positive and below are negative). The "
# "elevation_scalar applies to this value.")
#
# surface_elevation_at_source = field(
# Int32, offset=45, default=0, documentation=
# "Surface elevation at source. The elevation_scalar applies to this value.")
#
# source_depth_below_surface = field(
# Int32, offset=49, default=0, documentation=
# "Source depth below surface (a positive number). The elevation_scalar applies to this value.")
#
# datum_elevation_at_receiver_group = field(
# Int32, offset=53, default=0, documentation=
# "Source depth below surface (a positive number). The elevation_scalar applies to this value.")
#
# datum_elevation_at_source = field(
# Int32, offset=57, default=0, documentation=
# "Datum elevation at source. The elevation_scalar applies to this value.")
#
# water_depth_at_source = field(
# Int32, offset=61, default=0, documentation=
# "Water depth at source. The elevation_scalar applies to this value.")
#
# water_depth_at_group = field(
# Int32, offset=65, default=0, documentation=
# "Water depth at group. The elevation_scalar applies to this value."
# )
#
# elevation_scalar = field(
# Int16, offset=69, default=1, documentation=
# "Scalar to be applied to the elevations and depths specified in: receiver_group_elevation, "
# "surface_elevation_at_source, source_depth_below_surface, datum_elevation_at_receiver_group, "
# "datum_elevation_at_source, water_depth_at_source and water_depth_at_group, to give the real value. "
# "Scalar = 1, +10, +100, +1000, or +10,000. If positive, scalar is used as a multiplier; if negative, scalar is "
# "used as a divisor."
# )
#
# xy_scalar = field(
# Int16, offset=71, default=1, documentation=
# "Scalar to be applied to all coordinates specified in source_x, source_y, group_x, group_y, cdp_x and cdp_y to "
# "give the real value. Scalar = 1, +10, +100, +1000, or +10,000. If positive, scalar is used as a multiplier; "
# "if negative, scalar is used as divisor."
# )
#
# source_x = field(
# Int32, offset=73, default=0, documentation=
# "Source coordinate - X. The xy_scalar applies to this value. The coordinate reference system should be "
# "identified through an extended header Location Data stanza. If the coordinate units are in seconds of arc, "
# "decimal degrees or DMS, the X values represent longitude. A positive value designates east of Greenwich "
# "Meridian and a negative value designates west."
# )
#
# source_y = field(
# Int32, offset=77, default=0, documentation=
# "Source coordinate - Y. The xy_scalar applies to this value. The coordinate reference system should be "
# "identified through an extended header Location Data stanza. If the coordinate units are in seconds of arc, "
# "decimal degrees or DMS, the Y values represent latitude. A positive value designates north of the equator and "
# "a negative value designates south."
# )
#
# group_x = field(
# Int32, offset=73, default=0, documentation=
# "Group coordinate - X. The xy_scalar applies to this value. The coordinate reference system should be "
# "identified through an extended header Location Data stanza. If the coordinate units are in seconds of arc, "
# "decimal degrees or DMS, the X values represent longitude. A positive value designates east of Greenwich "
# "Meridian and a negative value designates west."
# )
#
# group_y = field(
# Int32, offset=77, default=0, documentation=
# "Source coordinate - Y. The xy_scalar applies to this value. The coordinate reference system should be "
# "identified through an extended header Location Data stanza. If the coordinate units are in seconds of arc, "
# "decimal degrees or DMS, the Y values represent latitude. A positive value designates north of the equator and "
# "a negative value designates south."
# )
#
# coordinate_units = field(
# Int16, offset=89, default=0, documentation=
# "Coordinate units: 1 = Length (meters or feet), 2 = Seconds of arc, 3 = Decimal degrees, 4 = Degrees, minutes, "
# "seconds (DMS). Note: To encode ±DDDMMSS bytes this value equals ±DDD*104 + MM*102 + SS with xy_scalar set to "
# "1; To encode ±DDDMMSS.ss this value equals ±DDD*106 + MM*104 + SS*102 with xy_scalar set to -100."
# )
#
# weathering_velocity = field(
# Int16, offset=91, default=0, documentation=
# "Weathering velocity. (ft/s or m/s as specified in Binary File Header bytes 3255- 3256)" # TODO
# )
#
# subweathering_velocity = field(
# Int16, offset=93, default=0, documentation=
# "Subweathering velocity. (ft/s or m/s as specified in Binary File Header bytes 3255-3256)" # TODO
# )
#
# uphole_time_at_source = field(
# Int16, offset=95, default=0, documentation=
# "Uphole time at source in milliseconds. The time_scalar applies to this value."
# )
#
# uphole_time_at_group = field(
# Int16, offset=97, default=0, documentation=
# "Uphole time at group in milliseconds. The time_scalar applies to this value."
# )
#
# source_static_correction = field(
# Int16, offset=99, default=0, documentation=
# "Source static correction in milliseconds. The time_scalar applies to this value."
# )
#
# group_static_correction = field(
# Int16, offset=101, default=0, documentation=
# "Group static correction in milliseconds. The time_scalar applies to this value."
# )
#
#
#
# def size_of(t):
+6 -5
View File
@@ -5,12 +5,13 @@ class Int16(int):
SIZE = 2
def __new__(cls, *args, **kwargs):
instance = super(cls).__new__(cls, *args, **kwargs)
instance = super().__new__(cls, *args, **kwargs)
if not (Int16.MINIMUM <= instance <= Int16.MAXIMUM):
raise ValueError("{} value {!r} outside range {}{}".format(cls.__name__, instance,
raise ValueError("{} value {!r} outside range {} to {}".format(cls.__name__, instance,
cls.MINIMUM, cls.MAXIMUM))
return instance
class Int32(int):
MINIMUM = -2147483648
@@ -18,9 +19,9 @@ class Int32(int):
SIZE = 4
def __new__(cls, *args, **kwargs):
instance = super(Int32, cls).__new__(cls, *args, **kwargs)
if not (Int16.MINIMUM <= instance <= Int16.MAXIMUM):
raise ValueError("{} value {!r} outside range {}{}".format(cls.__name__, instance,
instance = super().__new__(cls, *args, **kwargs)
if not (Int32.MINIMUM <= instance <= Int32.MAXIMUM):
raise ValueError("{} value {!r} outside range {} to {}".format(cls.__name__, instance,
cls.MINIMUM, cls.MAXIMUM))
return instance