mstarch: fixing up Fw UTs for refactored types

This commit is contained in:
Michael D Starch 2020-07-20 16:48:50 -07:00
parent 3be5da734b
commit 35879444ef
18 changed files with 306 additions and 542 deletions

View File

@ -1,5 +1,4 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
####
# fprime Python Package:
#
@ -17,12 +16,8 @@
# pip install -e ./Fw/Python
# ```
###
from __future__ import absolute_import
from __future__ import print_function
from setuptools import find_packages
from setuptools import setup
from setuptools import find_packages, setup
# Setup a python package using setup-tools. This is a newer (and more recommended) technology
# then distutils.

View File

@ -2,8 +2,12 @@
Created on May 29, 2020
@author: jishii
"""
from .type_exceptions import ArrayLengthException, TypeMismatchException
from .type_base import ValueType
from .type_exceptions import (
ArrayLengthException,
NotInitializedException,
TypeMismatchException,
)
class ArrayType(ValueType):
@ -47,8 +51,13 @@ class ArrayType(ValueType):
"""
JSONable type
"""
members = {"name": self.__typename, "type": self.__typename, "size": self.__arr_size, "format": self.__arr_format,
"values": [member.to_jsonable() for member in self.val]}
members = {
"name": self.__typename,
"type": self.__typename,
"size": self.__arr_size,
"format": self.__arr_format,
"values": [member.to_jsonable() for member in self.val],
}
return members
def serialize(self):
@ -64,7 +73,7 @@ class ArrayType(ValueType):
item = self.arr_type()
item.deserialize(data, offset + i * item.getSize())
values.append(item)
self.val = valuess
self.val = values
@property
def arr_type(self):
@ -84,4 +93,3 @@ class ArrayType(ValueType):
def getSize(self):
""" Return the size of the array """
return sum([item.getSize() for item in self.val])

View File

@ -2,10 +2,15 @@
Created on Dec 18, 2014
@author: tcanham, reder
"""
from __future__ import absolute_import
import struct
from .type_exceptions import DeserializeException, NotInitializedException, TypeMismatchException
from .type_base import ValueType
from .type_exceptions import (
DeserializeException,
NotInitializedException,
TypeMismatchException,
TypeRangeException
)
class BoolType(ValueType):
@ -13,26 +18,28 @@ class BoolType(ValueType):
Representation of a boolean type that will be stored for F prime. True values are stored as a U8 of 0xFF and False
is stored as a U8 of 0x00.
"""
TRUE = 0xFF
FALSE = 0x00
def validate(self, val):
""" Validate the given class """
if not isinstance(bool, val):
if not isinstance(val, bool):
raise TypeMismatchException(bool, type(val))
def serialize(self):
""" Serialize a boolean value"""
""" Serialize a boolean value """
if self.val is None:
raise NotInitializedException(type(self))
return struct.pack("B", 0xFF if self.val else 0x00)
def deserialize(self, data, offset):
"""
Utilize deserialized decorator here...
"""
""" Deserialize boolean value """
try:
return struct.unpack_from("B", self.TRUE if self.val else self.FALSE)
int_val = struct.unpack_from("B", data, offset)[0]
if int_val not in [self.TRUE, self.FALSE]:
raise TypeRangeException(int_val)
self.val = int_val == self.TRUE
except struct.error:
raise DeserializeException("Not enough bytes to deserialize bool.")

View File

@ -3,8 +3,15 @@ Created on Dec 18, 2014
@author: tcanham, reder
"""
import struct
from .type_exceptions import DeserializeException, EnumMismatchException, NotInitializedException, TypeMismatchException, TypeRangeException
from .type_base import ValueType
from .type_exceptions import (
DeserializeException,
EnumMismatchException,
NotInitializedException,
TypeMismatchException,
TypeRangeException,
)
class EnumType(ValueType):
@ -15,6 +22,7 @@ class EnumType(ValueType):
and current value as a string. The member values will have to be computed
containing code based on C enum rules
"""
def __init__(self, typename="", enum_dict=None, val=None):
"""
Constructor
@ -38,8 +46,8 @@ class EnumType(ValueType):
def validate(self, val):
""" Validate the value passed into the enumeration """
if isinstance(self.enum_dict(), dict):
raise TypeMismatchException(dict, type(val))
if not isinstance(self.enum_dict(), dict):
raise TypeMismatchException(dict, type(self.enum_dict()))
for member in self.keys():
if not isinstance(member, str):
raise TypeMismatchException(str, type(member))
@ -77,8 +85,11 @@ class EnumType(ValueType):
try:
int_val = struct.unpack_from(">i", data, offset)[0]
except:
raise DeserializeException("Could not deserialize enum value. Needed: {} bytes Found: {}"
.format(self.getSize(), len(data[offset:])))
raise DeserializeException(
"Could not deserialize enum value. Needed: {} bytes Found: {}".format(
self.getSize(), len(data[offset:])
)
)
for key, val in self.enum_dict().items():
if int_val == val:
self.val = key
@ -89,5 +100,4 @@ class EnumType(ValueType):
def getSize(self):
""" Calculates the size based on the size of an integer used to store it """
return struct.calcsize('>i');
return struct.calcsize(">i")

View File

