<commit_before>try:
    import StringIO
except:
    import io as StringIO
import traceback
from os.path import basename

try:
    __setFalse = False
except:
    import __builtin__
    setattr(__builtin__, 'True', 1)
    setattr(__builtin__, 'False', 0)

import pydevd_constants
from pydevd_constants import DictIterItems, DictKeys, xrange

# Note: 300 is already a lot to see in the outline (after that the user should really use the shell to get things)
# and this also means we'll pass less information to the client side (which makes debugging faster).
MAX_ITEMS_TO_HANDLE = 300

TOO_LARGE_MSG = 'Too large to show contents. Max items to show: ' + str(MAX_ITEMS_TO_HANDLE)
TOO_LARGE_ATTR = 'Unable to handle:'

#=======================================================================================================================
# UnableToResolveVariableException
#=======================================================================================================================
class UnableToResolveVariableException(Exception):
    pass

#=======================================================================================================================
# InspectStub
#=======================================================================================================================
class InspectStub:
    def isbuiltin(self, _args):
        return False
    def isroutine(self, object):
        return False

try:
    import inspect
except:
    inspect = InspectStub()

try:
    import java.lang #@UnresolvedImport
except:
    pass

#types does not include a MethodWrapperType
try:
    MethodWrapperType = type([].__str__)
except:
    MethodWrapperType = None

#=======================================================================================================================
# AbstractResolver
#=======================================================================================================================
class AbstractResolver:
    '''
        This class exists only for documentation purposes to explain how to create a resolver.

        Some examples on how to resolve things:
        - list: getDictionary could return a dict with index->item and use the index to resolve it later
        - set: getDictionary could return a dict with id(object)->object and reiterate in that array to resolve it later
        - arbitrary instance: getDictionary could return dict with attr_name->attr and use getattr to resolve it later
    '''

    def resolve(self, var, attribute):
        '''
            In this method, we'll resolve some child item given the string representation of the item in the key
            representing the previously asked dictionary.

            @param var: this is the actual variable to be resolved.
            @param attribute: this is the string representation of a key previously returned in getDictionary.
        '''
        raise NotImplementedError

    def getDictionary(self, var):
        '''
            @param var: this is the variable that should have its children gotten.

            @return: a dictionary where each pair key, value should be shown to the user as children items
            in the variables view for the given var.
        '''
        raise NotImplementedError

#=======================================================================================================================
# DefaultResolver
#=======================================================================================================================
class DefaultResolver:
    '''
        DefaultResolver is the class that'll actually resolve how to show some variable.
    '''

    def resolve(self, var, attribute):
        return getattr(var, attribute)

    def getDictionary(self, var):
        if MethodWrapperType:
            return self._getPyDictionary(var)
        else:
            return self._getJyDictionary(var)

    def _getJyDictionary(self, obj):
        ret = {}
        found = java.util.HashMap()

        original = obj
        if hasattr(obj, '__class__') and obj.__class__ == java.lang.Class:

            #get info about superclasses
            classes = []
            classes.append(obj)
            c = obj.getSuperclass()
            while c != None:
                classes.append(c)
                c = c.getSuperclass()

            #get info about interfaces
            interfs = []
            for obj in classes:
                interfs.extend(obj.getInterfaces())
            classes.extend(interfs)

            #now is the time when we actually get info on the declared methods and fields
            for obj in classes:

                declaredMethods = obj.getDeclaredMethods()
                declaredFields = obj.getDeclaredFields()
                for i in xrange(len(declaredMethods)):
                    name = declaredMethods[i].getName()
                    ret[name] = declaredMethods[i].toString()
                    found.put(name, 1)

                for i in xrange(len(declaredFields)):
                    name = declaredFields[i].getName()
                    found.put(name, 1)
                    #if declaredFields[i].isAccessible():
                    declaredFields[i].setAccessible(True)
                    #ret[name] = declaredFields[i].get( declaredFields[i] )
                    try:
                        ret[name] = declaredFields[i].get(original)
                    except:
                        ret[name] = declaredFields[i].toString()

        #this simple dir does not always get all the info, that's why we have the part before
        #(e.g.: if we do a dir on String, some methods that are from other interfaces such as
        #charAt don't appear)
        try:
            d = dir(original)
            for name in d:
                if found.get(name) is not 1:
                    ret[name] = getattr(original, name)
        except:
            #sometimes we're unable to do a dir
            pass

        return ret

    def _getPyDictionary(self, var):
        filterPrivate = False
        filterSpecial = True
        filterFunction = True
        filterBuiltIn = True

        names = dir(var)
        if not names and hasattr(var, '__members__'):
            names = var.__members__
        d = {}

        #Be aware that the order in which the filters are applied attempts to
        #optimize the operation by removing as many items as possible in the
        #first filters, leaving fewer items for later filters

        if filterBuiltIn or filterFunction:
            for n in names:
                if filterSpecial:
                    if n.startswith('__') and n.endswith('__'):
                        continue

                if filterPrivate:
                    if n.startswith('_') or n.endswith('__'):
                        continue

                try:
                    attr = getattr(var, n)

                    #filter builtins?
                    if filterBuiltIn:
                        if inspect.isbuiltin(attr):
                            continue

                    #filter functions?
                    if filterFunction:
                        if inspect.isroutine(attr) or isinstance(attr, MethodWrapperType):
                            continue
                except:
                    #if some error occurs getting it, let's put it to the user.
                    strIO = StringIO.StringIO()
                    traceback.print_exc(file=strIO)
                    attr = strIO.getvalue()

                d[ n ] = attr

        return d

#=======================================================================================================================
# DictResolver
#=======================================================================================================================
class DictResolver:

    def resolve(self, dict, key):
        if key in ('__len__', TOO_LARGE_ATTR):
            return None

        if '(' not in key:
            #we have to treat that because the dict resolver is also used to directly resolve the global and local
            #scopes (which already have the items directly)
            return dict[key]

        #ok, we have to iterate over the items to find the one that matches the id, because that's the only way
        #to actually find the reference from the string we have before.
        expected_id = int(key.split('(')[-1][:-1])
        for key, val in DictIterItems(dict):
            if id(key) == expected_id:
                return val

        raise UnableToResolveVariableException()

    def keyStr(self, key):
        if isinstance(key, str):
            return '%r'%key
        else:
            if not pydevd_constants.IS_PY3K:
                if isinstance(key, unicode):
                    return "u'%s'"%key
            return key

    def getDictionary(self, dict):
        ret = {}

        i = 0
        for key, val in DictIterItems(dict):
            i += 1
            #we need to add the id because otherwise we cannot find the real object to get its contents later on.
            key = '%s (%s)' % (self.keyStr(key), id(key))
            ret[key] = val
            if i > MAX_ITEMS_TO_HANDLE:
                ret[TOO_LARGE_ATTR] = TOO_LARGE_MSG
                break

        ret['__len__'] = len(dict)
        return ret

#=======================================================================================================================
# TupleResolver
#=======================================================================================================================
class TupleResolver: #to enumerate tuples and lists

    def resolve(self, var, attribute):
        '''
            @param var: that's the original attribute
            @param attribute: that's the key passed in the dict (as a string)
        '''
        if attribute in ('__len__', TOO_LARGE_ATTR):
            return None
        return var[int(attribute)]

    def getDictionary(self, var):
        l = len(var)
        d = {}

        format_str = '%0' + str(int(len(str(l)))) + 'd'

        i = 0
        for item in var:
            d[format_str % i] = item
            i += 1

            if i > MAX_ITEMS_TO_HANDLE:
                d[TOO_LARGE_ATTR] = TOO_LARGE_MSG
                break

        d['__len__'] = len(var)
        return d

#=======================================================================================================================
# SetResolver
#=======================================================================================================================
class SetResolver:
    '''
        Resolves a set as dict id(object)->object
    '''

    def resolve(self, var, attribute):
        if attribute in ('__len__', TOO_LARGE_ATTR):
            return None

        attribute = int(attribute)
        for v in var:
            if id(v) == attribute:
                return v

        raise UnableToResolveVariableException('Unable to resolve %s in %s' % (attribute, var))

    def getDictionary(self, var):
        d = {}
        i = 0
        for item in var:
            i+= 1
            d[id(item)] = item

            if i > MAX_ITEMS_TO_HANDLE:
                d[TOO_LARGE_ATTR] = TOO_LARGE_MSG
                break

        d['__len__'] = len(var)
        return d

#=======================================================================================================================
# InstanceResolver
#=======================================================================================================================
class InstanceResolver:

    def resolve(self, var, attribute):
        field = var.__class__.getDeclaredField(attribute)
        field.setAccessible(True)
        return field.get(var)

    def getDictionary(self, obj):
        ret = {}

        declaredFields = obj.__class__.getDeclaredFields()
        for i in xrange(len(declaredFields)):
            name = declaredFields[i].getName()
            try:
                declaredFields[i].setAccessible(True)
                ret[name] = declaredFields[i].get(obj)
            except:
                traceback.print_exc()

        return ret

#=======================================================================================================================
# JyArrayResolver
#=======================================================================================================================
class JyArrayResolver:
    '''
        This resolves a regular Object[] array from java
    '''

    def resolve(self, var, attribute):
        if attribute == '__len__':
            return None
        return var[int(attribute)]

    def getDictionary(self, obj):
        ret = {}

        for i in xrange(len(obj)):
            ret[ i ] = obj[i]

        ret['__len__'] = len(obj)
        return ret

