You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
psrGROUP/env/Lib/site-packages/haystack/outputters/python.py

286 lines
11 KiB

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 Loic Jaquemet loic.jaquemet+python@gmail.com
#
import logging
import numbers
import sys
from haystack.outputters import Outputter
from haystack import types
from haystack import basicmodel
log = logging.getLogger('python')
class PythonOutputter(Outputter):
""" Parse a self._ctypes structure and outputs a pure python object."""
def parse(self, obj, prefix='', depth=50):
"""
Returns a Plain Old python object as a perfect copy of this self._ctypes object.
array would be lists, pointers, inner allocators, and circular
reference should be handled nicely.
"""
# get self class.
try:
obj_module_name = obj.__class__.__module__
obj_class_name = obj.__class__.__name__
try:
obj_module = self._model.get_pythoned_module(obj_module_name)
except KeyError:
# FIXME - ctypes modules should not be in sys.modules. what about reloading?
self._model.build_python_class_clones(sys.modules[obj_module_name])
obj_module = self._model.get_pythoned_module(obj_module_name)
my_class = getattr(obj_module, "%s_py" % obj_class_name)
except AttributeError as e:
log.warning('did you forget to register your python allocators ?')
raise
my_self = my_class()
my_address = self._ctypes.addressof(obj)
# keep ref of the POPO too.
if self._memory_handler.hasRef(my_class, my_address):
return self._memory_handler.getRef(my_class, my_address)
# save our POPO in a partially resolved state, to keep from loops.
self._memory_handler.keepRef(my_self, my_class, my_address)
for field, typ in basicmodel.get_fields(obj):
attr = getattr(obj, field)
try:
member = self._attrToPyObject(attr, field, typ)
except NameError as e:
raise NameError('%s %s\n%s' % (field, typ, e))
setattr(my_self, field, member)
# save the original type (me) and the field
setattr(my_self, '_ctype_', type(obj))
return my_self
def _attrToPyObject(self, attr, field, attrtype):
if self._ctypes.is_basic_type(attrtype):
if self._ctypes.is_basic_ctype(type(attr)):
obj = attr.value
else:
obj = attr
elif self._ctypes.is_struct_type(attrtype) or self._ctypes.is_union_type(attrtype):
attr._mappings_ = self._memory_handler
obj = self.parse(attr)
elif self._ctypes.is_array_of_basic_type(attrtype):
# return a list of int, float, or a char[] to str
obj = self._utils.ctypes_to_python_array(attr)
elif self._ctypes.is_array_type(attrtype):
# array of something else than int/byte
obj = []
eltyp = type(attr[0])
for i in range(0, len(attr)):
obj.append(self._attrToPyObject(attr[i], i, eltyp))
elif self._ctypes.is_cstring_type(attrtype):
obj = self._memory_handler.getRef(
self._ctypes.CString,
self._utils.get_pointee_address(
attr.ptr))
elif self._ctypes.is_function_type(attrtype):
obj = repr(attr)
elif self._ctypes.is_pointer_type(attrtype):
# get the cached Value of the LP.
_subtype = self._utils.get_subtype(attrtype)
_address = self._utils.get_pointee_address(attr)
#if field == 'ProcessHeaps':
# import code
# code.interact(local=locals())
if _address == 0:
# Null pointer
obj = None
elif self._ctypes.is_pointer_to_void_type(attrtype):
# TODO: make a prototype for c_void_p loading
# void types a rereturned as None
obj = None
elif self._ctypes.is_array_of_basic_type(attrtype):
log.error('basic Type array - %s' % (field))
obj = 'BasicType array'
else:
# FIXME we should NOT recurse
# get the cached Value of the LP.
cache = self._memory_handler.getRef(_subtype, _address)
if cache is not None: # struct, union...
obj = self._attrToPyObject(cache, field, _subtype)
else:
# you got here because your pointer is not loaded:
# did you ignore it in expectedValues ?
# is it in the middle of a struct ?
# is that a linked list ?
# is it a invalid instance ?
log.debug('Pointer for field:%s %s/%s not in cache '
'0x%x' % (field, attrtype, self._utils.get_subtype(attrtype),
_address))
return (None, None)
elif isinstance(attr, numbers.Number):
# case for int, long. But needs to be after c_void_p pointers case
obj = attr
else:
log.error('toPyObj default to return attr %s' % (type(attr)))
obj = attr
return obj
def json_encode_pyobj(obj):
if hasattr(obj, '_ctype_'):
return obj.__dict__
elif type(obj).__name__ == 'int':
log.warning('found an int')
return str(obj)
else:
return obj
class pyObj(object):
"""
Base class for a plain old python object.
all haystack/ctypes classes will be translated in this format before pickling.
Operations :
- toString(self, prefix): print a nicely formatted data structure
:param prefix: str to insert before each line (\t after that)
- findCtypes(self) : checks if a self._ctypes is to be found somewhere is the object.
Useful to check if the object can be pickled.
"""
def toString(self, prefix='', maxDepth=10):
if maxDepth < 0:
return '#(- not printed by Excessive recursion - )'
s = '{\n'
if hasattr(self, '_ctype_'):
items = [n for n, t in basicmodel.get_record_type_fields(self._ctype_)]
else:
log.warning('no _ctype_')
items = [n for n in self.__dict__.keys() if n != '_ctype_']
for attrname in items:
attr = getattr(self, attrname)
typ = type(attr)
s += "%s%s: %s\n" % (prefix,
attrname,
self._attrToString(
attr,
attrname,
typ,
prefix + '\t',
maxDepth=maxDepth - 1))
s += '}'
return s
def _attrToString(self, attr, attrname, typ, prefix, maxDepth):
s = ''
if isinstance(attr, tuple) or isinstance(attr, list):
for i in xrange(0, len(attr)):
s += '%s,' % (self._attrToString(attr[i],
i,
None,
prefix + '\t',
maxDepth))
s = "[%s]," % (s)
elif not hasattr(attr, '__dict__'):
s = '%s,' % (repr(attr))
elif isinstance(attr, pyObj):
s = '%s,' % (attr.toString(prefix, maxDepth))
else:
s = '%s,' % (repr(attr))
return s
def __len__(self):
return self._len_
def findCtypes(self, cache=None):
""" recurse on members to check for self._ctypes object. """
if cache is None:
cache = set()
ret = False
for attrname, attr in self.__dict__.items():
if id(attr) in cache: # do not recurse in already parsed
continue
# ignore _ctype_, it's a ctype class type, we know that.
if attrname == '_ctype_':
cache.add(id(attr))
continue
typ = type(attr)
attr = getattr(self, attrname)
log.debug('findCtypes on attr %s' % attrname)
if self._attrFindCtypes(attr, attrname, typ, cache):
log.warning('Found a self._ctypes in %s' % (attrname))
ret = True
return ret
def _attrFindCtypes(self, attr, attrname, typ, cache):
ret = False
cache.add(id(attr))
if hasattr(attr, '_ctype_'): # a pyobj
return attr.findCtypes(cache)
elif isinstance(attr, tuple) or isinstance(attr, list):
for el in attr:
if self._attrFindCtypes(el, 'element', None, cache):
log.warning('Found a self._ctypes in array/tuple')
return True
elif types.is_ctypes_instance(attr):
log.warning('Found a self._ctypes in self %s' % (attr))
return True
else: # int, long, str ...
ret = False
return ret
def __iter__(self):
""" iterate on a instance's type's _fields_ members following the original type field order """
for k, typ in basicmodel.get_fields(self._ctype_):
v = getattr(self, k)
yield (k, v, typ)
pass
# the python cannot contain a ref to a ctypes
def __getstate__(self):
d = self.__dict__.copy()
if '_ctype_' in d:
d['_ctype_'] = d['_ctype_'].__class__.__name__
return d
def __reduce__(self):
"""Explains how to rebuild this class once pickled."""
state = self.__dict__.copy()
if '_ctype_' in state:
state['_ctype_'] = state['_ctype_'].__class__.__name__
name = self.__class__.__name__
modulename = self.__module__
return (_pyObjBuilder(), # __call__
(modulename, name), # arg for builder
state
)
class _pyObjBuilder:
"""Builder of pickled instance of pyObj into properly named POPO"""
def __call__(self, modulename, classname):
# make a simple object which has no complex __init__ (this one will do)
obj = pyObj()
# class = getattr(containing_class, class_name)
# kpy = type('%s.%s' % (modulename, classname), (pyObj,), {})
kpy = type(classname, (pyObj,), {})
obj.__class__ = kpy
return obj
def findCtypesInPyObj(memory_handler, obj):
""" check function to help in unpickling errors correction """
if hasattr(obj, 'findCtypes'):
if obj.findCtypes():
log.warning('Found a self._ctypes in array/tuple')
return True
elif isinstance(obj, tuple) or isinstance(obj, list):
for el in obj:
if findCtypesInPyObj(memory_handler, el):
log.warning('Found a self._ctypes in array/tuple')
return True
elif types.is_ctypes_instance(obj):
return True
return False