@ -1,248 +0,0 @@
# ===============================================================================
# NAME: filepacket.py
#
# DESCRIPTION: A python version of Rob Bocchino's filepacket classes
# AUTHOR: jdperez
# EMAIL: jdperez@jpl.nasa.gov
# DATE CREATED: June 17, 2015
#
# Copyright 2015, California Institute of Technology.
# ALL RIGHTS RESERVED. U.S. Government Sponsorship acknowledged.
# ===============================================================================
#
# Python standard modules
from __future__ import print_function
from __future__ import absolute_import
from abc import ABCMeta, abstractmethod
import six
from .numerical_types import U8Type, U16Type, U32Type
from enum import Enum
# PacketType = Enum('PacketType', 'START DATA END CANCEL NONE')
class PacketType(Enum):
START = 0
DATA = 1
END = 2
CANCEL = 3
NONE = 255
@six.add_metaclass(ABCMeta)
class FilePacketAbc(object):
@abstractmethod
def serialize(self):
pass
@abstractmethod
def deserialize(self):
pass
@abstractmethod
def getBufSize(self):
pass
class Header(FilePacketAbc):
def __init__(self, pktType, seqIdx):
self.__pktType = U8Type(pktType)
self.__seqIdx = U32Type(seqIdx)
def deserialize(self, buf):
ptr = 0
self.__pktType.deserialize(buf, ptr)
ptr += self.__pktType.getSize()
self.__seqIdx.deserialize(buf, ptr)
def serialize(self):
return self.__pktType.serialize() + self.__seqIdx.serialize()
def getBufSize(self):
return self.__pktType.getSize() + self.__seqIdx.getSize()
class StartPacket(FilePacketAbc):
def __init__(self, seqIdx=None, filesize=None, srcPath="", destPath=""):
self.__header = Header(PacketType.START.value, seqIdx)
self.__filesize = U32Type(filesize)
self.__srcPathLen = U8Type(len(srcPath))
self.__srcPath = srcPath
self.__destPathLen = U8Type(len(destPath))
self.__destPath = destPath
def setSrcPath(self, path):
self.__srcPathLen = len(path)
self.__srcPath = path
def setDestPath(self, path):
self.__destPathLen = len(path)
self.__destPath = path
def getDestPath(self):
return self.__destPath
def getSrcPath(self):
return self.__srcPath
def getSrcLen(self):
return self.__srcPathLen
def getDestLen(self):
return self.__destPathLen
def getFileSize(self):
return self.__filesize
def serialize(self):
return (
self.__header.serialize()
+ self.__filesize.serialize()
+ self.__srcPathLen.serialize()
+ self.__srcPath
+ self.__destPathLen.serialize()
+ self.__destPath
)
def deserialize(self, buf):
ptr = 0
self.__header.deserialize(buf)
ptr += self.__header.getBufSize()
self.__filesize.deserialize(buf, ptr)
ptr += self.__filesize.getSize()
self.__srcPathLen.deserialize(buf, ptr)
ptr += self.__srcPathLen.getSize()
self.__srcPath = buf[ptr : self.__srcPathLen.val]
ptr += self.__srcPathLen.val
self.__destPathLen.deserialize(buf, ptr)
ptr += self.__destPathLen.getSize()
self.__destPath = buf[ptr:]
def getBufSize(self):
return (
self.__header.getBufSize()
+ self.__filesize.getSize()
+ self.__srcPathLen.getSize()
+ self.__srcPathLen.val
+ self.__destPathLen.getSize()
+ self.__destPathLen.val
)
class DataPacket:
def __init__(self, seqIdx=None, byteOffset=None, dataSize=None, data=None):
self.__header = Header(PacketType.DATA.value, seqIdx)
self.__byteOffset = U32Type(byteOffset)
self.__dataSize = U16Type(dataSize)
self.__data = data
def serialize(self):
return (
self.__header.serialize()
+ self.__byteOffset.serialize()
+ self.__dataSize.serialize()
+ self.__data
)
def deserialize(self, buf):
ptr = 0
self.__header.deserialize(buf)
ptr += self.__header.getBufSize()
self.__byteOffset.deserialize(buf, ptr)
ptr += self.__byteOffset.getSize()
self.__dataSize.deserialize(buf, ptr)
ptr += self.__dataSize.getSize()
self.__data = buf[ptr:]
def getBufSize(self):
return (
self.__header.getBufSize()
+ self.__byteOffset.getSize()
+ self.__dataSize.getSize()
+ self.__dataSize.val
)
def getHeader(self):
return self.__header
def getByteOffset(self):
return self.__byteOffset
def getDataSize(self):
return self.__dataSize
def getData(self):
return self.__data
class EndPacket:
def __init__(self, seqIdx=None, xSum=None):
self.__header = Header(PacketType.END.value, seqIdx)
self.__xSum = U32Type(xSum)
def setChecksum(self, xSum):
self.__xSum = xSum
def getChecksum(self):
return self.__xSum
def serialize(self):
return self.__header.serialize() + self.__xSum.serialize()
def deserialize(self, buf):
ptr = 0
self.__header.deserialize(buf)
ptr += self.__header.getBufSize()
self.__xSum.deserialize(buf, ptr)
def getBufSize(self):
return self.__header.getBufSize() + self.__xSum.getSize()
class CancelPacket:
def __init__(self, seqIdx=None):
self.__header = Header(PacketType.CANCEL.value, seqIdx)
class FilePacket:
def __init__(self, filePacket):
self.__filePacket = filePacket
def serialize(self):
# return self.__filePacket.header.pktType.serialize() +\
# self.__filePacket.header.seqIdx.serialize() +\
# self.__filePacket.serialize()
return self.__filePacket.serialize()
def deserialize(self, buf):
ptr = 0
"""
self.__filePacket.header.pktType.deserialize(buf, ptr)
ptr += self.__filePacket.header.pktType.getSize()
self.__filePacket.header.seqIdx.deserialize(buf, ptr)
ptr += self.__seqIdx.getSize()
self.__filePacket.deserialize(buf, ptr)
"""
self.__filePacket.deserialize(buf, ptr)
def getBufSize(self):
return self.__filePacket.getBufSize()

View File