#=======================================================================================================================
# NdArrayResolver
#=======================================================================================================================
class NdArrayResolver:
    '''
        This resolves a numpy ndarray returning some metadata about the NDArray
    '''

    def is_numeric(self, obj):
        if not hasattr(obj, 'dtype'):
            return False
        return obj.dtype.kind in 'biufc'

    def resolve(self, obj, attribute):
        if attribute == '__internals__':
            return defaultResolver.getDictionary(obj)
        if attribute == 'min':
            if self.is_numeric(obj):
                return obj.min()
            else:
                return None
        if attribute == 'max':
            if self.is_numeric(obj):
                return obj.max()
            else:
                return None
        if attribute == 'shape':
            return obj.shape
        if attribute == 'dtype':
            return obj.dtype
        if attribute == 'size':
            return obj.size
        if attribute.startswith('['):
            container = NdArrayItemsContainer()
            i = 0
            format_str = '%0' + str(int(len(str(len(obj))))) + 'd'
            for item in obj:
                setattr(container, format_str % i, item)
                i += 1
                if i > MAX_ITEMS_TO_HANDLE:
                    setattr(container, TOO_LARGE_ATTR, TOO_LARGE_MSG)
                    break
            return container
        return None

    def getDictionary(self, obj):
        ret = dict()
        ret['__internals__'] = defaultResolver.getDictionary(obj)
        if obj.size > 1024 * 1024:
            ret['min'] = 'ndarray too big, calculating min would slow down debugging'
            ret['max'] = 'ndarray too big, calculating max would slow down debugging'
        else:
            if self.is_numeric(obj):
                ret['min'] = obj.min()
                ret['max'] = obj.max()
            else:
                ret['min'] = 'not a numeric object'
                ret['max'] = 'not a numeric object'
        ret['shape'] = obj.shape
        ret['dtype'] = obj.dtype
        ret['size'] = obj.size
        ret['[0:%s] ' % (len(obj))] = list(obj[0:MAX_ITEMS_TO_HANDLE])
        return ret

class NdArrayItemsContainer: pass

#=======================================================================================================================
# MultiValueDictResolver
#=======================================================================================================================
class MultiValueDictResolver(DictResolver):

    def resolve(self, dict, key):
        if key in ('__len__', TOO_LARGE_ATTR):
            return None

        #ok, we have to iterate over the items to find the one that matches the id, because that's the only way
        #to actually find the reference from the string we have before.
        expected_id = int(key.split('(')[-1][:-1])
        for key in DictKeys(dict):
            val = dict.getlist(key)
            if id(key) == expected_id:
                return val

        raise UnableToResolveVariableException()

    def getDictionary(self, dict):
        ret = {}
        i = 0
        for key in DictKeys(dict):
            val = dict.getlist(key)
            i += 1
            #we need to add the id because otherwise we cannot find the real object to get its contents later on.
            key = '%s (%s)' % (self.keyStr(key), id(key))
            ret[key] = val
            if i > MAX_ITEMS_TO_HANDLE:
                ret[TOO_LARGE_ATTR] = TOO_LARGE_MSG
                break

        ret['__len__'] = len(dict)
        return ret

#=======================================================================================================================
# FrameResolver
#=======================================================================================================================
class FrameResolver:
    '''
    This resolves a frame.
    '''

    def resolve(self, obj, attribute):
        if attribute == '__internals__':
            return defaultResolver.getDictionary(obj)

        if attribute == 'stack':
            return self.getFrameStack(obj)

        if attribute == 'f_locals':
            return obj.f_locals

        return None

    def getDictionary(self, obj):
        ret = dict()
        ret['__internals__'] = defaultResolver.getDictionary(obj)
        ret['stack'] = self.getFrameStack(obj)
        ret['f_locals'] = obj.f_locals
        return ret

    def getFrameStack(self, frame):
        ret = []
        if frame is not None:
            ret.append(self.getFrameName(frame))

            while frame.f_back:
                frame = frame.f_back
                ret.append(self.getFrameName(frame))

        return ret

    def getFrameName(self, frame):
        if frame is None:
            return 'None'
        try:
            name = basename(frame.f_code.co_filename)
            return 'frame: %s [%s:%s]  id:%s' % (frame.f_code.co_name, name, frame.f_lineno, id(frame))
        except:
            return 'frame object'

defaultResolver = DefaultResolver()
dictResolver = DictResolver()
tupleResolver = TupleResolver()
instanceResolver = InstanceResolver()
jyArrayResolver = JyArrayResolver()
setResolver = SetResolver()
ndarrayResolver = NdArrayResolver()
multiValueDictResolver = MultiValueDictResolver()
frameResolver = FrameResolver()
<commit_msg>Show deque elements in Variables Pane (PY-12940)<commit_after>try:
    import StringIO
except:
    import io as StringIO
import traceback
from os.path import basename

try:
    __setFalse = False
except:
    import __builtin__
    setattr(__builtin__, 'True', 1)
    setattr(__builtin__, 'False', 0)

import pydevd_constants
from pydevd_constants import DictIterItems, DictKeys, xrange

# Note: 300 is already a lot to see in the outline (after that the user should really use the shell to get things)
# and this also means we'll pass less information to the client side (which makes debugging faster).
MAX_ITEMS_TO_HANDLE = 300

TOO_LARGE_MSG = 'Too large to show contents. Max items to show: ' + str(MAX_ITEMS_TO_HANDLE)
TOO_LARGE_ATTR = 'Unable to handle:'

#=======================================================================================================================
# UnableToResolveVariableException
#=======================================================================================================================
class UnableToResolveVariableException(Exception):
    pass

#=======================================================================================================================
# InspectStub
#=======================================================================================================================
class InspectStub:
    def isbuiltin(self, _args):
        return False
    def isroutine(self, object):
        return False

try:
    import inspect
except:
    inspect = InspectStub()

try:
    import java.lang #@UnresolvedImport
except:
    pass

#types does not include a MethodWrapperType
try:
    MethodWrapperType = type([].__str__)
except:
    MethodWrapperType = None

#=======================================================================================================================
# AbstractResolver
#=======================================================================================================================
class AbstractResolver:
    '''
        This class exists only for documentation purposes to explain how to create a resolver.

        Some examples on how to resolve things:
        - list: getDictionary could return a dict with index->item and use the index to resolve it later
        - set: getDictionary could return a dict with id(object)->object and reiterate in that array to resolve it later
        - arbitrary instance: getDictionary could return dict with attr_name->attr and use getattr to resolve it later
    '''

    def resolve(self, var, attribute):
        '''
            In this method, we'll resolve some child item given the string representation of the item in the key
            representing the previously asked dictionary.

            @param var: this is the actual variable to be resolved.
            @param attribute: this is the string representation of a key previously returned in getDictionary.
        '''
        raise NotImplementedError

    def getDictionary(self, var):
        '''
            @param var: this is the variable that should have its children gotten.

            @return: a dictionary where each pair key, value should be shown to the user as children items
            in the variables view for the given var.
        '''
        raise NotImplementedError

#=======================================================================================================================
# DefaultResolver
#=======================================================================================================================
class DefaultResolver:
    '''
        DefaultResolver is the class that'll actually resolve how to show some variable.
    '''

    def resolve(self, var, attribute):
        return getattr(var, attribute)

    def getDictionary(self, var):
        if MethodWrapperType:
            return self._getPyDictionary(var)
        else:
            return self._getJyDictionary(var)

    def _getJyDictionary(self, obj):
        ret = {}
        found = java.util.HashMap()

        original = obj
        if hasattr(obj, '__class__') and obj.__class__ == java.lang.Class:

            #get info about superclasses
            classes = []
            classes.append(obj)
            c = obj.getSuperclass()
            while c != None:
                classes.append(c)
                c = c.getSuperclass()

            #get info about interfaces
            interfs = []
            for obj in classes:
                interfs.extend(obj.getInterfaces())
            classes.extend(interfs)

            #now is the time when we actually get info on the declared methods and fields
            for obj in classes:

                declaredMethods = obj.getDeclaredMethods()
                declaredFields = obj.getDeclaredFields()
                for i in xrange(len(declaredMethods)):
                    name = declaredMethods[i].getName()
                    ret[name] = declaredMethods[i].toString()
                    found.put(name, 1)

                for i in xrange(len(declaredFields)):
                    name = declaredFields[i].getName()
                    found.put(name, 1)
                    #if declaredFields[i].isAccessible():
                    declaredFields[i].setAccessible(True)
                    #ret[name] = declaredFields[i].get( declaredFields[i] )
                    try:
                        ret[name] = declaredFields[i].get(original)
                    except:
                        ret[name] = declaredFields[i].toString()

        #this simple dir does not always get all the info, that's why we have the part before
        #(e.g.: if we do a dir on String, some methods that are from other interfaces such as
        #charAt don't appear)
        try:
            d = dir(original)
            for name in d:
                if found.get(name) is not 1:
                    ret[name] = getattr(original, name)
        except:
            #sometimes we're unable to do a dir
            pass

        return ret

    def _getPyDictionary(self, var):
        filterPrivate = False
        filterSpecial = True
        filterFunction = True
        filterBuiltIn = True

        names = dir(var)
        if not names and hasattr(var, '__members__'):
            names = var.__members__
        d = {}

        #Be aware that the order in which the filters are applied attempts to
        #optimize the operation by removing as many items as possible in the
        #first filters, leaving fewer items for later filters

        if filterBuiltIn or filterFunction:
            for n in names:
                if filterSpecial:
                    if n.startswith('__') and n.endswith('__'):
                        continue

                if filterPrivate:
                    if n.startswith('_') or n.endswith('__'):
                        continue

                try:
                    attr = getattr(var, n)

                    #filter builtins?
                    if filterBuiltIn:
                        if inspect.isbuiltin(attr):
                            continue

                    #filter functions?
                    if filterFunction:
                        if inspect.isroutine(attr) or isinstance(attr, MethodWrapperType):
                            continue
                except:
                    #if some error occurs getting it, let's put it to the user.
                    strIO = StringIO.StringIO()
                    traceback.print_exc(file=strIO)
                    attr = strIO.getvalue()

                d[ n ] = attr

        return d

