This file is indexed.

/usr/lib/python3/dist-packages/signaller.py is in python3-signaller 1.1.0-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
"""Signals and slots implementation with asyncio support

Slots can be functions, methods or coroutines. Weak references are used by default.
If slot is coroutine, it will be scheduled to run asynchronously with ``asyncio.async()``
(but you must run event loop by yourself).

You can also run blocking functions asynchronously by specifying ``force_async=True`` when
connecting signal to slot (it will only apply to that slot) or when creating signal (it will
apply to all connected slots). ThreadPoolExecutor with 5 worker threads is used by default,
but it can be changed when creating signal with ``executor`` argument.
"""

import asyncio, concurrent.futures, weakref, inspect, logging
from functools import wraps

logger = logging.getLogger(__name__)


def autoconnect(cls):
    """Class decorator for automatically connecting instance methods to signals"""
    old_init = cls.__init__

    @wraps(old_init)
    def new_init(self, *args, **kwargs):
        for name, method in inspect.getmembers(self, predicate=inspect.ismethod):
            if hasattr(method, '_signals'):
                for sig, sig_kwargs in method._signals.items():
                    sig.connect(method, **sig_kwargs)
        old_init(self, *args, **kwargs)

    cls.__init__ = new_init
    return cls


class Reference:
    """Weak or strong reference to function or method"""
    def __init__(self, obj, callback=None, weak=True, force_async=False):
        if not callable(obj):
            raise TypeError('obj has to be callable')

        self.force_async = force_async
        self._weak = weak
        self._alive = True
        self._hash = obj.__hash__()
        self._repr = obj.__repr__()

        if self.weak:
            if inspect.ismethod(obj):
                self._ref = weakref.WeakMethod(obj, self._wrap_callback(callback))
            else:
                self._ref = weakref.ref(obj, self._wrap_callback(callback))
        else:
            self._ref = obj

    def _wrap_callback(self, callback):
        """Wrap callback to be called with reference to ourselves, not underlying weakref object"""
        def wrapper(obj):
            logger.debug('Object {} has been deleted'.format(self._repr))
            self._alive = False
            if callback is not None:
                return callback(self)
        return wrapper

    @property
    def weak(self):
        """Returns True if this is weak reference"""
        return self._weak

    @property
    def alive(self):
        """Returns True if underlying weak reference is still alive"""
        return self._alive

    def getobject(self):
        """Returns underlying object"""
        return self._ref() if self.weak else self._ref

    def __call__(self, *args, **kwargs):
        return self.getobject()(*args, **kwargs)

    def __hash__(self):
        return self._hash

    def __eq__(self, other):
        return self.__hash__() == other.__hash__()

    def __repr__(self):
        return '<Reference ({}) to {}{}>'.format(
            'weak' if self.weak else 'strong',
            self._repr,
            ' (dead)' if not self.alive else ''
        )


class Signal:
    """Signal emitter"""
    def __init__(self, name='', loop=None, force_async=False, executor=None):
        self.name = name
        self.loop = loop
        self.force_async = force_async
        self.executor = executor or concurrent.futures.ThreadPoolExecutor(max_workers=5)
        self._slots = set()

    def emit(self, *args, **kwargs):
        """Emit signal (call all connected slots)"""
        logger.info('Emitting signal {}'.format(self))
        for ref in self._slots:
            if asyncio.iscoroutinefunction(ref.getobject()):
                logger.debug('Scheduling coroutine {}'.format(ref))
                asyncio.async(ref(*args, **kwargs), loop=self.loop)
            else:
                if self.force_async or ref.force_async:
                    logger.debug('Calling slot {} asynchronously (in executor {})'.format(
                        ref, self.executor
                    ))
                    self.executor.submit(ref, *args, **kwargs)
                else:
                    logger.debug('Calling slot {}'.format(ref))
                    ref(*args, **kwargs)

    def clear(self):
        """Disconnect all slots"""
        logger.info('Disconnecting all slots from signal {}'.format(self))
        self._slots.clear()

    def connect(self, *args, weak=True, force_async=False):
        """Connect signal to slot (can be also used as decorator)"""
        def wrapper(func):
            args = inspect.getfullargspec(func).args
            if inspect.isfunction(func) and args and args[0] == 'self':
                logger.debug('Marking instance method {} for autoconnect to signal {}'.format(
                    func, self
                ))
                if not hasattr(func, '_signals'):
                    func._signals = {}
                func._signals[self] = {'weak': weak, 'force_async': force_async}
            else:
                logger.info('Connecting signal {} to slot {}'.format(self, func))
                self._slots.add(
                    Reference(func, callback=self.disconnect, weak=weak, force_async=force_async)
                )
            return func

        # If there is one (and only one) positional argument and this argument is callable,
        # assume it is the decorator (without any optional keyword arguments)
        if len(args) == 1 and callable(args[0]):
            return wrapper(args[0])
        else:
            return wrapper

    def disconnect(self, slot):
        """Disconnect slot from signal"""
        try:
            logger.info('Disconnecting slot {} from signal {}'.format(slot, self))
            self._slots.remove(slot)
        except KeyError:
            logger.warning('Slot {} is not connected!'.format(slot))
            pass

    def __repr__(self):
        return '<Signal {} at {}>'.format(
            '\'{}\''.format(self.name) if self.name else '<anonymous>',
            hex(id(self))
        )