@ -6,28 +6,57 @@ that map to stdint.h integer sizes, that is, 8-bit, 16-bit, 32-bit, and 64-bit s
@author mstarch
"""
import re
import abc
import re
import struct
from .type_base import ValueType
from .type_exceptions import DeserializeException, NotInitializedException, TypeMismatchException, TypeRangeException
from .type_exceptions import (
DeserializeException,
NotInitializedException,
TypeMismatchException,
TypeRangeException,
)
BITS_RE = re.compile(r"[IUF](\d\d?)")
class NumericalType(ValueType, abc.ABC):
""" Numerical types that can be serialized using struct and are of some power of 2 byte width """
@classmethod
def __get_bits(cls):
def get_bits(cls):
""" Gets the integer bits of a given type """
match = BITS_RE.match(cls)
assert match, "Type {} does not follow format I#Type nor U#Type required of integer types".format(cls)
match = BITS_RE.match(cls.__name__)
assert (
match
), "Type {} does not follow format I#Type U#Type nor F#Type required of numerical types".format(
cls
)
return int(match.group(1))
@classmethod
def getSize(cls):
""" Gets the size of the integer based on the size specified in the class name """
return int(cls.__get_bits()/8)
return int(cls.get_bits() / 8)
@staticmethod
@abc.abstractmethod
def get_serialize_format():
""" Gets the format serialization string such that the class can be serialized via struct """
def serialize(self):
""" Serializes this type using struct and the val property """
if self.val is None:
raise NotInitializedException(type(self))
return struct.pack(self.get_serialize_format(), self.val)
def deserialize(self, data, offset):
""" Serializes this type using struct and the val property """
try:
self.val = struct.unpack_from(self.get_serialize_format(), data, offset)[0]
except struct.error as err:
raise DeserializeException(str(err))
class IntegerType(NumericalType, abc.ABC):
@ -38,10 +67,10 @@ class IntegerType(NumericalType, abc.ABC):
if not isinstance(val, int):
raise TypeMismatchException(int, type(val))
min_val = 0
max_val = 1 << self.__get_integer_bits()
if self.startswith("I"):
min_val -= int(max_val/2)
max_val -= int(max_val/2)
max_val = 1 << self.get_bits()
if self.__class__.__name__.startswith("I"):
min_val -= int(max_val / 2)
max_val -= int(max_val / 2)
if val < min_val or val >= max_val:
raise TypeRangeException(val)
@ -66,48 +95,61 @@ class I8Type(IntegerType):
class I16Type(IntegerType):
""" Double byte integer type. Represents C shorts """
@staticmethod
def get_serialize_format():
""" Allows serialization using struct """
return ">h"
class I32Type(IntegerType):
""" Four byte integer type. Represents C int32_t, """
@staticmethod
def get_serialize_format():
""" Allows serialization using struct """
return ">i"
class I64Type(IntegerType):
""" Eight byte integer type. Represents C int64_t, """
@staticmethod
def get_serialize_format():
""" Allows serialization using struct """
return ">q"
class U8Type(IntegerType):
""" Single byte integer type. Represents C chars """
@staticmethod
def get_serialize_format():
""" Allows serialization using struct """
return "B"
class U16Type(IntegerType):
""" Double byte integer type. Represents C shorts """
@staticmethod
def get_serialize_format():
""" Allows serialization using struct """
return ">H"
class U32Type(IntegerType):
""" Four byte integer type. Represents C unt32_t, """
@staticmethod
def get_serialize_format():
""" Allows serialization using struct """
return ">I"
class U64Type(IntegerType):
""" Eight byte integer type. Represents C unt64_t, """
@staticmethod
def get_serialize_format():
""" Allows serialization using struct """
@ -116,14 +158,16 @@ class U64Type(IntegerType):
class F32Type(FloatType):
""" Eight byte integer type. Represents C unt64_t, """
@staticmethod
def get_serialize_format():
""" Allows serialization using struct """
return ">f"
class F64Type(IntegerType):
class F64Type(FloatType):
""" Eight byte integer type. Represents C unt64_t, """
@staticmethod
def get_serialize_format():
""" Allows serialization using struct """

View File

@ -4,12 +4,9 @@ Created on Dec 18, 2014
@author: tcanham
"""
import copy
from __future__ import print_function
from __future__ import absolute_import
from .type_exceptions import TypeMismatchException
from .type_exceptions import NotInitializedException
from .type_base import BaseType, ValueType
from .type_exceptions import NotInitializedException, TypeMismatchException
class SerializableType(ValueType):
@ -36,10 +33,14 @@ class SerializableType(ValueType):
self.__typename = typename
# If the member list is defined, stamp in None for any missing descriptions
if mem_list:
new_mem_list = [entry if len(entry) == 4 else (entry[0], entry[1], entry[2], None) for entry in mem_list]
new_mem_list = [
entry if len(entry) == 4 else (entry[0], entry[1], entry[2], None)
for entry in mem_list
]
# Set the member list then set the value
self.mem_list = new_mem_list
self.val = val
if val is not None:
self.val = val
def validate(self, val=None):
""" Validate this object including member list and values """
@ -52,11 +53,11 @@ class SerializableType(ValueType):
# Check each of these members for correct types
if not isinstance(member_name, str):
raise TypeMismatchException(str, type(member_name))
elif isinstance(member_val, BaseType):
elif not isinstance(member_val, BaseType):
raise TypeMismatchException(BaseType, type(member_val))
elif not isinstance(format_string, str):
raise TypeMismatchException(str, type(format_string))
elif not isinstance(description, str):
elif description is not None and not isinstance(description, str):
raise TypeMismatchException(str, type(description))
# When a value is set and is not empty we need to set the member properties
if not val:
@ -64,7 +65,9 @@ class SerializableType(ValueType):
# If a value is supplied then check each value against the member list
for val_member, list_entry in zip(val, self.mem_list):
_, member_list_val, _, _ = list_entry
member_list_val.validate(val_member) # Insure that the the val_member is consistent with the existing member
member_list_val.validate(
val_member
) # Insure that the the val_member is consistent with the existing member
@property
def mem_list(self):
@ -84,7 +87,9 @@ class SerializableType(ValueType):
""" Serializes the members of the serializable """
if self.mem_list is None:
raise NotInitializedException(type(self))
return b"".join([member_val.serialize() for _, member_val, _, _ in self.mem_list])
return b"".join(
[member_val.serialize() for _, member_val, _, _ in self.mem_list]
)
def deserialize(self, data, offset):
""" Deserialize the values of each of the members """
@ -98,16 +103,16 @@ class SerializableType(ValueType):
@property
def val(self):
""" Getter for .val """
return super().val
return ValueType.val.fget(self)
@val.setter
def val(self, val):
""" Setter for .val calls validate internally """
super().val = val
ValueType.val.fset(self, val)
# When a value is set, we need to set the member properties
for val_member, list_entry in zip(val, self.mem_list):
_, member_list_val, _, _ = list_entry
list_entry.val = val_member
member_list_val.val = val_member
def getSize(self):
""" The size of a struct is the size of all the members """