#=======================================================================================================================
# DictResolver
#=======================================================================================================================
class DictResolver:

    def resolve(self, dict, key):
        if key in ('__len__', TOO_LARGE_ATTR):
            return None

        if '(' not in key:
            #we have to treat that because the dict resolver is also used to directly resolve the global and local
            #scopes (which already have the items directly)
            return dict[key]

        #ok, we have to iterate over the items to find the one that matches the id, because that's the only way
        #to actually find the reference from the string we have before.
        expected_id = int(key.split('(')[-1][:-1])
        for key, val in DictIterItems(dict):
            if id(key) == expected_id:
                return val

        raise UnableToResolveVariableException()

    def keyStr(self, key):
        if isinstance(key, str):
            return '%r'%key
        else:
            if not pydevd_constants.IS_PY3K:
                if isinstance(key, unicode):
                    return "u'%s'"%key
            return key

    def getDictionary(self, dict):
        ret = {}

        i = 0
        for key, val in DictIterItems(dict):
            i += 1
            #we need to add the id because otherwise we cannot find the real object to get its contents later on.
            key = '%s (%s)' % (self.keyStr(key), id(key))
            ret[key] = val
            if i > MAX_ITEMS_TO_HANDLE:
                ret[TOO_LARGE_ATTR] = TOO_LARGE_MSG
                break

        ret['__len__'] = len(dict)
        return ret

#=======================================================================================================================
# TupleResolver
#=======================================================================================================================
class TupleResolver: #to enumerate tuples and lists

    def resolve(self, var, attribute):
        '''
            @param var: that's the original attribute
            @param attribute: that's the key passed in the dict (as a string)
        '''
        if attribute in ('__len__', TOO_LARGE_ATTR):
            return None
        return var[int(attribute)]

    def getDictionary(self, var):
        l = len(var)
        d = {}

        format_str = '%0' + str(int(len(str(l)))) + 'd'

        i = 0
        for item in var:
            d[format_str % i] = item
            i += 1

            if i > MAX_ITEMS_TO_HANDLE:
                d[TOO_LARGE_ATTR] = TOO_LARGE_MSG
                break

        d['__len__'] = len(var)
        return d

#=======================================================================================================================
# SetResolver
#=======================================================================================================================
class SetResolver:
    '''
        Resolves a set as dict id(object)->object
    '''

    def resolve(self, var, attribute):
        if attribute in ('__len__', TOO_LARGE_ATTR):
            return None

        attribute = int(attribute)
        for v in var:
            if id(v) == attribute:
                return v

        raise UnableToResolveVariableException('Unable to resolve %s in %s' % (attribute, var))

    def getDictionary(self, var):
        d = {}
        i = 0
        for item in var:
            i+= 1
            d[id(item)] = item

            if i > MAX_ITEMS_TO_HANDLE:
                d[TOO_LARGE_ATTR] = TOO_LARGE_MSG
                break

        d['__len__'] = len(var)
        return d

#=======================================================================================================================
# InstanceResolver
#=======================================================================================================================
class InstanceResolver:

    def resolve(self, var, attribute):
        field = var.__class__.getDeclaredField(attribute)
        field.setAccessible(True)
        return field.get(var)

    def getDictionary(self, obj):
        ret = {}

        declaredFields = obj.__class__.getDeclaredFields()
        for i in xrange(len(declaredFields)):
            name = declaredFields[i].getName()
            try:
                declaredFields[i].setAccessible(True)
                ret[name] = declaredFields[i].get(obj)
            except:
                traceback.print_exc()

        return ret

#=======================================================================================================================
# JyArrayResolver
#=======================================================================================================================
class JyArrayResolver:
    '''
        This resolves a regular Object[] array from java
    '''

    def resolve(self, var, attribute):
        if attribute == '__len__':
            return None
        return var[int(attribute)]

    def getDictionary(self, obj):
        ret = {}

        for i in xrange(len(obj)):
            ret[ i ] = obj[i]

        ret['__len__'] = len(obj)
        return ret

#=======================================================================================================================
# NdArrayResolver
#=======================================================================================================================
class NdArrayResolver:
    '''
        This resolves a numpy ndarray returning some metadata about the NDArray
    '''

    def is_numeric(self, obj):
        if not hasattr(obj, 'dtype'):
            return False
        return obj.dtype.kind in 'biufc'

    def resolve(self, obj, attribute):
        if attribute == '__internals__':
            return defaultResolver.getDictionary(obj)
        if attribute == 'min':
            if self.is_numeric(obj):
                return obj.min()
            else:
                return None
        if attribute == 'max':
            if self.is_numeric(obj):
                return obj.max()
            else:
                return None
        if attribute == 'shape':
            return obj.shape
        if attribute == 'dtype':
            return obj.dtype
        if attribute == 'size':
            return obj.size
        if attribute.startswith('['):
            container = NdArrayItemsContainer()
            i = 0
            format_str = '%0' + str(int(len(str(len(obj))))) + 'd'
            for item in obj:
                setattr(container, format_str % i, item)
                i += 1
                if i > MAX_ITEMS_TO_HANDLE:
                    setattr(container, TOO_LARGE_ATTR, TOO_LARGE_MSG)
                    break
            return container
        return None

    def getDictionary(self, obj):
        ret = dict()
        ret['__internals__'] = defaultResolver.getDictionary(obj)
        if obj.size > 1024 * 1024:
            ret['min'] = 'ndarray too big, calculating min would slow down debugging'
            ret['max'] = 'ndarray too big, calculating max would slow down debugging'
        else:
            if self.is_numeric(obj):
                ret['min'] = obj.min()
                ret['max'] = obj.max()
            else:
                ret['min'] = 'not a numeric object'
                ret['max'] = 'not a numeric object'
        ret['shape'] = obj.shape
        ret['dtype'] = obj.dtype
        ret['size'] = obj.size
        ret['[0:%s] ' % (len(obj))] = list(obj[0:MAX_ITEMS_TO_HANDLE])
        return ret

class NdArrayItemsContainer: pass

#=======================================================================================================================
# MultiValueDictResolver
#=======================================================================================================================
class MultiValueDictResolver(DictResolver):

    def resolve(self, dict, key):
        if key in ('__len__', TOO_LARGE_ATTR):
            return None

        #ok, we have to iterate over the items to find the one that matches the id, because that's the only way
        #to actually find the reference from the string we have before.
        expected_id = int(key.split('(')[-1][:-1])
        for key in DictKeys(dict):
            val = dict.getlist(key)
            if id(key) == expected_id:
                return val

        raise UnableToResolveVariableException()

    def getDictionary(self, dict):
        ret = {}
        i = 0
        for key in DictKeys(dict):
            val = dict.getlist(key)
            i += 1
            #we need to add the id because otherwise we cannot find the real object to get its contents later on.
            key = '%s (%s)' % (self.keyStr(key), id(key))
            ret[key] = val
            if i > MAX_ITEMS_TO_HANDLE:
                ret[TOO_LARGE_ATTR] = TOO_LARGE_MSG
                break

        ret['__len__'] = len(dict)
        return ret

#=======================================================================================================================
# DequeResolver
#=======================================================================================================================
class DequeResolver(TupleResolver):
    def getDictionary(self, var):
        d = TupleResolver.getDictionary(self, var)
        d['maxlen'] = getattr(var, 'maxlen', None)
        return d

#=======================================================================================================================
# FrameResolver
#=======================================================================================================================
class FrameResolver:
    '''
    This resolves a frame.
    '''

    def resolve(self, obj, attribute):
        if attribute == '__internals__':
            return defaultResolver.getDictionary(obj)

        if attribute == 'stack':
            return self.getFrameStack(obj)

        if attribute == 'f_locals':
            return obj.f_locals

        return None

    def getDictionary(self, obj):
        ret = dict()
        ret['__internals__'] = defaultResolver.getDictionary(obj)
        ret['stack'] = self.getFrameStack(obj)
        ret['f_locals'] = obj.f_locals
        return ret

    def getFrameStack(self, frame):
        ret = []
        if frame is not None:
            ret.append(self.getFrameName(frame))

            while frame.f_back:
                frame = frame.f_back
                ret.append(self.getFrameName(frame))

        return ret

    def getFrameName(self, frame):
        if frame is None:
            return 'None'
        try:
            name = basename(frame.f_code.co_filename)
            return 'frame: %s [%s:%s]  id:%s' % (frame.f_code.co_name, name, frame.f_lineno, id(frame))
        except:
            return 'frame object'

defaultResolver = DefaultResolver()
dictResolver = DictResolver()
tupleResolver = TupleResolver()
instanceResolver = InstanceResolver()
jyArrayResolver = JyArrayResolver()
setResolver = SetResolver()
ndarrayResolver = NdArrayResolver()
multiValueDictResolver = MultiValueDictResolver()
dequeResolver = DequeResolver()
frameResolver = FrameResolver()
<|endoftext|>


