mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-09-06 09:47:38 +02:00
Move context, tidy up object model and add memory to test_rig.
This commit is contained in:
+27
-10
@@ -5,34 +5,51 @@ Created on 10 Mar 2013
|
||||
'''
|
||||
|
||||
import pdb
|
||||
from volatility.framework import xp_sp2_x86_vtypes, context, symbols
|
||||
from volatility import framework
|
||||
from volatility.framework import xp_sp2_x86_vtypes, layers
|
||||
from volatility.framework.symbols import vtypes, native
|
||||
|
||||
def main():
|
||||
def test_symbols():
|
||||
nativelst = native.x86NativeTable
|
||||
|
||||
virtual_types = xp_sp2_x86_vtypes.ntkrnlmp_types
|
||||
|
||||
ntkrnlmp = vtypes.VTypeSymbolTable('ntkrnlmp', virtual_types, nativelst)
|
||||
|
||||
ctx = context.Context(symbols.SymbolSpace(nativelst))
|
||||
ctx.symbol_space.append(nativelst)
|
||||
ctx = framework.Context(nativelst)
|
||||
# ctx.symbol_space.append(nativelst)
|
||||
ctx.symbol_space.append(ntkrnlmp)
|
||||
print("Symbols,", nativelst.symbols)
|
||||
|
||||
for _ in []: # 1, 2]:
|
||||
for i in list(ntkrnlmp.symbols):
|
||||
symbol = ctx.symbol_space.resolve('ntkrnlmp!' + i)
|
||||
print(symbol.symbol_name, symbol, symbol.size)
|
||||
# objthing = symbol(context, layer_name = '', offset = 0)
|
||||
for i in list(ntkrnlmp.symbols):
|
||||
symbol = ctx.symbol_space.resolve('ntkrnlmp!' + i)
|
||||
print(symbol.symbol_name, symbol, symbol.size)
|
||||
_objthing = symbol(ctx, layer_name = '', offset = 0)
|
||||
symbol = ctx.symbol_space.resolve('ntkrnlmp!_EPROCESS')
|
||||
|
||||
def test_memory():
|
||||
nativelst = native.x86NativeTable
|
||||
virtual_types = xp_sp2_x86_vtypes.ntkrnlmp_types
|
||||
virtual_types['TEST_SYMBOL'] = [0x6,
|
||||
{'test1': [0x0, ['unsigned int']],
|
||||
'test2': [0x4, ['unsigned short']]
|
||||
}]
|
||||
ntkrnlmp = vtypes.VTypeSymbolTable('ntkrnlmp', virtual_types, nativelst)
|
||||
|
||||
ctx = framework.Context(nativelst)
|
||||
ctx.symbol_space.append(ntkrnlmp)
|
||||
|
||||
base = layers.BufferDataLayer(ctx, 'data', buffer = b"\x01\x00\x00\x00\x02\x00")
|
||||
ctx.memory.add_layer(base)
|
||||
val = ctx.object('ntkrnlmp!TEST_SYMBOL', 'data', 0)
|
||||
print(hex(val.test1), val.test1.size)
|
||||
|
||||
if __name__ == '__main__':
|
||||
# import timeit
|
||||
# print(timeit.Timer(main).timeit(10))
|
||||
try:
|
||||
main()
|
||||
# test_symbols()
|
||||
test_memory()
|
||||
except Exception as e:
|
||||
print(repr(e))
|
||||
pdb.post_mortem()
|
||||
|
||||
@@ -28,5 +28,44 @@ def require_version(*args):
|
||||
if args[1] > version()[1]:
|
||||
raise Exception("Framework version " + ".".join([str(x) for x in version()[0:1]]) + " is an older revision than the required version " + ".".join([str(x) for x in args[0:2]]))
|
||||
|
||||
from volatility.framework import interfaces, symbols, layers
|
||||
|
||||
class Context(interfaces.ContextInterface):
|
||||
"""Maintains the context within which to construct objects"""
|
||||
|
||||
def __init__(self, natives):
|
||||
super(Context, self).__init__()
|
||||
self._symbol_space = symbols.SymbolSpace(natives)
|
||||
self._memory = layers.Memory()
|
||||
|
||||
### Symbol Space Functions
|
||||
|
||||
@property
|
||||
def symbol_space(self):
|
||||
return self._symbol_space
|
||||
|
||||
@property
|
||||
def memory(self):
|
||||
return self._memory
|
||||
|
||||
### Address Space Functions
|
||||
|
||||
def add_translation_layer(self, layer):
|
||||
"""Adds a named translation layer to the context"""
|
||||
self._memory.add_layer(layer)
|
||||
|
||||
### Object Factory Functions
|
||||
|
||||
def object(self, symbol, layer_name, offset):
|
||||
"""Object factory, takes a context, symbol, offset and optional layername
|
||||
|
||||
Looks up the layername in the context, finds the object template based on the symbol,
|
||||
and constructs an object using the object template on the layer at the offset.
|
||||
|
||||
Returns a fully constructed object
|
||||
"""
|
||||
object_template = self._symbol_space.resolve(symbol)
|
||||
return object_template(self, layer_name = layer_name, offset = offset)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
'''
|
||||
Created on 12 Feb 2013
|
||||
|
||||
@author: mike
|
||||
'''
|
||||
|
||||
from volatility.framework import interfaces
|
||||
|
||||
class Context(interfaces.ContextInterface):
|
||||
"""Maintains the context within which to construct objects"""
|
||||
|
||||
def __init__(self, symbol_space):
|
||||
super(Context, self).__init__()
|
||||
self._symbol_space = symbol_space
|
||||
self._layers = {}
|
||||
|
||||
### Symbol Space Functions
|
||||
|
||||
@property
|
||||
def symbol_space(self):
|
||||
return self._symbol_space
|
||||
|
||||
### Address Space Functions
|
||||
|
||||
def add_translation_layer(self, layer, name = None):
|
||||
"""Adds a named translation layer to the context"""
|
||||
self._layers[name] = layer
|
||||
|
||||
### Object Factory Functions
|
||||
|
||||
def object(self, symbol, layer_name, offset):
|
||||
"""Object factory, takes a context, symbol, offset and optional layername
|
||||
|
||||
Looks up the layername in the context, finds the object template based on the symbol,
|
||||
and constructs an object using the object template on the layer at the offset.
|
||||
|
||||
Returns a fully constructed object
|
||||
"""
|
||||
object_template = self._symbol_space.resolve(symbol)
|
||||
return object_template(self, layer_name = layer_name, offset = offset)
|
||||
|
||||
@@ -15,3 +15,6 @@ class InvalidAddressException(VolatilityException):
|
||||
|
||||
class SymbolSpaceError(VolatilityException):
|
||||
"""Thrown when an error occurs dealing with Symbols and Symbolspaces"""
|
||||
|
||||
class LayerException(VolatilityException):
|
||||
"""Thrown when an error occurs dealing with memory and layers"""
|
||||
|
||||
@@ -19,10 +19,15 @@ class ContextInterface(object):
|
||||
def symbol_space(self):
|
||||
"""Returns the symbol_space for the context"""
|
||||
|
||||
### Address Space Functions
|
||||
### Memory Functions
|
||||
|
||||
def add_translation_layer(self, layer, name = None):
|
||||
"""Adds a named translation layer to the context"""
|
||||
@property
|
||||
def memory(self):
|
||||
"""Returns the memory object for the context"""
|
||||
|
||||
def add_layer(self, layer):
|
||||
"""Adds a named translation layer to the context memory"""
|
||||
self.memory.add_layer(layer)
|
||||
|
||||
### Object Factory Functions
|
||||
|
||||
@@ -37,19 +42,19 @@ class ContextInterface(object):
|
||||
|
||||
class ObjectInterface(validity.ValidityRoutines):
|
||||
""" A base object required to be the ancestor of every object used in volatility """
|
||||
def __init__(self, context, layer_name, offset, symbol_name, size, **kwargs):
|
||||
def __init__(self, context, layer_name, offset, symbol_name, size, parent = None, **kwargs):
|
||||
# Since objects are likely to be instantiated often,
|
||||
# we're only checking that a context is a context
|
||||
# Everything else may be wrong, but that will get caught later on
|
||||
self.type_check(context, ContextInterface)
|
||||
self._context = context
|
||||
self._context = self.type_check(context, ContextInterface)
|
||||
self._parent = None if not parent else self.type_check(parent, ObjectInterface)
|
||||
self._offset = offset
|
||||
self._layer_name = layer_name
|
||||
self._symbol_name = symbol_name
|
||||
self._size = size
|
||||
|
||||
def cast(self, new_symbol_name):
|
||||
object_template = self._context.resolve(new_symbol_name)
|
||||
object_template = self._context.symbol_space.resolve(new_symbol_name)
|
||||
return object_template(context = self._context, layer_name = self._layer_name, offset = self._offset)
|
||||
|
||||
class Template(object):
|
||||
|
||||
@@ -50,3 +50,6 @@ class TranslationLayerInterface(DataLayerInterface):
|
||||
|
||||
def mapping(self, offset, length):
|
||||
"""Returns a list of (offset, length, layer) mappings"""
|
||||
|
||||
def dependencies(self):
|
||||
"""Returns a list of layer names that this layer translates onto"""
|
||||
|
||||
@@ -4,13 +4,60 @@ Created on 4 May 2013
|
||||
@author: mike
|
||||
'''
|
||||
|
||||
from volatility.framework import validity, exceptions
|
||||
from volatility.framework.interfaces import layers
|
||||
|
||||
class Memory(validity.ValidityRoutines):
|
||||
"""Container for multiple layers of data"""
|
||||
|
||||
def __init__(self):
|
||||
self._layers = {}
|
||||
|
||||
def read(self, layer, offset, length, pad = False):
|
||||
"""Reads from a particular layer at offset for length bytes"""
|
||||
return self[layer].read(offset, length, pad)
|
||||
|
||||
def write(self, layer, offset, data):
|
||||
"""Writes to a particular layer at offset for length bytes"""
|
||||
self[layer].write(offset, data)
|
||||
|
||||
def add_layer(self, layer):
|
||||
"""Adds a layer to memory model
|
||||
|
||||
This will throw an exception if the required dependencies are not met
|
||||
"""
|
||||
self.type_check(layer, layers.DataLayerInterface)
|
||||
if isinstance(layer, layers.TranslationLayerInterface):
|
||||
if layer.name in self._layers:
|
||||
raise exceptions.LayerException("")
|
||||
missing_list = [sublayer for sublayer in layer.dependencies if sublayer not in self._layers]
|
||||
if missing_list:
|
||||
raise exceptions.LayerException("Layer " + layer.name + " has unmet dependencies of " + ", ".join(missing_list))
|
||||
self._layers[layer.name] = layer
|
||||
|
||||
def del_layer(self, name):
|
||||
"""Removes the layer called name
|
||||
|
||||
This will throw an exception if other layers depend upon this layer
|
||||
"""
|
||||
for layer in self._layers:
|
||||
depend_list = [superlayer for superlayer in self._layers if name in superlayer.dependencies]
|
||||
if depend_list:
|
||||
raise exceptions.LayerException("Layer " + layer.name + " is depended upon by " + ", ".join(depend_list))
|
||||
del self._layers[name]
|
||||
|
||||
def __getitem__(self, name):
|
||||
"""Returns the layer of specified name"""
|
||||
return self._layers[name]
|
||||
|
||||
def check_cycles(self):
|
||||
"""Runs through the available layers and identifies if there are cycles in the DAG"""
|
||||
|
||||
class BufferDataLayer(layers.DataLayerInterface):
|
||||
"""A DataLayer class backed by a buffer in memory, designed for testing and swift data access"""
|
||||
|
||||
def __init__(self, name = None, buffer = None):
|
||||
super(BufferDataLayer, self).__init__(name)
|
||||
def __init__(self, context, name, buffer):
|
||||
super(BufferDataLayer, self).__init__(context, name)
|
||||
self._buffer = self.type_check(buffer, bytes)
|
||||
|
||||
@property
|
||||
|
||||
@@ -28,15 +28,19 @@ class Void(interfaces.ObjectInterface):
|
||||
class PrimitiveObject(interfaces.ObjectInterface):
|
||||
"""PrimitiveObject is an interface for any objects that should simulate a Python primitive"""
|
||||
|
||||
def __init__(self, context, layer_name, offset, symbol_name, struct_format = '<I', **kwargs):
|
||||
super(PrimitiveObject, self).__init__(context = context, layer_name = layer_name, offset = offset, symbol_name = symbol_name)
|
||||
def __init__(self, context, layer_name, offset, symbol_name, size = None, parent = None, struct_format = '<I', **kwargs):
|
||||
super(PrimitiveObject, self).__init__(context = context,
|
||||
layer_name = layer_name,
|
||||
offset = offset,
|
||||
symbol_name = symbol_name,
|
||||
size = size,
|
||||
parent = parent)
|
||||
self._struct_format = struct_format
|
||||
|
||||
@classmethod
|
||||
def _struct_value(cls, struct_format, context, layer_name, offset, symbol_name):
|
||||
aspace = context.get_address_space(layer_name)
|
||||
length = struct.calcsize(struct_format)
|
||||
data = aspace.read(offset, length)
|
||||
data = context.memory.read(layer_name, offset, length)
|
||||
(value,) = struct.unpack(struct_format, data)
|
||||
return value
|
||||
|
||||
@@ -58,19 +62,19 @@ class Integer(PrimitiveObject, int):
|
||||
"""Primitive Object that handles standard numeric types"""
|
||||
|
||||
def __new__(cls, context, layer_name, offset, symbol_name, struct_format, **kwargs):
|
||||
return cls._struct_value(struct_format, context, layer_name, offset, symbol_name)
|
||||
return int.__new__(cls, cls._struct_value(struct_format, context, layer_name, offset, symbol_name))
|
||||
|
||||
class Float(PrimitiveObject, float):
|
||||
"""Primitive Object that handles double or floating point numbers"""
|
||||
|
||||
def __new__(cls, context, layer_name, offset, symbol_name, struct_format, **kwargs):
|
||||
return cls._struct_value(struct_format, context, layer_name, offset, symbol_name)
|
||||
return float.__new__(cls, cls._struct_value(struct_format, context, layer_name, offset, symbol_name))
|
||||
|
||||
class Bytes(PrimitiveObject, bytes):
|
||||
"""Primitive Object that handles specific series of bytes"""
|
||||
|
||||
def __new__(cls, context, layer_name, offset, symbol_name, length = 1):
|
||||
return cls._struct_value(str(length) + "s", context, layer_name, offset, symbol_name)
|
||||
def __new__(cls, context, layer_name, offset, symbol_name, length = 1, **kwargs):
|
||||
return bytes.__new__(cls, cls._struct_value(str(length) + "s", context, layer_name, offset, symbol_name))
|
||||
|
||||
class String(PrimitiveObject, str):
|
||||
"""Primitive Object that handles string values
|
||||
@@ -78,18 +82,20 @@ class String(PrimitiveObject, str):
|
||||
length: specifies the maximum possible length that the string could hold in memory
|
||||
"""
|
||||
|
||||
def __new__(cls, context, layer_name, offset, symbol_name, length = 1):
|
||||
return cls._struct_value(str(length) + "s", context, layer_name, offset, symbol_name)
|
||||
def __new__(cls, context, layer_name, offset, symbol_name, length = 1, **kwargs):
|
||||
return str.__new__(cls, cls._struct_value(str(length) + "s", context, layer_name, offset, symbol_name))
|
||||
|
||||
class Pointer(Integer):
|
||||
"""Pointer which points to another object"""
|
||||
def __init__(self, context, layer_name, offset, symbol_name, struct_format = None, target = None):
|
||||
def __init__(self, context, layer_name, offset, symbol_name, size = None, parent = None, struct_format = None, target = None, **kwargs):
|
||||
if not isinstance(target, templates.ObjectTemplate):
|
||||
raise TypeError("Pointer targets must be an ObjectTemplate")
|
||||
super(Pointer, self).__init__(context,
|
||||
layer_name = layer_name,
|
||||
offset = offset,
|
||||
symbol_name = symbol_name,
|
||||
size = size,
|
||||
parent = parent,
|
||||
struct_format = struct_format)
|
||||
self._target = target
|
||||
|
||||
@@ -97,7 +103,11 @@ class Pointer(Integer):
|
||||
"""Dereferences the pointer"""
|
||||
# Cache the target
|
||||
if isinstance(self._target, templates.ObjectTemplate):
|
||||
self._target = self.target(context = self._context, layer_name = self._layer_name, offset = self, self = self._target.size, parent = self)
|
||||
self._target = self.target(context = self._context,
|
||||
layer_name = self._layer_name,
|
||||
offset = self,
|
||||
self = self._target.size,
|
||||
parent = self)
|
||||
return self._target
|
||||
|
||||
def __getattr__(self, attr):
|
||||
@@ -120,8 +130,13 @@ class Pointer(Integer):
|
||||
|
||||
class BitField(PrimitiveObject, int):
|
||||
"""Object containing a field which is made up of bits rather than whole bytes"""
|
||||
def __new__(cls, context, layer_name, offset, symbol_name, target = None, start_bit = 0, end_bit = 0):
|
||||
value = target(context = context, layer_name = layer_name, offset = offset, symbol_name = symbol_name)
|
||||
def __new__(cls, context, layer_name, offset, symbol_name, size = None, parent = None, target = None, start_bit = 0, end_bit = 0, **kwargs):
|
||||
value = target(context = context,
|
||||
layer_name = layer_name,
|
||||
offset = offset,
|
||||
symbol_name = symbol_name,
|
||||
size = size,
|
||||
parent = parent)
|
||||
return (value >> start_bit) & ((1 << end_bit) - 1)
|
||||
|
||||
@classmethod
|
||||
@@ -140,10 +155,15 @@ class Enumeration(interfaces.ObjectInterface):
|
||||
|
||||
class Array(interfaces.ObjectInterface, collections.Sequence):
|
||||
"""Object which can contain a fixed number of an object type"""
|
||||
def __init__(self, context, layer_name, offset, symbol_name, size = None, count = 0, target = None):
|
||||
def __init__(self, context, layer_name, offset, symbol_name, size = None, parent = None, count = 0, target = None, **kwargs):
|
||||
if not isinstance(target, templates.ObjectTemplate):
|
||||
raise TypeError("Array target must be an ObjectTemplate")
|
||||
super(Array, self).__init__(context = context, layer_name = layer_name, offset = offset, symbol_name = symbol_name, size = size)
|
||||
super(Array, self).__init__(context = context,
|
||||
layer_name = layer_name,
|
||||
offset = offset,
|
||||
symbol_name = symbol_name,
|
||||
size = size,
|
||||
parent = parent)
|
||||
self._count = count
|
||||
self._target = target
|
||||
|
||||
@@ -179,12 +199,13 @@ class Array(interfaces.ObjectInterface, collections.Sequence):
|
||||
class Struct(interfaces.ObjectInterface):
|
||||
"""Object which can contain members that are other objects"""
|
||||
|
||||
def __init__(self, context, layer_name, offset, symbol_name, size = None, members = None):
|
||||
def __init__(self, context, layer_name, offset, symbol_name, size = None, members = None, parent = None, **kwargs):
|
||||
super(Struct, self).__init__(context = context,
|
||||
layer_name = layer_name,
|
||||
offset = offset,
|
||||
symbol_name = symbol_name,
|
||||
size = size)
|
||||
size = size,
|
||||
parent = parent)
|
||||
self.check_members(members)
|
||||
self._members = members
|
||||
self._concrete_members = {}
|
||||
|
||||
@@ -4,9 +4,9 @@ Created on 1 Mar 2013
|
||||
@author: mike
|
||||
'''
|
||||
|
||||
from volatility.framework import interfaces
|
||||
from volatility.framework import interfaces, validity
|
||||
|
||||
class ObjectTemplate(interfaces.Template):
|
||||
class ObjectTemplate(interfaces.Template, validity.ValidityRoutines):
|
||||
"""Factory class that produces objects that adhere to the Object interface on demand
|
||||
|
||||
This is effectively a method of currying, but adds more structure to avoid abuse.
|
||||
@@ -17,9 +17,7 @@ class ObjectTemplate(interfaces.Template):
|
||||
"""
|
||||
def __init__(self, object_class = None, symbol_name = None, **kwargs):
|
||||
super(ObjectTemplate, self).__init__(symbol_name = symbol_name, **kwargs)
|
||||
if not issubclass(object_class, interfaces.ObjectInterface):
|
||||
raise TypeError("ObjectTemplate object_class must inherit from ObjectInterface")
|
||||
self.object_class = object_class
|
||||
self.object_class = self.class_check(object_class, interfaces.ObjectInterface)
|
||||
|
||||
@property
|
||||
def size(self):
|
||||
@@ -46,7 +44,11 @@ class ObjectTemplate(interfaces.Template):
|
||||
|
||||
Returns: an object adhereing to the Object interface
|
||||
"""
|
||||
return self.object_class(context = context, layer_name = layer_name, offset = offset, symbol_name = self.symbol_name, size = self.size, parent = parent, **self._kwargs)
|
||||
# We always use the template size (as calculated by the object class)
|
||||
# over the one passed in by an argument
|
||||
self._kwargs['size'] = self.size
|
||||
self._kwargs['symbol_name'] = self.symbol_name
|
||||
return self.object_class(context = context, layer_name = layer_name, offset = offset, parent = parent, **self._kwargs)
|
||||
|
||||
class ReferenceTemplate(interfaces.Template):
|
||||
"""Factory class that produces objects based on a delayed reference type
|
||||
|
||||
@@ -53,6 +53,8 @@ class SymbolSpace(collections.Mapping):
|
||||
table_name = symarr[0]
|
||||
symbol_name = symarr[1]
|
||||
return self._dict[table_name].resolve(symbol_name)
|
||||
elif symbol in self.natives:
|
||||
return self.natives.resolve(symbol)
|
||||
raise exceptions.SymbolNotFoundException("Malformed symbol name")
|
||||
|
||||
def resolve(self, symbol):
|
||||
|
||||
@@ -33,19 +33,18 @@ class NativeTable(interfaces.symbols.NativeTableInterface):
|
||||
symbol_space is used to resolve any target symbols if they don't exist in this list
|
||||
"""
|
||||
if symbol_name == 'void':
|
||||
return obj.templates.ObjectTemplate(obj.Void, symbol_name = symbol_name, size = 0)
|
||||
return obj.templates.ObjectTemplate(obj.Void, symbol_name = symbol_name)
|
||||
elif symbol_name == 'array':
|
||||
return obj.templates.ObjectTemplate(obj.Array, symbol_name = symbol_name, count = 0, target = self.resolve('void'), size = 0)
|
||||
return obj.templates.ObjectTemplate(obj.Array, symbol_name = symbol_name, count = 0, target = self.resolve('void'))
|
||||
elif symbol_name == 'Enumeration':
|
||||
return obj.templates.ObjectTemplate(obj.Enumeration, symbol_name = symbol_name, target = self.resolve('void'), choices = {}, size = 0)
|
||||
return obj.templates.ObjectTemplate(obj.Enumeration, symbol_name = symbol_name, target = self.resolve('void'), choices = {})
|
||||
elif symbol_name == 'BitField':
|
||||
return obj.templates.ObjectTemplate(obj.BitField, symbol_name = symbol_name, start_bit = 0, end_bit = 0, size = 0)
|
||||
return obj.templates.ObjectTemplate(obj.BitField, symbol_name = symbol_name, start_bit = 0, end_bit = 0)
|
||||
|
||||
_native_type, native_format = self._native_dictionary[symbol_name]
|
||||
native_size = struct.calcsize(native_format)
|
||||
if symbol_name == 'pointer':
|
||||
return obj.templates.ObjectTemplate(obj.Pointer, symbol_name = symbol_name, target = self.resolve('void'), size = native_size)
|
||||
return obj.templates.ObjectTemplate(self.get_symbol_class(symbol_name), symbol_name = symbol_name, struct_format = native_format, size = native_size)
|
||||
return obj.templates.ObjectTemplate(obj.Pointer, symbol_name = symbol_name, target = self.resolve('void'))
|
||||
return obj.templates.ObjectTemplate(self.get_symbol_class(symbol_name), symbol_name = symbol_name, struct_format = native_format)
|
||||
|
||||
native_types = {'int' : (obj.Integer, '<i'),
|
||||
'long': (obj.Integer, '<i'),
|
||||
|
||||
@@ -10,9 +10,16 @@ class ValidityRoutines(object):
|
||||
def type_check(self, value, valid_type):
|
||||
"""Checks that value is an instance of valid_type, and returns value if it is, or throws a TypeError otherwise"""
|
||||
if not isinstance(value, valid_type):
|
||||
raise TypeError(self.__class__.__name__ + " expected " + valid_type.__class__.__name__ + ", not " + type(value))
|
||||
print(repr(valid_type), repr(type(value).__name__))
|
||||
raise TypeError(self.__class__.__name__ + " expected " + valid_type.__name__ + ", not " + type(value).__name__)
|
||||
return value
|
||||
|
||||
def class_check(self, klass, valid_class):
|
||||
"""Checks that value is an instance of valid_type, and returns value if it is, or throws a TypeError otherwise"""
|
||||
if not issubclass(klass, valid_class):
|
||||
raise TypeError(self.__class__.__name__ + " expected " + valid_class.__name__ + ", not " + klass.__name__)
|
||||
return klass
|
||||
|
||||
def confirm(self, assertion, error):
|
||||
"""Acts like an assertion, but will not be disabled when __debug__ is disabled"""
|
||||
if not assertion:
|
||||
|
||||
Reference in New Issue
Block a user