View File

@ -8,11 +8,13 @@ import struct
from fprime.constants import DATA_ENCODING
from .type_exceptions import TypeMismatchException
from .type_exceptions import NotInitializedException
from .type_exceptions import StringSizeException
from .type_exceptions import DeserializeException
from . import type_base
from .type_exceptions import (
DeserializeException,
NotInitializedException,
StringSizeException,
TypeMismatchException,
)
class StringType(type_base.ValueType):
@ -20,6 +22,7 @@ class StringType(type_base.ValueType):
String type representation for F prime. This is a value type that stores a half-word first for representing the
length of this given string.
"""
def __init__(self, val=None, max_string_len=None):
"""
Constructor to build a string
@ -44,7 +47,9 @@ class StringType(type_base.ValueType):
if self.val is None:
raise NotInitializedException(type(self))
# Check string size before serializing
elif self.__max_string_len is not None and len(self.val) > self.__max_string_len:
elif (
self.__max_string_len is not None and len(self.val) > self.__max_string_len
):
raise StringSizeException(len(self.val), self.__max_string_len)
# Pack the string size first then return the encoded data
buff = struct.pack(">H", len(self.val)) + self.val.encode(DATA_ENCODING)
@ -57,13 +62,16 @@ class StringType(type_base.ValueType):
try:
val_size = struct.unpack_from(">H", data, offset)[0]
# Deal with not enough data left in the buffer
if len(data[offset + 2:]) < val_size:
raise DeserializeException("Not enough data to deserialize string data. Needed: {} Left: {}"
.format(val_size, len(data[offset + 2:])))
if len(data[offset + 2 :]) < val_size:
raise DeserializeException(
"Not enough data to deserialize string data. Needed: {} Left: {}".format(
val_size, len(data[offset + 2 :])
)
)
# Deal with a string that is larger than max string
elif self.__max_string_len is not None and val_size > self.__max_string_len:
raise StringSizeException(val_size, self.__max_string_len)
self.val = data[offset + 2:offset + 2 + val_size].decode(DATA_ENCODING)
self.val = data[offset + 2 : offset + 2 + val_size].decode(DATA_ENCODING)
except struct.error:
raise DeserializeException("Not enough bytes to deserialize string length.")
@ -71,4 +79,4 @@ class StringType(type_base.ValueType):
"""
Get the size of this object
"""
return struct.calcsize(">H") + len(self.val)
return struct.calcsize(">H") + len(self.val)

View File