<commit_before>"""Word Co-occurence finder tool
"""
import os
import sys
import mmap
import json
import typing as tg
from multiprocessing import Pool
import csv
import pdb
import re
from collections import defaultdict

def word_count(args: tg.Tuple[tg.List[str], tg.Dict[str, tg.List]]) -> tg.Tuple[str, str, tg.Dict[str, str]]:
    """function that count the number of defined keys in given text
    using tuple to store args due to Pool's map method only support one parameter
    """
    if DEBUG:
        #print(args)
        pass

    row, raw_key = args

    id_, string = row

    result = defaultdict()

    for entity, entity_key in raw_key.items():
        is_match = False
        for key_type, key_list in entity_key.items():
            if key_type in ("abbreviation", "regular_expression"):
                for key in key_list:
                    match = re.search(key, string, re.UNICODE)
                    if match:
                        is_match = True
                        break
            elif type in ("full_name",):
                for key in key_list:
                    match = re.search(key, string, re.UNICODE + re.IGNORECASE)
                    if match:
                        is_match = True
                        break
        if is_match:
            result[entity] = match.group()

    return id_, string, result # tg.Dict[str, str]

DEBUG = False
NUM_PROCESS = 8
CHUNK_SIZE = 100
COOCCURENCE_THRESHOLD = 1

if __name__ == "__main__":
    if DEBUG:
        text_file = "TEST.tsv"
        with open("TEST.json", 'r', encoding='utf_8') as keyin:
            keys = json.load(keyin)
        pass
    else:
        if len(sys.argv) < 3:
            sys.exit("Not enough parameters")
        else:
            text_file = sys.argv[1]
            with open(sys.argv[2], 'r', encoding='utf_8') as keyin:
                keys = json.load(keyin)

    print("Config loaded.")

    with open(text_file, 'r', encoding='utf_8') as tsvin:
        print("Text opened, processing.")
        tsv_reader = csv.reader(tsvin, delimiter='\t')
        with Pool(NUM_PROCESS) as p:
            result = p.imap_unordered(word_count, ((row, keys) for row in tsv_reader), CHUNK_SIZE)
            with open("out_1.tsv", 'w', encoding='utf_8', newline='') as outfile1, open("out_2.tsv", 'w', encoding='utf_8', newline='') as outfile2:
                tsv_writer1 = csv.writer(outfile1, delimiter='\t')
                tsv_writer2 = csv.writer(outfile2, delimiter='\t')
                for id_, original_string, result_dict in result:
                    if len(result_dict) > COOCCURENCE_THRESHOLD:
                        tsv_writer1.writerows(([id_, entity, raw_mention] for entity, raw_mention in result_dict.items()))
                        tsv_writer2.writerow([id_, original_string] + [entity for entity in result_dict])
        print("Finished. Check out_1.tsv and out_2.tsv file for result")<commit_msg>fix: fix misspell variables<commit_after>"""Word Co-occurence finder tool
"""
import os
import sys
import mmap
import json
import typing as tg
from multiprocessing import Pool
import csv
import pdb
import re
from collections import defaultdict

def word_count(args: tg.Tuple[tg.List[str], tg.Dict[str, tg.List]]) -> tg.Tuple[str, str, tg.Dict[str, str]]:
    """function that count the number of defined keys in given text
    using tuple to store args due to Pool's map method only support one parameter
    """
    if DEBUG:
        #print(args)
        pass

    row, raw_key = args

    id_, string = row

    result = defaultdict()

    for entity, entity_key in raw_key.items():
        is_match = False
        for key_type, key_list in entity_key.items():
            if key_type in ("abbreviation", "regular_expression"):
                for key in key_list:
                    match = re.search(key, string, re.UNICODE)
                    if match:
                        is_match = True
                        break
            elif key_type in ("full_name",):
                for key in key_list:
                    match = re.search(key, string, re.UNICODE + re.IGNORECASE)
                    if match:
                        is_match = True
                        break
        if is_match:
            result[entity] = match.group()

    return id_, string, result # tg.Dict[str, str]

DEBUG = False
NUM_PROCESS = 8
CHUNK_SIZE = 100
COOCCURENCE_THRESHOLD = 1

if __name__ == "__main__":
    if DEBUG:
        text_file = "TEST.tsv"
        with open("TEST.json", 'r', encoding='utf_8') as keyin:
            keys = json.load(keyin)
        pass
    else:
        if len(sys.argv) < 3:
            sys.exit("Not enough parameters")
        else:
            text_file = sys.argv[1]
            with open(sys.argv[2], 'r', encoding='utf_8') as keyin:
                keys = json.load(keyin)

    print("Config loaded.")

    with open(text_file, 'r', encoding='utf_8') as tsvin:
        print("Text opened, processing.")
        tsv_reader = csv.reader(tsvin, delimiter='\t')
        with Pool(NUM_PROCESS) as p:
            result = p.imap_unordered(word_count, ((row, keys) for row in tsv_reader), CHUNK_SIZE)
            with open("out_1.tsv", 'w', encoding='utf_8', newline='') as outfile1, open("out_2.tsv", 'w', encoding='utf_8', newline='') as outfile2:
                tsv_writer1 = csv.writer(outfile1, delimiter='\t')
                tsv_writer2 = csv.writer(outfile2, delimiter='\t')
                for id_, original_string, result_dict in result:
                    if len(result_dict) > COOCCURENCE_THRESHOLD:
                        tsv_writer1.writerows(([id_, entity, raw_mention] for entity, raw_mention in result_dict.items()))
                        tsv_writer2.writerow([id_, original_string] + [entity for entity in result_dict])
        print("Finished. Check out_1.tsv and out_2.tsv file for result")<|endoftext|>


<commit_before>#!/usr/bin/env python

from operator import mul as multiply

def count(val,iter):
    """ Counts how many times a value (val) occurs in an iterable """
    return sum(1 for x in (y for y in iter if y is not None) if val == x)
def table(seq):
    """
    For each unique value in seq, runs count() on it. Returns a dictionary in
    the form of {value1: count, value2: count}.
    """
    seq = [x for x in seq]
    keys = set(seq)
    return dict([(k,seq.count(k)) for k in keys])
def flatten(x):
    """
    Recursively flattens lists.
    From: http://stackoverflow.com/questions/5409224/python-recursively-flatten-a-list
    """
    try:
        it = iter(x)
    except TypeError:
        yield x
    else:
        for i in it:
            for j in flatten(i):
                yield j
def invert_dict(d):
    """
    Makes the keys the values and the values the keys
    WARNING: No guarantee of dictionary structure if mappings are not unique
    """
    return dict( (y,x) for x,y in d.items() )

# Common stats/math functions
def product(iter):
    """
    Reduces an iterable by multiplication. Analogous to sum, but with multiplication
    instead of addition.
    """
    # This should really be a python builtiin
    return reduce(multiply, iter)
def cumsum(iter):
    """
    Cumulative sum:
    >>> pydigree.cumsum([0,1,2,3,4])
    [0, 1, 3, 6, 10]
    """
    value = 0
    g = [None] * len(iter)
    for idx,x in enumerate(iter):
        value += x
        g[idx]=value
    return g

def invcumsum(iter):
    """
    Inverse of cumsum. iter can't be a generator currently

    >>> a = range(5)
    >>> pydigree.invcumsum(pydigree.cumsum(a)) == a
    True

    """
    return [x - iter[i-1] if i > 0 else 0 for i,x in enumerate(iter)]

<commit_msg>added grouper<commit_after>#!/usr/bin/env python

from itertools import izip_longest
from operator import mul as multiply

def count(val,iter):
    """ Counts how many times a value (val) occurs in an iterable """
    return sum(1 for x in (y for y in iter if y is not None) if val == x)
def table(seq):
    """
    For each unique value in seq, runs count() on it. Returns a dictionary in
    the form of {value1: count, value2: count}.
    """
    seq = [x for x in seq]
    keys = set(seq)
    return dict([(k,seq.count(k)) for k in keys])
def flatten(x):
    """
    Recursively flattens lists.
    From: http://stackoverflow.com/questions/5409224/python-recursively-flatten-a-list
    """
    try:
        it = iter(x)
    except TypeError:
        yield x
    else:
        for i in it:
            for j in flatten(i):
                yield j
def grouper(iterable, n, fillvalue=None):
    "Collect data into fixed-length chunks or blocks"
    # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx
    args = [iter(iterable)] * n
    return izip_longest(fillvalue=fillvalue, *args)
def invert_dict(d):
    """
    Makes the keys the values and the values the keys
    WARNING: No guarantee of dictionary structure if mappings are not unique
    """
    return dict( (y,x) for x,y in d.items() )

# Common stats/math functions
def product(iter):
    """
    Reduces an iterable by multiplication. Analogous to sum, but with multiplication
    instead of addition.
    """
    # This should really be a python builtiin
    return reduce(multiply, iter)
def cumsum(iter):
    """
    Cumulative sum:
    >>> pydigree.cumsum([0,1,2,3,4])
    [0, 1, 3, 6, 10]
    """
    value = 0
    g = [None] * len(iter)
    for idx,x in enumerate(iter):
        value += x
        g[idx]=value
    return g

def invcumsum(iter):
    """
    Inverse of cumsum. iter can't be a generator currently

    >>> a = range(5)
    >>> pydigree.invcumsum(pydigree.cumsum(a)) == a
    True

    """
    return [x - iter[i-1] if i > 0 else 0 for i,x in enumerate(iter)]

<|endoftext|>


<commit_before>#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys
import os
import time
import logging
import asyncio
import unittest
from multiprocessing import Process, Pipe
from types import FunctionType, MethodType