@ -16,19 +16,18 @@ time tags sent with serialized data in the fprime architecture.
@bug No known bugs
"""
from __future__ import print_function
from __future__ import absolute_import
import datetime
from enum import Enum
import math
from enum import Enum
# Custom Python Modules
import fprime.common.models.serialize.numerical_types
from fprime.common.models.serialize.type_exceptions import TypeException
from fprime.common.models.serialize.type_exceptions import TypeRangeException
from fprime.common.models.serialize import type_base
from fprime.common.models.serialize.type_exceptions import (
TypeException,
TypeRangeException,
)
TimeBase = Enum(
"TimeBase",
@ -55,6 +54,10 @@ class TimeType(type_base.BaseType):
Used to parse, store, and create human readable versions of the time tags
included in serialized output from fprime_gds systems
Note: comparisons support comparing to numbers or other instances of TimeType. If comparing to
another TimeType, these comparisons use the provided compare method. See TimeType.compare for
a description of this behavior. See comparison functions at the end.
"""
def __init__(self, time_base=0, time_context=0, seconds=0, useconds=0):
@ -82,8 +85,12 @@ class TimeType(type_base.BaseType):
self._check_time_base(time_base)
self._check_useconds(useconds)
self.__timeBase = fprime.common.models.serialize.numerical_types.U16Type(time_base)
self.__timeContext = fprime.common.models.serialize.numerical_types.U8Type(time_context)
self.__timeBase = fprime.common.models.serialize.numerical_types.U16Type(
time_base
)
self.__timeContext = fprime.common.models.serialize.numerical_types.U8Type(
time_context
)
self.__secs = fprime.common.models.serialize.numerical_types.U32Type(seconds)
self.__usecs = fprime.common.models.serialize.numerical_types.U32Type(useconds)
@ -279,7 +286,6 @@ class TimeType(type_base.BaseType):
dt = self.get_datetime(time_zone)
# If we could convert to a valid datetime, use that, otherwise, format
# TODO use time_zone arg
if dt:
return dt.strftime("%Y-%m-%d %H:%M:%S%z")
else:
@ -329,22 +335,16 @@ class TimeType(type_base.BaseType):
self._check_time_base(time_base)
self._check_useconds(useconds)
self.__timeBase = fprime.common.models.serialize.numerical_types.U16Type(time_base)
self.__timeBase = fprime.common.models.serialize.numerical_types.U16Type(
time_base
)
self.__secs = fprime.common.models.serialize.numerical_types.U32Type(seconds)
self.__usecs = fprime.common.models.serialize.numerical_types.U32Type(useconds)
def __repr__(self):
return "Time"
#The following Python special methods add support for rich comparison of TimeTypes to other
#TimeTypes and numbers.
"""
The following Python special methods add support for rich comparison of TimeTypes to other
TimeTypes and numbers.
Note: comparisons support comparing to numbers or other instances of TimeType. If comparing to
another TimeType, these comparisons use the provided compare method. See TimeType.compare for
a description of this behavior.
"""
def __get_float(self):
def get_float(self):
"""
a helper method that gets the current TimeType as a float where the non-fraction is seconds
and the fraction is microseconds. This enables comparisons with numbers.
@ -352,46 +352,50 @@ class TimeType(type_base.BaseType):
return self.seconds + (self.useconds / 1000000)
def __lt__(self, other):
""" Less than """
if isinstance(other, TimeType):
return self.compare(self, other) < 0
else:
return self.__get_float() < other
return self.get_float() < other
def __le__(self, other):
""" Less than or equal """
if isinstance(other, TimeType):
return self.compare(self, other) <= 0
else:
return self.__get_float() <= other
return self.get_float() <= other
def __eq__(self, other):
""" Equal """
if isinstance(other, TimeType):
return self.compare(self, other) == 0
else:
return self.__get_float() == other
return self.get_float() == other
def __ne__(self, other):
""" Not equal """
if isinstance(other, TimeType):
return self.compare(self, other) != 0
else:
return self.__get_float() != other
return self.get_float() != other
def __gt__(self, other):
""" Greater than """
if isinstance(other, TimeType):
return self.compare(self, other) > 0
else:
return self.__get_float() > other
return self.get_float() > other
def __ge__(self, other):
""" Greater than or equal """
if isinstance(other, TimeType):
return self.compare(self, other) >= 0
else:
return self.__get_float() >= other
return self.get_float() >= other
"""
The following helper methods enable support for arithmetic operations on TimeTypes.
"""
#The following helper methods enable support for arithmetic operations on TimeTypes.
def __set_float(self, num):
def set_float(self, num):
"""
a helper method that takes a float and sets a TimeType's seconds and useconds fields.
Note: This method is private because it is only used by the _get_type_from_float helper to
@ -402,84 +406,89 @@ class TimeType(type_base.BaseType):
self.seconds = int(math.floor(num))
self.useconds = int(round((num - self.seconds) * 1000000))
def __get_type_from_float(self, num):
def get_type_from_float(self, num):
"""
a helper method that returns a new instance of TimeType and sets the seconds and useconds
fields using the given number. The new TimeType's time_base and time_context will be
preserved from the calling object.
"""
tType = TimeType(self.__timeBase.val, self.__timeContext.val)
tType.__set_float(num)
tType.set_float(num)
return tType
"""
The following Python special methods add support for arithmetic operations on TimeTypes.
"""
#The following Python special methods add support for arithmetic operations on TimeTypes.
def __add__(self, other):
""" Addition """
if isinstance(other, TimeType):
other = other.__get_float()
num = self.__get_float() + other
return self.__get_type_from_float(num)
other = other.get_float()
num = self.get_float() + other
return self.get_type_from_float(num)
def __sub__(self, other):
""" Subtraction """
if isinstance(other, TimeType):
other = other.__get_float()
num = self.__get_float() - other
return self.__get_type_from_float(num)
other = other.get_float()
num = self.get_float() - other
return self.get_type_from_float(num)
def __mul__(self, other):
""" Multiplication """
if isinstance(other, TimeType):
other = other.__get_float()
num = self.__get_float() * other
return self.__get_type_from_float(num)
other = other.get_float()
num = self.get_float() * other
return self.get_type_from_float(num)
def __truediv__(self, other):
""" True division """
if isinstance(other, TimeType):
other = other.__get_float()
num = self.__get_float() / other
return self.__get_type_from_float(num)
other = other.get_float()
num = self.get_float() / other
return self.get_type_from_float(num)
def __floordiv__(self, other):
""" Floored divisionv """
if isinstance(other, TimeType):
other = other.__get_float()
num = self.__get_float() // other
return self.__get_type_from_float(num)
other = other.get_float()
num = self.get_float() // other
return self.get_type_from_float(num)
"""
The following Python special methods add support for reflected arithmetic operations on
TimeTypes.
"""
#The following Python special methods add support for reflected arithmetic operations on TimeTypes.
def __radd__(self, other):
""" Reflected addition """
if isinstance(other, TimeType):
other = other.__get_float()
num = other + self.__get_float()
return self.__get_type_from_float(num)
other = other.get_float()
num = other + self.get_float()
return self.get_type_from_float(num)
def __rsub__(self, other):
""" Reflected subtraction """
if isinstance(other, TimeType):
other = other.__get_float()
num = other - self.__get_float()
return self.__get_type_from_float(num)
other = other.get_float()
num = other - self.get_float()
return self.get_type_from_float(num)
def __rmul__(self, other):
""" Reflected multiplication """
if isinstance(other, TimeType):
other = other.__get_float()
num = other * self.__get_float()
return self.__get_type_from_float(num)
other = other.get_float()
num = other * self.get_float()
return self.get_type_from_float(num)
def __rtruediv__(self, other):
""" Reflected division """
if isinstance(other, TimeType):
other = other.__get_float()
num = other / self.__get_float()
return self.__get_type_from_float(num)
other = other.get_float()
num = other / self.get_float()
return self.get_type_from_float(num)
def __rfloordiv__(self, other):
""" Reflected floored division """
if isinstance(other, TimeType):
other = other.__get_float()
num = other // self.__get_float()
return self.__get_type_from_float(num)
other = other.get_float()
num = other // self.get_float()
return self.get_type_from_float(num)
def ser_deser_test(t_base, t_context, secs, usecs, should_err=False):
@ -504,11 +513,11 @@ def ser_deser_test(t_base, t_context, secs, usecs, should_err=False):
try:
val = TimeType(t_base, t_context, secs, usecs)
print(("creating: TimeType(%d, %d, %d, %d)" % (t_base, t_context, secs, usecs)))
print((str(val)))
print("creating: TimeType(%d, %d, %d, %d)" % (t_base, t_context, secs, usecs))
print(str(val))
buff = val.serialize()
print(("Serialized: %s" % repr(buff)))
print("Serialized: %s" % repr(buff))
type_base.showBytes(buff)
val2 = TimeType()