from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.common.utils import free_port
from selenium.webdriver.remote.webdriver import WebDriver
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.support.select import Select

from tornado.httpclient import AsyncHTTPClient, HTTPResponse
from tornado.platform.asyncio import AsyncIOMainLoop, to_asyncio_future
from tornado.websocket import websocket_connect, WebSocketClientConnection

from wdom.misc import install_asyncio
from wdom import options
from wdom.window import customElements
from wdom.element import Element
from wdom import server

driver = webdriver.Firefox
local_webdriver = None
remote_webdriver = None
browser_implict_wait = 0
logger = logging.getLogger(__name__)

def reset():
    '''Reset wdom objects. This function clear all connections, elements, and
    resistered custom elements. This function also makes new
    document/application and set them.
    '''
    from wdom.document import get_new_document, set_document
    set_document(get_new_document())
    from wdom.server import _tornado
    _tornado.connections.clear()
    _tornado.set_application(_tornado.Application())
    try:
        from wdom.server import _aiohttp
        _aiohttp.connections.clear()
        _aiohttp.set_application(_aiohttp.Application())
    except ImportError:
        pass
    Element._elements_with_id.clear()
    Element._elements.clear()
    customElements.clear()

def suppress_logging():
    options.root_logger.removeHandler(options._log_handler)
    options.root_logger.addHandler(logging.NullHandler())

class TestCase(unittest.TestCase):
    '''Base class for testing wdom modules. This class is a sub class of the
    ``unittest.TestCase``. After all test methods, reset wdom's global objects
    like document, application, and elements. If you use ``tearDown`` method,
    do not forget to call ``super().tearDown()``.

    If you want to reuse document/application object in your test class, please
    set them in each setup phase as follow::

        @classmethod
        def setUpClass(cls):
            cls.your_doc = get_document()
            cls.your_app = get_app()

        def setUp(self):
            from wdom.document import set_document
            from wdom.server import set_application
            set_document(self.your_doc)
            set_application(self.your_app)
    '''
    def tearDown(self):
        reset()
        super().tearDown()

    def assertIsTrue(self, bl):
        self.assertIs(bl, True)

    def assertIsFalse(self, bl):
        self.assertIs(bl, False)

class HTTPTestCase(TestCase):
    '''For http/ws connection test.'''
    _server_started = False

    def start(self):
        with self.assertLogs('wdom', 'INFO'):
            self.server = server.start_server(port=0)
        self.port = self.server.port
        self.url = 'http://localhost:{}'.format(self.port)
        self.ws_url = 'ws://localhost:{}'.format(self.port)
        self._server_started = True

    def tearDown(self):
        if self._server_started:
            with self.assertLogs('wdom', 'INFO'):
                server.stop_server(self.server)
            self._server_started = False
        super().tearDown()

    @asyncio.coroutine
    def fetch(self, url: str, encoding: str = 'utf-8') -> HTTPResponse:
        '''Fetch url. Response body is decoded by ``encoding`` and set
        ``text`` property of the response. If failed to decode, ``text``
        property will set to ``None``.
        '''
        response = yield from to_asyncio_future(
            AsyncHTTPClient().fetch(url, raise_error=False))
        try:
            response.text = response.body.decode(encoding)
        except UnicodeDecodeError:
            response.text = None
        return response

    @asyncio.coroutine
    def ws_connect(self, url: str, _retry=0,
                   _max=100 if os.environ.get('TRAVIS') else 20,
                   _wait=0.05) -> WebSocketClientConnection:
        '''Make WebSocket connection to the url.'''
        try:
            ws = yield from to_asyncio_future(websocket_connect(url))
        except ConnectionRefusedError:
            if _retry <= _max:
                yield from asyncio.sleep(_wait)
                ws = yield from self.ws_connect(url, _retry+1, _max, _wait)
            else:
                raise ConnectionRefusedError(
                    'WebSocket connection refused: {}'.format(url))
        return ws

def start_webdriver():
    '''Start WebDriver and set implicit_wait if it is not started.'''
    global local_webdriver
    if local_webdriver is None:
        local_webdriver = driver()
        if browser_implict_wait:
            local_webdriver.implicitly_wait(browser_implict_wait)

def close_webdriver():
    '''Close WebDriver.'''
    global local_webdriver
    if local_webdriver is not None:
        local_webdriver.close()
        local_webdriver = None

def get_webdriver():
    '''Return WebDriver of current process. If it is not started, start and
    return it.'''
    if globals().get('local_webdriver') is None:
        start_webdriver()
    return local_webdriver

def _clear():
    global conn, wd_conn, browser_manager, remote_webdriver
    conn = None
    wd_conn = None
    browser_manager = None
    remote_webdriver = None

def start_remote_browser():
    '''Start WebDriver on remote process.'''
    global browser_manager, conn, wd_conn
    conn, wd_conn = Pipe()

    def start_browser():
        global wd_conn
        bc = BrowserController(wd_conn)
        bc.run()

    browser_manager = Process(target=start_browser)
    browser_manager.start()

def close_remote_browser():
    '''Terminate browser process.'''
    global conn, browser_manager
    conn.send({'target': 'process', 'method': 'quit'})
    time.sleep(0.3)
    logger.info('\nRemote Browser closed')
    conn.close()
    if browser_manager is not None:
        browser_manager.terminate()
    _clear()

def get_remote_browser():
    '''Start new WebDriver for remote process.'''
    global remote_webdriver
    if remote_webdriver is None:
        remote_webdriver = driver()
        if browser_implict_wait:
            remote_webdriver.implicitly_wait(browser_implict_wait)
        return remote_webdriver
    else:
        return remote_webdriver

class BrowserController:
    '''Class to run webdriver in different proceess. Inter-process
    communication is done via Pipe.
    '''
    _select_methods = [s for s in dir(Select) if not s.startswith('_')]

    def __init__(self, conn):
        '''Set up connection and start webdriver. ``conn`` is a one end of
        ``Pipe()``, which is used the inter-process communication.
        '''
        self.conn = conn
        self.wd = get_remote_browser()
        self.element = None

    def set_element_by_id(self, id):
        '''Find element with ``id`` and set it as operation target. When
        successfully find the element, send ``True``. If failed to find the
        element, send message ``Error NoSuchElement: {{ id }}``.'''
        try:
            self.element = self.wd.find_element_by_css_selector(
                '[rimo_id="{}"]'.format(id))
            return True
        except NoSuchElementException:
            return 'Error NoSuchElement: ' + id

    def quit(self, *args):
        self.wd.quit()
        return 'closed'

    def close(self, *args):
        self.wd.close()
        return 'closed'

    def _execute_method(self, method, args):
        if isinstance(method, (FunctionType, MethodType)):
            self.conn.send(method(*args))
        else:
            # not callable, just send it back
            self.conn.send(method)

    def run(self):
        '''Running process. Wait message from the other end of the connection,
        and when gat message, execute the method specified by the message.
        The message is a python's dict, which must have ``method`` field.
        '''
        while True:
            req = self.conn.recv()
            target = req.get('target', '')
            method_name = req.get('method', '')
            args = req.get('args', [])
            if target == 'process':
                method = getattr(self, method_name)
            elif target == 'browser':
                method = getattr(self.wd, method_name)
            elif target == 'element':
                if self.element is None:
                    # Element must be set
                    self.conn.send('Error: No Element Set')
                    continue
                if (method_name in self._select_methods and
                        self.element.tag_name.lower() == 'select'):
                    s = Select(self.element)
                    method = getattr(s, method_name)
                else:
                    method = getattr(self.element, method_name)
            self._execute_method(method, args)

def wait_for():
    '''Wait the action doing in the browser process, and afer finish it,
    return the value.'''
    return asyncio.get_event_loop().run_until_complete(wait_coro())

@asyncio.coroutine
def wait_coro():
    while True:
        state = conn.poll()
        if state:
            res = conn.recv()
            return res
        else:
            yield from asyncio.sleep(0)
            continue

def _get_properties(cls):
    props = set()
    for k, v in vars(cls).items():
        if not isinstance(v, (FunctionType, MethodType)):
            props.add(k)
    return props

class Controller:
    target = None
    properties = set()

    def __getattr__(self, attr: str):
        global conn

        def wrapper(*args):
            conn.send({'target': self.target, 'method': attr, 'args': args})
            res = wait_for()
            if isinstance(res, str):
                if res.startswith('Error NoSuchElement'):
                    raise NoSuchElementException(res)
                elif res.startswith('Error'):
                    raise ValueError(res)
            return res
        if attr in self.properties:
            return wrapper()
        else:
            return wrapper

class ProcessController(Controller):
    target = 'process'

class RemoteBrowserController(Controller):
    target = 'browser'
    properties = _get_properties(WebDriver)

class RemoteElementController(Controller):
    target = 'element'
    properties = _get_properties(WebElement)