View File

@ -6,9 +6,10 @@ Replaced type base class with decorators
"""
import abc
import struct
from .type_exceptions import AbstractMethodException
from .type_exceptions import DeserializeException
from .type_exceptions import NotInitializedException
from .type_exceptions import (
AbstractMethodException
)
class BaseType(abc.ABC):
@ -21,10 +22,9 @@ class BaseType(abc.ABC):
"""
Serializes the current object type.
"""
pass
@abc.abstractmethod
def deserialize(self, offset):
def deserialize(self, data, offset):
"""
AbstractDeserialize interface
"""
@ -39,14 +39,13 @@ class BaseType(abc.ABC):
def __repr__(self):
""" Produces a string representation of a given type """
return type(self).replace("Type", "")
return self.__class__.__name__.replace("Type", "")
@abc.abstractmethod
def to_jsonable(self):
"""
Converts this type to a JSON serializable object
"""
if hasattr(self, "val"):
return {"value": self.val, "type": str(self)}
raise AbstractMethodException("to_jsonable")
@ -55,6 +54,7 @@ class ValueType(BaseType, abc.ABC):
An abstract base type used to represent a single value. This defines the value property, allowing for setting and
reading from the .val member.
"""
def __init__(self, val=None):
""" Defines the single value """
self.__val = None
@ -62,7 +62,6 @@ class ValueType(BaseType, abc.ABC):
if val is not None:
self.val = val
@abc.abstractmethod
def validate(self, val):
"""
@ -71,7 +70,6 @@ class ValueType(BaseType, abc.ABC):
:param val: value to validate
:raises TypeMismatchException: value has incorrect type, TypeRangeException: val is out of range
"""
pass
@property
def val(self):
@ -84,66 +82,11 @@ class ValueType(BaseType, abc.ABC):
self.validate(val)
self.__val = val
@abc.abstractmethod
@staticmethod
def get_serialize_format():
""" Gets the format serialization string such that the class can be serialized via struct """
def serialize(self):
""" Serializes this type using struct and the val property """
if self.val is None:
raise NotInitializedException(type(self))
return struct.pack(self.get_serialize_format(), self.val)
def deserialize(self, data, offset):
""" Serializes this type using struct and the val property """
try:
self.val = struct.unpack_from(self.get_serialize_format(), data, offset)[0]
except struct.error as err:
raise DeserializeException(str(err))
#
#
def deserialize(Class):
"""
Decorator adds common deserialize method
"""
setattr(Class, "__val", None)
def _deserialize(self, tformat, data, offset):
if offset > (len(data) - len(data[offset:])):
raise DeserializeException(
"Not enough data to deserialize! Needed: %d Left: %d"
% (self.getSize(), offset)
)
self.val = struct.unpack_from(tformat, data, offset)[0]
setattr(Class, "_deserialize", _deserialize)
return Class
#
#
def serialize(Class):
"""
Decorator adds common serialize method
"""
setattr(Class, "__val", None)
def _serialize(self, tformat, arg=None):
if self.val == None:
raise NotInitializedException(type(self))
if arg == None:
return struct.pack(tformat, self.val)
else:
return struct.pack(tformat, arg)
setattr(Class, "_serialize", _serialize)
return Class
def to_jsonable(self):
"""
Converts this type to a JSON serializable object
"""
return {"value": self.val, "type": str(self)}
#

View File