class RemoteBrowserTestCase:
    '''This class is **Experimental**.

    Utility class for testing apps with webdriver in another process. Mainly
    used for development and test of wdom library itself. This class does not
    support all methods provided by selenium.webdriver, but maybe enough.

    Usage:

    Make subclass and override ``get_app`` method to return app (instance of
    ``wdom.server.Application`` or ``tornado.web.Application``), which you want
    to test.
    '''
    wait_time = 0.2 if os.environ.get('TRAVIS', False) else 0.05

    def start(self):
        self._prev_logging = options.config.logging
        options.config.logging = 'warn'
        self.proc = ProcessController()
        self.browser = RemoteBrowserController()
        self.element = RemoteElementController()
        try:
            self.server = server.start_server(port=0)
        except OSError:
            self.wait(0.2)
            self.server = server.start_server(port=0)
        self.address = self.server.address
        self.url = 'http://{0}:{1}/'.format(self.address, self.port)
        self.wait()
        self.browser.get(self.url)
        self.wait()

    def tearDown(self):
        options.config.logging = self._prev_logging
        self.stop_server()
        self.wait()
        sys.stdout.flush()
        sys.stderr.flush()
        super().tearDown()

    @property
    def port(self) -> int:
        return self.server.port

    def stop_server(self):
        server.stop_server(self.server)

    def wait(self, timeout=None):
        '''Wait until ``timeout``. The default timeout is zero, so wait a
        single event loop. This method does not block the thread, so the server
        in test still can send response before timeout.
        '''
        asyncio.get_event_loop().run_until_complete(
            asyncio.sleep(timeout or self.wait_time))

    def set_element(self, node):
        '''Wrapper method of ``set_element_by_id``. Set the ``node`` as a
        target node of the browser process.'''
        return self.proc.set_element_by_id(node.rimo_id)

class WebDriverTestCase:
    '''Base class for testing UI on browser. This class starts up an HTTP
    server on a new subprocess.

    Subclasses must override ``get_app()`` method, which returns the
    ``pygmariot.server.Application`` or ``tornado.web.Application`` to be
    tested.
    '''
    wait_time = 0.2 if os.environ.get('TRAVIS', False) else 0.05

    @classmethod
    def setUpClass(cls):
        cls._orig_loop = asyncio.get_event_loop()
        # Need to use different loop for aiohttp after remote_browser tests
        # but I can't understand why...?
        asyncio.set_event_loop(asyncio.new_event_loop())
        # When change default loop, tornado's ioloop needs to be reinstalled
        AsyncIOMainLoop().clear_current()
        AsyncIOMainLoop().clear_instance()
        install_asyncio()

    @classmethod
    def tearDownClass(cls):
        # Set original loop and reinstall to tornado
        asyncio.set_event_loop(cls._orig_loop)
        AsyncIOMainLoop().clear_current()
        AsyncIOMainLoop().clear_instance()
        install_asyncio()

    def start(self):
        self.wd = get_webdriver()

        def start_server(port):
            import asyncio
            from wdom import server
            server.start_server(port=port)
            asyncio.get_event_loop().run_forever()

        self.address = 'localhost'
        self.port = free_port()
        self.url = 'http://{0}:{1}/'.format(self.address, self.port)

        self.server = Process(
            target=start_server,
            args=(self.port, )
        )
        self.server.start()
        self.wait(0.1)
        self.wd.get(self.url)
        self.wait(0.05)

    def tearDown(self):
        '''Terminate server subprocess.'''
        self.server.terminate()
        sys.stdout.flush()
        sys.stderr.flush()
        super().tearDown()

    def get_app(self):
        '''This method should be overridden. Return
        ``pygmariot.server.Application`` or ``tornado.web.Application`` to be
        tested.
        '''
        NotImplementedError

    def wait(self, timeout=None):
        '''Wait until ``timeout``. The default timeout is zero, so wait a
        single event loop.'''
        time.sleep(timeout or self.wait_time)

    def send_keys(self, element, keys: str):
        '''Send ``keys`` to ``element`` one-by-one. Safer than using
        ``element.send_keys`` method.
        '''
        for k in keys:
            element.send_keys(k)
<commit_msg>Longer wait for travis<commit_after>#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys
import os
import time
import logging
import asyncio
import unittest
from multiprocessing import Process, Pipe
from types import FunctionType, MethodType

from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.common.utils import free_port
from selenium.webdriver.remote.webdriver import WebDriver
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.support.select import Select

from tornado.httpclient import AsyncHTTPClient, HTTPResponse
from tornado.platform.asyncio import AsyncIOMainLoop, to_asyncio_future
from tornado.websocket import websocket_connect, WebSocketClientConnection

from wdom.misc import install_asyncio
from wdom import options
from wdom.window import customElements
from wdom.element import Element
from wdom import server

driver = webdriver.Firefox
local_webdriver = None
remote_webdriver = None
browser_implict_wait = 0
logger = logging.getLogger(__name__)

def reset():
    '''Reset wdom objects. This function clear all connections, elements, and
    resistered custom elements. This function also makes new
    document/application and set them.
    '''
    from wdom.document import get_new_document, set_document
    set_document(get_new_document())
    from wdom.server import _tornado
    _tornado.connections.clear()
    _tornado.set_application(_tornado.Application())
    try:
        from wdom.server import _aiohttp
        _aiohttp.connections.clear()
        _aiohttp.set_application(_aiohttp.Application())
    except ImportError:
        pass
    Element._elements_with_id.clear()
    Element._elements.clear()
    customElements.clear()

def suppress_logging():
    options.root_logger.removeHandler(options._log_handler)
    options.root_logger.addHandler(logging.NullHandler())

class TestCase(unittest.TestCase):
    '''Base class for testing wdom modules. This class is a sub class of the
    ``unittest.TestCase``. After all test methods, reset wdom's global objects
    like document, application, and elements. If you use ``tearDown`` method,
    do not forget to call ``super().tearDown()``.

    If you want to reuse document/application object in your test class, please
    set them in each setup phase as follow::

        @classmethod
        def setUpClass(cls):
            cls.your_doc = get_document()
            cls.your_app = get_app()

        def setUp(self):
            from wdom.document import set_document
            from wdom.server import set_application
            set_document(self.your_doc)
            set_application(self.your_app)
    '''
    def tearDown(self):
        reset()
        super().tearDown()

    def assertIsTrue(self, bl):
        self.assertIs(bl, True)

    def assertIsFalse(self, bl):
        self.assertIs(bl, False)

class HTTPTestCase(TestCase):
    '''For http/ws connection test.'''
    _server_started = False

    def start(self):
        with self.assertLogs('wdom', 'INFO'):
            self.server = server.start_server(port=0)
        self.port = self.server.port
        self.url = 'http://localhost:{}'.format(self.port)
        self.ws_url = 'ws://localhost:{}'.format(self.port)
        self._server_started = True

    def tearDown(self):
        if self._server_started:
            with self.assertLogs('wdom', 'INFO'):
                server.stop_server(self.server)
            self._server_started = False
        super().tearDown()

    @asyncio.coroutine
    def fetch(self, url: str, encoding: str = 'utf-8') -> HTTPResponse:
        '''Fetch url. Response body is decoded by ``encoding`` and set
        ``text`` property of the response. If failed to decode, ``text``
        property will set to ``None``.
        '''
        response = yield from to_asyncio_future(
            AsyncHTTPClient().fetch(url, raise_error=False))
        try:
            response.text = response.body.decode(encoding)
        except UnicodeDecodeError:
            response.text = None
        return response

    @asyncio.coroutine
    def ws_connect(self, url: str, _retry=0,
                   _max=100 if os.environ.get('TRAVIS') else 20,
                   _wait=0.2 if os.environ.get('TRAVIS') else 0.05
                   ) -> WebSocketClientConnection:
        '''Make WebSocket connection to the url.'''
        try:
            ws = yield from to_asyncio_future(websocket_connect(url))
        except ConnectionRefusedError:
            if _retry <= _max:
                yield from asyncio.sleep(_wait)
                ws = yield from self.ws_connect(url, _retry+1, _max, _wait)
            else:
                raise ConnectionRefusedError(
                    'WebSocket connection refused: {}'.format(url))
        return ws

def start_webdriver():
    '''Start WebDriver and set implicit_wait if it is not started.'''
    global local_webdriver
    if local_webdriver is None:
        local_webdriver = driver()
        if browser_implict_wait:
            local_webdriver.implicitly_wait(browser_implict_wait)

def close_webdriver():
    '''Close WebDriver.'''
    global local_webdriver
    if local_webdriver is not None:
        local_webdriver.close()
        local_webdriver = None

def get_webdriver():
    '''Return WebDriver of current process. If it is not started, start and
    return it.'''
    if globals().get('local_webdriver') is None:
        start_webdriver()
    return local_webdriver

def _clear():
    global conn, wd_conn, browser_manager, remote_webdriver
    conn = None
    wd_conn = None
    browser_manager = None
    remote_webdriver = None

def start_remote_browser():
    '''Start WebDriver on remote process.'''
    global browser_manager, conn, wd_conn
    conn, wd_conn = Pipe()

    def start_browser():
        global wd_conn
        bc = BrowserController(wd_conn)
        bc.run()

    browser_manager = Process(target=start_browser)
    browser_manager.start()

def close_remote_browser():
    '''Terminate browser process.'''
    global conn, browser_manager
    conn.send({'target': 'process', 'method': 'quit'})
    time.sleep(0.3)
    logger.info('\nRemote Browser closed')
    conn.close()
    if browser_manager is not None:
        browser_manager.terminate()
    _clear()

def get_remote_browser():
    '''Start new WebDriver for remote process.'''
    global remote_webdriver
    if remote_webdriver is None:
        remote_webdriver = driver()
        if browser_implict_wait:
            remote_webdriver.implicitly_wait(browser_implict_wait)
        return remote_webdriver
    else:
        return remote_webdriver

class BrowserController:
    '''Class to run webdriver in different proceess. Inter-process
    communication is done via Pipe.
    '''
    _select_methods = [s for s in dir(Select) if not s.startswith('_')]

    def __init__(self, conn):
        '''Set up connection and start webdriver. ``conn`` is a one end of
        ``Pipe()``, which is used the inter-process communication.
        '''
        self.conn = conn
        self.wd = get_remote_browser()
        self.element = None

    def set_element_by_id(self, id):
        '''Find element with ``id`` and set it as operation target. When
        successfully find the element, send ``True``. If failed to find the
        element, send message ``Error NoSuchElement: {{ id }}``.'''
        try:
            self.element = self.wd.find_element_by_css_selector(
                '[rimo_id="{}"]'.format(id))
            return True
        except NoSuchElementException:
            return 'Error NoSuchElement: ' + id

    def quit(self, *args):
        self.wd.quit()
        return 'closed'

    def close(self, *args):
        self.wd.close()
        return 'closed'

    def _execute_method(self, method, args):
        if isinstance(method, (FunctionType, MethodType)):
            self.conn.send(method(*args))
        else:
            # not callable, just send it back
            self.conn.send(method)

    def run(self):
        '''Running process. Wait message from the other end of the connection,
        and when gat message, execute the method specified by the message.
        The message is a python's dict, which must have ``method`` field.
        '''
        while True:
            req = self.conn.recv()
            target = req.get('target', '')
            method_name = req.get('method', '')
            args = req.get('args', [])
            if target == 'process':
                method = getattr(self, method_name)
            elif target == 'browser':
                method = getattr(self.wd, method_name)
            elif target == 'element':
                if self.element is None:
                    # Element must be set
                    self.conn.send('Error: No Element Set')
                    continue
                if (method_name in self._select_methods and
                        self.element.tag_name.lower() == 'select'):
                    s = Select(self.element)
                    method = getattr(s, method_name)
                else:
                    method = getattr(self.element, method_name)
            self._execute_method(method, args)

def wait_for():
    '''Wait the action doing in the browser process, and afer finish it,
    return the value.'''
    return asyncio.get_event_loop().run_until_complete(wait_coro())

@asyncio.coroutine
def wait_coro():
    while True:
        state = conn.poll()
        if state:
            res = conn.recv()
            return res
        else:
            yield from asyncio.sleep(0)
            continue

def _get_properties(cls):
    props = set()
    for k, v in vars(cls).items():
        if not isinstance(v, (FunctionType, MethodType)):
            props.add(k)
    return props

class Controller:
    target = None
    properties = set()

    def __getattr__(self, attr: str):
        global conn

        def wrapper(*args):
            conn.send({'target': self.target, 'method': attr, 'args': args})
            res = wait_for()
            if isinstance(res, str):
                if res.startswith('Error NoSuchElement'):
                    raise NoSuchElementException(res)
                elif res.startswith('Error'):
                    raise ValueError(res)
            return res
        if attr in self.properties:
            return wrapper()
        else:
            return wrapper

class ProcessController(Controller):
    target = 'process'

class RemoteBrowserController(Controller):
    target = 'browser'
    properties = _get_properties(WebDriver)

class RemoteElementController(Controller):
    target = 'element'
    properties = _get_properties(WebElement)

class RemoteBrowserTestCase:
    '''This class is **Experimental**.

    Utility class for testing apps with webdriver in another process. Mainly
    used for development and test of wdom library itself. This class does not
    support all methods provided by selenium.webdriver, but maybe enough.

    Usage:

    Make subclass and override ``get_app`` method to return app (instance of
    ``wdom.server.Application`` or ``tornado.web.Application``), which you want
    to test.
    '''
    wait_time = 0.2 if os.environ.get('TRAVIS', False) else 0.05

    def start(self):
        self._prev_logging = options.config.logging
        options.config.logging = 'warn'
        self.proc = ProcessController()
        self.browser = RemoteBrowserController()
        self.element = RemoteElementController()
        try:
            self.server = server.start_server(port=0)
        except OSError:
            self.wait(0.2)
            self.server = server.start_server(port=0)
        self.address = self.server.address
        self.url = 'http://{0}:{1}/'.format(self.address, self.port)
        self.wait()
        self.browser.get(self.url)
        self.wait()

    def tearDown(self):
        options.config.logging = self._prev_logging
        self.stop_server()
        self.wait()
        sys.stdout.flush()
        sys.stderr.flush()
        super().tearDown()

    @property
    def port(self) -> int:
        return self.server.port

    def stop_server(self):
        server.stop_server(self.server)

    def wait(self, timeout=None):
        '''Wait until ``timeout``. The default timeout is zero, so wait a
        single event loop. This method does not block the thread, so the server
        in test still can send response before timeout.
        '''
        asyncio.get_event_loop().run_until_complete(
            asyncio.sleep(timeout or self.wait_time))

    def set_element(self, node):
        '''Wrapper method of ``set_element_by_id``. Set the ``node`` as a
        target node of the browser process.'''
        return self.proc.set_element_by_id(node.rimo_id)

class WebDriverTestCase:
    '''Base class for testing UI on browser. This class starts up an HTTP
    server on a new subprocess.

    Subclasses must override ``get_app()`` method, which returns the
    ``pygmariot.server.Application`` or ``tornado.web.Application`` to be
    tested.
    '''
    wait_time = 0.2 if os.environ.get('TRAVIS', False) else 0.05

    @classmethod
    def setUpClass(cls):
        cls._orig_loop = asyncio.get_event_loop()
        # Need to use different loop for aiohttp after remote_browser tests
        # but I can't understand why...?
        asyncio.set_event_loop(asyncio.new_event_loop())
        # When change default loop, tornado's ioloop needs to be reinstalled
        AsyncIOMainLoop().clear_current()
        AsyncIOMainLoop().clear_instance()
        install_asyncio()

    @classmethod
    def tearDownClass(cls):
        # Set original loop and reinstall to tornado
        asyncio.set_event_loop(cls._orig_loop)
        AsyncIOMainLoop().clear_current()
        AsyncIOMainLoop().clear_instance()
        install_asyncio()

    def start(self):
        self.wd = get_webdriver()

        def start_server(port):
            import asyncio
            from wdom import server
            server.start_server(port=port)
            asyncio.get_event_loop().run_forever()

        self.address = 'localhost'
        self.port = free_port()
        self.url = 'http://{0}:{1}/'.format(self.address, self.port)

        self.server = Process(
            target=start_server,
            args=(self.port, )
        )
        self.server.start()
        self.wait(0.1)
        self.wd.get(self.url)
        self.wait(0.05)

    def tearDown(self):
        '''Terminate server subprocess.'''
        self.server.terminate()
        sys.stdout.flush()
        sys.stderr.flush()
        super().tearDown()

    def get_app(self):
        '''This method should be overridden. Return
        ``pygmariot.server.Application`` or ``tornado.web.Application`` to be
        tested.
        '''
        NotImplementedError

    def wait(self, timeout=None):
        '''Wait until ``timeout``. The default timeout is zero, so wait a
        single event loop.'''
        time.sleep(timeout or self.wait_time)

    def send_keys(self, element, keys: str):
        '''Send ``keys`` to ``element`` one-by-one. Safer than using
        ``element.send_keys`` method.
        '''
        for k in keys:
            element.send_keys(k)
<|endoftext|>


<commit_before>from django.db import models, IntegrityError
from django.contrib.auth.models import User
from django.template.defaultfilters import slugify

class LikeMinded(models.Model):
	user = models.ForeignKey(User, related_name='+')
	likeminded = models.ForeignKey(User, related_name='+')
	priority = models.IntegerField()

	def __unicode__(self):
		return self.user.username + ' => ' + self.likeminded.username + ' ' + str(self.priority)

	class Meta:
		unique_together = ('user', 'likeminded')

class Topic(models.Model):

	def __unicode__(self):
		if not self.id:
			return 'Topic'
		return 'Topic #' + str(self.id)

class BooleanOpinion(models.Model):
	user = models.ForeignKey(User, related_name='+')
	topic = models.ForeignKey(Topic, related_name='boolean_opinions')
	value = models.BooleanField()

	@staticmethod
	def setOpinionForUser(user, topic, value):
		try:
			boolean_opinion = BooleanOpinion.objects.get(user=user, topic=topic)
			boolean_opinion.value = value
			boolean_opinion.save()
		except BooleanOpinion.DoesNotExist:
			boolean_opinion = BooleanOpinion(user=user, topic=topic, value=value)
			try:
				boolean_opinion.save()
			except IntegrityError:
				boolean_opinion = BooleanOpinion.objects.get(user=user, topic=topic)
				boolean_opinion.value = value
				boolean_opinion.save()

	@staticmethod
	def getBestOpinionFor(user, topic):
		"""Returns opinion value (True/False) from
		specific topic that is best match to given
		user. Anonymous user is None.

		If opinion could not be decided, then None
		is returned.
		"""

		# If not anonymous
		if user:
			# Check if user has opinion by himself/herself
			try:
				opinion = BooleanOpinion.objects.filter(topic=topic).get(user=user)
				return opinion.value
			except BooleanOpinion.DoesNotExist:
				pass

			# Go all like-minded users through
			# and check what they think
			likemindeds = LikeMinded.objects.filter(user=user).order_by('priority')
			for likeminded in likemindeds:
				try:
					opinion = BooleanOpinion.objects.filter(topic=topic).get(user=likeminded.likeminded)
					return opinion.value
				except BooleanOpinion.DoesNotExist:
					pass

		# Get average value in case user is anonymous or if
		# nobody of the like-mindeds did not had an opinion.
		opinions = BooleanOpinion.objects.filter(topic=topic)
		if len(opinions) == 0:
			# Unable to decide
			return None
		trues = 0
		for opinion in opinions:
			if opinion.value:
				trues += 1
		return trues*2 >= len(opinions)

	def __unicode__(self):
		result = self.user.username
		if self.value:
			result += ' believes that '
		else:
			result += ' does not believe that '
		result += unicode(self.topic)
		result += ' is true'
		return result

	class Meta:
		unique_together = ('user', 'topic')