@ -4,14 +4,13 @@ Created on Dec 18, 2014
@author: tcanham
"""
from __future__ import print_function
from __future__ import absolute_import
# Exception classes for all types
class TypeException(Exception):
def __init__(self, val):
super().__init__(val)
self.except_msg = val
def getMsg(self):
@ -20,33 +19,31 @@ class TypeException(Exception):
class AbstractMethodException(TypeException):
def __init__(self, val):
super(AbstractMethodException, self).__init__(
"%s must be implemented since it is abstract!" % str(val)
)
super().__init__("%s must be implemented since it is abstract!" % str(val))
class TypeRangeException(TypeException):
def __init__(self, val):
super(TypeRangeException, self).__init__("Value %s out of range!" % str(val))
super().__init__("Value %s out of range!" % str(val))
class StringSizeException(TypeException):
def __init__(self, size, max_size):
super(StringSizeException, self).__init__(
"String size %s is greater than %s!" % (str(size), str(max_size))
super().__init__(
"String size {} is greater than {}!".format(str(size), str(max_size))
)
class TypeMismatchException(TypeException):
def __init__(self, expected_type, actual_type):
super(TypeMismatchException, self).__init__(
"Type %s expected, type %s found!" % (expected_type, actual_type)
super().__init__(
"Type {} expected, type {} found!".format(expected_type, actual_type)
)
class ArrayLengthException(TypeException):
def __init__(self, arr_type, expected_len, actual_len):
super(ArrayLengthException, self).__init__(
super().__init__(
"Array type %s is of length %s, actual length %s found!"
% (arr_type, expected_len, actual_len)
)
@ -54,38 +51,35 @@ class ArrayLengthException(TypeException):
class EnumMismatchException(TypeException):
def __init__(self, enum, bad_member):
super(EnumMismatchException, self).__init__(
"Invalid enum member %s set in %s enum!" % (bad_member, enum)
super().__init__(
"Invalid enum member {} set in {} enum!".format(bad_member, enum)
)
class DeserializeException(TypeException):
def __init__(self, message):
super(DeserializeException, self).__init__(message)
pass
class ArgNotFoundException(TypeException):
def __init__(self, message):
super(ArgNotFoundException, self).__init__("Arg %s not found!" % message)
super().__init__("Arg %s not found!" % message)
class NotInitializedException(TypeException):
def __init__(self, message):
super(NotInitializedException, self).__init__(
"Instance %s not initialized!" % message
)
super().__init__("Instance %s not initialized!" % message)
class NotOverridenException(TypeException):
def __init__(self, message):
super(NotOverridenException, self).__init__(
super().__init__(
"Required base class method not overrwritten in type %s!" % message
)
class ArgLengthMismatchException(TypeException):
def __init__(self, arg_length_actual, arg_length_given):
super(ArgLengthMismatchException, self).__init__(
super().__init__(
"%d args provided, but command expects %d args!"
% (arg_length_given, arg_length_actual)
)
@ -93,7 +87,7 @@ class ArgLengthMismatchException(TypeException):
class CompoundTypeLengthMismatchException(TypeException):
def __init__(self, field_length_actual, field_length_given):
super(CompoundTypeLengthMismatchException, self).__init__(
super().__init__(
"%d fields provided, but compound type expects %d fields!"
% (field_length_given, field_length_actual)
)

View File

@ -7,28 +7,26 @@ receiver of these delegated functions.
@author mstarch
"""
import io
import os
import re
import pty
import sys
import time
import shutil
import tempfile
import subprocess
import itertools
import functools
import collections
import selectors
# Get a cache directory for building CMakeList file, if need and remove at exit
import atexit
import six
import collections
import functools
import itertools
import os
import pty
import re
import selectors
import shutil
import subprocess
import sys
import tempfile
import time
import fprime.fbuild
import fprime.fbuild.settings
class CMakeBuildCache(object):
class CMakeBuildCache:
"""
Builds CMake deployment for the purposes of inspecting that build. This exists because generating a build on every
call take a long time. This cache will hold the results to prevent recalculation.
@ -64,7 +62,7 @@ class CMakeBuildCache(object):
return self.tempdir
class CMakeHandler(object):
class CMakeHandler:
"""
CMake handler interacts with an F prime CMake-based system. This will help us interact with CMake in refined ways.
"""
@ -156,11 +154,11 @@ class CMakeHandler(object):
run_args + fleshed_args, write_override=True, environment=environment
)
def find_hashed_file(self, build_dir, hash):
def find_hashed_file(self, build_dir, hash_value):
"""
Find a file from a hash
:param build_dir: build directory to search
:param hash: hash number
:param hash_value: hash number
:return: filename
"""
hashes_file = os.path.join(build_dir, "hashes.txt")
@ -168,9 +166,9 @@ class CMakeHandler(object):
raise CMakeException(
"Failed to find {}, was the build generated.".format(hashes_file)
)
with open(hashes_file, "r") as file_handle:
with open(hashes_file) as file_handle:
lines = filter(
lambda line: "{:x}".format(hash) in line, file_handle.readlines()
lambda line: "{:x}".format(hash_value) in line, file_handle.readlines()
)
return list(lines)
@ -289,7 +287,7 @@ class CMakeHandler(object):
:param cmake_dir: a cmake directory (project or build) to used. default: None, use existing temp cached build.
:return: list of values, or Nones
"""
if isinstance(fields, six.string_types):
if isinstance(fields, str):
fields = [fields]
# Setup the build_dir if it can be detected. Without a cache or specified value, we can crash
build_dir = self._build_directory_from_cmake_dir(cmake_dir)
@ -407,7 +405,7 @@ class CMakeHandler(object):
if not os.path.isfile(cmake_file):
raise CMakeProjectException(source_dir, "No CMakeLists.txt is defined")
# Test the cmake_file for project(
with open(cmake_file, "r") as file_handle:
with open(cmake_file) as file_handle:
project_lines = list(
filter(lambda line: "project(" in line, file_handle.readlines())
)
@ -491,10 +489,7 @@ class CMakeHandler(object):
os.close(pty_out_w)
os.close(pty_err_w)
ret, stdout, stderr = self._communicate(
proc,
io.open(pty_out_r, mode="rb"),
io.open(pty_err_r, mode="rb"),
print_output,
proc, open(pty_out_r, mode="rb"), open(pty_err_r, mode="rb"), print_output,
)
# Raising an exception when the return code is non-zero allows us to handle the exception internally if it is
# needed. Thus we do not just exit.
@ -540,7 +535,7 @@ class CMakeHandler(object):
line = key.fileobj.readline().decode().replace("\r\n", "\n")
# Some systems (like running inside Docker) raise an io error instead of returning "" when the device
# is ended. Not sure why this is, but the effect is the same, on IOError assume end-of-input
except IOError:
except OSError:
line = ""
appendable.append(line)
# Streams are EOF when the line returned is empty. Once this occurs, we are responsible for closing the
@ -568,7 +563,7 @@ class CMakeInconsistencyException(CMakeException):
def __init__(self, project_cmake, build_dir):
""" Force an appropriate message """
super(CMakeInconsistencyException, self).__init__(
super().__init__(
"{} is inconsistent with build {}. Regenerate the build".format(
project_cmake, build_dir
)
@ -580,9 +575,7 @@ class CMakeOrphanException(CMakeException):
def __init__(self, file_dir):
""" Force an appropriate message """
super(CMakeOrphanException, self).__init__(
"{} is outside the F prime project".format(file_dir)
)
super().__init__("{} is outside the F prime project".format(file_dir))
class CMakeProjectException(CMakeException):
@ -590,7 +583,7 @@ class CMakeProjectException(CMakeException):
def __init__(self, project_dir, error):
""" Force an appropriate message """
super(CMakeProjectException, self).__init__(
super().__init__(
"{} is an invalid F prime deployment. {}".format(project_dir, error)
)
@ -600,7 +593,7 @@ class CMakeInvalidBuildException(CMakeException):
def __init__(self, build_dir):
""" Force an appropriate message """
super(CMakeInvalidBuildException, self).__init__(
super().__init__(
"{} is not a CMake build directory. Please setup using 'fprime-util generate'".format(
build_dir
)
@ -612,7 +605,7 @@ class CMakeExecutionException(CMakeException):
def __init__(self, message, stderr, printed):
""" The error data should be stored """
super(CMakeExecutionException, self).__init__(message)
super().__init__(message)
self.stderr = stderr
self.need = not printed
@ -636,6 +629,4 @@ class CMakeNoSuchTargetException(CMakeException):
def __init__(self, build_dir, target):
""" Better messaging for this exception """
super(CMakeNoSuchTargetException, self).__init__(
"{} does not support target {}".format(build_dir, target)
)
super().__init__("{} does not support target {}".format(build_dir, target))

View File

@ -6,8 +6,8 @@ settings from the settings.default file that is part of the F prime deployment d
@author mstarch
"""
import os
import configparser # Written after PY2 eol
import os
class IniSettings:
@ -104,7 +104,7 @@ class IniSettings:
config_dir = IniSettings.read_safe_path(
confparse, "fprime", "config_directory", settings_file
)
config_dir = None if not config_dir else ac_consts[0]
config_dir = None if not config_dir else config_dir[0]
# Read separate environment file if necessary
env_file = IniSettings.read_safe_path(
@ -158,10 +158,6 @@ class IniSettings:
class FprimeLocationUnknownException(Exception):
""" Fprime location could not be determined """
pass
class FprimeSettingsException(Exception):
""" An exception for handling F prime settings misconfiguration """
pass

View File

@ -5,6 +5,7 @@ This acts as the main entry point for the fprime.util module. This allows users
This will include the build_helper scripts and run them.
"""
import sys
import fprime.util.build_helper

View File

@ -13,15 +13,13 @@ are supported herein:
@author mstarch
"""
from __future__ import print_function
import os
import sys
import argparse
import os
import shutil
import sys
import fprime.fbuild
UT_SUFFIX = "-ut"
ACTION_MAP = {
"generate": {
@ -248,6 +246,7 @@ def parse_args(args):
cmake_args, make_args = validate(parsed)
return parsed, cmake_args, make_args, automatic_build_dir
def confirm():
"""
Confirms the removal of the file with a yes or no input.
@ -255,18 +254,12 @@ def confirm():
"""
# Loop "forever"
while True:
# Py 2/3
prompter = input
try:
prompter = raw_input
except NameError:
pass
confirm = prompter("Purge this directory (yes/no)?")
if confirm.lower() in ["y", "yes"]:
confirm_input = input("Purge this directory (yes/no)?")
if confirm_input.lower() in ["y", "yes"]:
return True
elif confirm.lower() in ["n", "no"]:
elif confirm_input.lower() in ["n", "no"]:
return False
print("{} is invalid. Please use 'yes' or 'no'".format(confirm))
print("{} is invalid. Please use 'yes' or 'no'".format(confirm_input))
def purge_functionality(build_dir, force=False):
@ -323,7 +316,7 @@ def utility_entry(args=None):
.format("automatic" if automatic_build_dir else "specified", parsed.build_dir + UT_SUFFIX))
fprime.fbuild.builder().generate_build(parsed.path, parsed.build_dir + UT_SUFFIX, cmake_args)
except Exception as exc:
print("[INFO] Error detected, automatically cleaning up failed-generation")
print("[INFO] Error detected, automatically cleaning up failed-generation. Error: {}".format(exc))
purge_functionality(parsed.build_dir, True)
raise
else:

View File

@ -5,20 +5,26 @@ Created on Jun 25, 2020
@author: hpaulson
"""
from __future__ import absolute_import
import pytest
from fprime.common.models.serialize import type_base
from fprime.common.models.serialize.bool_type import BoolType
from fprime.common.models.serialize.enum_type import EnumType
from fprime.common.models.serialize.numerical_types import I8Type, I16Type, I32Type, I64Type, U8Type, U16Type, U32Type, \
U64Type, F32Type, F64Type
from fprime.common.models.serialize.numerical_types import (
F32Type,
F64Type,
I8Type,
I16Type,
I32Type,
I64Type,
U8Type,
U16Type,
U32Type,
U64Type,
)
from fprime.common.models.serialize.serializable_type import SerializableType
from fprime.common.models.serialize.string_type import StringType
from fprime.common.models.serialize.time_type import TimeType
from fprime.common.models.serialize.time_type import TimeBase
from fprime.common.models.serialize.time_type import ser_deser_test
from fprime.common.models.serialize.time_type import TimeBase, TimeType, ser_deser_test
def test_bool_type():

View File

@ -2,11 +2,12 @@ import os
import sys
import unittest
from fprime.common.models.serialize.time_type import TimeType
filename = os.path.dirname(__file__)
fprime_path = os.path.join(filename, "../../../../../src")
sys.path.insert(0, fprime_path)
from fprime.common.models.serialize.time_type import TimeType
class TimeTypeTestCases(unittest.TestCase):
def setUp(self):

View File

@ -7,10 +7,11 @@ that they function as expected.
@author mstarch
"""
import os
import pytest
import shutil
import tempfile
import pytest
import fprime.fbuild