class TagCloudGroup(models.Model):
	""" TagCloudGroup is used to retrieve multiple
	clouds that have some specific tag in them.
	"""

	def __unicode__(self):
		if not self.id:
			return 'Tag cloud group'
		return 'Tag cloud group #' + str(self.id)

class Tag(models.Model):
	name = models.CharField(max_length=255)
	slug = models.CharField(max_length=255)
	group = models.ForeignKey(TagCloudGroup, related_name='tags')

	def generateSlug(self):
		"""Forms slug from name.

		If generated slug is not valid, you can call this
		function again and new slug will be generated.
		"""

		# If there is already a slug, then make
		# some number to the end of new slug
		extra_end = ''
		if self.slug:
			m = re.search('-(?P<number>[0-9]+)$', self.slug)
			if m:
				old_num = int(m.groupdict()['number'])
				extra_end = '-' + str(old_num + 1)
			else:
				extra_end = '-0'

		self.slug = slugify(self.name) + extra_end

	def __unicode__(self):
		result = self.name
		if self.group.id:
			result += '/Tag cloud group #' + str(self.group.id)
		return result

	class Meta:
		unique_together = (('name', 'group'), ('slug', 'group'))

class TagCloud(models.Model):
	group = models.ForeignKey(TagCloudGroup, related_name='clouds')

	def addTag(self, tag_name, user):
		# First check if tag already exists in the group.
		# If not, add it there and also slugify it
		tags = self.group.tags.filter(name=tag_name)
		if len(tags) == 0:
			tag = Tag(name=tag_name, slug='', group=self.group)
			while True:
				tag.generateSlug()
				try:
					tag.save()
					break
				except IntegrityError:
					pass
		else:
			tag = tags[0]

		# Ensure there is topic about this tag belonging to this cloud
		try:
			tag_belongs_to = TagBelongsTo.objects.get(tag=tag, cloud=self)
		except TagBelongsTo.DoesNotExist:
			topic = Topic()
			topic.save()
			tag_belongs_to = TagBelongsTo(tag=tag, cloud=self, topic=topic)
			try:
				tag_belongs_to.save()
			except IntegrityError:
				topic.delete()
				tag_belongs_to = TagBelongsTo.objects.get(tag=tag, cloud=self)

		BooleanOpinion.setOpinionForUser(user, tag_belongs_to.topic, True)

	def __unicode__(self):
		if not self.id:
			return 'Tag cloud'
		return 'Tag cloud #' + str(self.id)

class TagBelongsTo(models.Model):
	topic = models.OneToOneField(Topic, related_name='tag_belongs_to')
	tag = models.ForeignKey(Tag, related_name='clouds')
	cloud = models.ForeignKey(TagCloud, related_name='tags')

	def __unicode__(self):
		result = '"' + self.tag.name + '" belongs to Tag cloud'
		if self.cloud.id:
			result += ' #' + str(self.cloud.id)
		return result

	class Meta:
		unique_together = ('tag', 'cloud')

<commit_msg>TagCloud can now return list of tags for specific/anonymous user.<commit_after>from django.db import models, IntegrityError
from django.contrib.auth.models import User
from django.template.defaultfilters import slugify

class LikeMinded(models.Model):
	user = models.ForeignKey(User, related_name='+')
	likeminded = models.ForeignKey(User, related_name='+')
	priority = models.IntegerField()

	def __unicode__(self):
		return self.user.username + ' => ' + self.likeminded.username + ' ' + str(self.priority)

	class Meta:
		unique_together = ('user', 'likeminded')

class Topic(models.Model):

	def __unicode__(self):
		if not self.id:
			return 'Topic'
		return 'Topic #' + str(self.id)

class BooleanOpinion(models.Model):
	user = models.ForeignKey(User, related_name='+')
	topic = models.ForeignKey(Topic, related_name='boolean_opinions')
	value = models.BooleanField()

	@staticmethod
	def setOpinionForUser(user, topic, value):
		try:
			boolean_opinion = BooleanOpinion.objects.get(user=user, topic=topic)
			boolean_opinion.value = value
			boolean_opinion.save()
		except BooleanOpinion.DoesNotExist:
			boolean_opinion = BooleanOpinion(user=user, topic=topic, value=value)
			try:
				boolean_opinion.save()
			except IntegrityError:
				boolean_opinion = BooleanOpinion.objects.get(user=user, topic=topic)
				boolean_opinion.value = value
				boolean_opinion.save()

	@staticmethod
	def getBestOpinionFor(user, topic):
		"""Returns opinion value (True/False) from
		specific topic that is best match to given
		user. Anonymous user is None.

		If opinion could not be decided, then None
		is returned.
		"""

		# If not anonymous
		if user:
			# Check if user has opinion by himself/herself
			try:
				opinion = BooleanOpinion.objects.filter(topic=topic).get(user=user)
				return opinion.value
			except BooleanOpinion.DoesNotExist:
				pass

			# Go all like-minded users through
			# and check what they think
			likemindeds = LikeMinded.objects.filter(user=user).order_by('priority')
			for likeminded in likemindeds:
				try:
					opinion = BooleanOpinion.objects.filter(topic=topic).get(user=likeminded.likeminded)
					return opinion.value
				except BooleanOpinion.DoesNotExist:
					pass

		# Get average value in case user is anonymous or if
		# nobody of the like-mindeds did not had an opinion.
		opinions = BooleanOpinion.objects.filter(topic=topic)
		if len(opinions) == 0:
			# Unable to decide
			return None
		trues = 0
		for opinion in opinions:
			if opinion.value:
				trues += 1
		return trues*2 >= len(opinions)

	def __unicode__(self):
		result = self.user.username
		if self.value:
			result += ' believes that '
		else:
			result += ' does not believe that '
		result += unicode(self.topic)
		result += ' is true'
		return result

	class Meta:
		unique_together = ('user', 'topic')

class TagCloudGroup(models.Model):
	""" TagCloudGroup is used to retrieve multiple
	clouds that have some specific tag in them.
	"""

	def __unicode__(self):
		if not self.id:
			return 'Tag cloud group'
		return 'Tag cloud group #' + str(self.id)

class Tag(models.Model):
	name = models.CharField(max_length=255)
	slug = models.CharField(max_length=255)
	group = models.ForeignKey(TagCloudGroup, related_name='tags')

	def generateSlug(self):
		"""Forms slug from name.

		If generated slug is not valid, you can call this
		function again and new slug will be generated.
		"""

		# If there is already a slug, then make
		# some number to the end of new slug
		extra_end = ''
		if self.slug:
			m = re.search('-(?P<number>[0-9]+)$', self.slug)
			if m:
				old_num = int(m.groupdict()['number'])
				extra_end = '-' + str(old_num + 1)
			else:
				extra_end = '-0'

		self.slug = slugify(self.name) + extra_end

	def __unicode__(self):
		result = self.name
		if self.group.id:
			result += '/Tag cloud group #' + str(self.group.id)
		return result

	class Meta:
		unique_together = (('name', 'group'), ('slug', 'group'))

class TagCloud(models.Model):
	group = models.ForeignKey(TagCloudGroup, related_name='clouds')

	def addTag(self, tag_name, user):
		# First check if tag already exists in the group.
		# If not, add it there and also slugify it
		tags = self.group.tags.filter(name=tag_name)
		if len(tags) == 0:
			tag = Tag(name=tag_name, slug='', group=self.group)
			while True:
				tag.generateSlug()
				try:
					tag.save()
					break
				except IntegrityError:
					pass
		else:
			tag = tags[0]

		# Ensure there is topic about this tag belonging to this cloud
		try:
			tag_belongs_to = TagBelongsTo.objects.get(tag=tag, cloud=self)
		except TagBelongsTo.DoesNotExist:
			topic = Topic()
			topic.save()
			tag_belongs_to = TagBelongsTo(tag=tag, cloud=self, topic=topic)
			try:
				tag_belongs_to.save()
			except IntegrityError:
				topic.delete()
				tag_belongs_to = TagBelongsTo.objects.get(tag=tag, cloud=self)

		BooleanOpinion.setOpinionForUser(user, tag_belongs_to.topic, True)

	def getTagsFor(self, user):
		"""Returns list of tags for specific user.
		User can also be None.
		"""
		all_tags = self.tags.all()
		result = []
		for tag_belongs_to in all_tags:
			topic = tag_belongs_to.topic
			opinion_value = BooleanOpinion.getBestOpinionFor(user, topic)
			if opinion_value:
				result.append(tag_belongs_to.tag)
		return result

	def __unicode__(self):
		if not self.id:
			return 'Tag cloud'
		return 'Tag cloud #' + str(self.id)

class TagBelongsTo(models.Model):
	topic = models.OneToOneField(Topic, related_name='tag_belongs_to')
	tag = models.ForeignKey(Tag, related_name='clouds')
	cloud = models.ForeignKey(TagCloud, related_name='tags')

	def __unicode__(self):
		result = '"' + self.tag.name + '" belongs to Tag cloud'
		if self.cloud.id:
			result += ' #' + str(self.cloud.id)
		return result

	class Meta:
		unique_together = ('tag', 'cloud')

<|endoftext|>