예제 #1
0
    async def send_message(self,
                           message: Union[str, int, float],
                           connection: Union[Connection,
                                             Iterable[Connection]] = None,
                           blocking: bool = False) -> bool:
        '''
        Send a message to either one or more connections.
        This function by default is a fire & forget method, but when set
        to `blocking=True` waits if the message could be dispatched to all
        recipients. Only the latter returns a real (boolean) result, 
        telling if the message could be successfully written to (not received by) 
        the client(s)
        '''
        try:
            # Get affected connections
            affected = [connection
                        ] if not checker.isIterable(connection) else connection
            connections = [c for c in affected if not c.state['closed']]

            # Encode and check message
            message = str(message)
            if self.lines and not message.endswith(self.line_separator):
                message += self.line_separator
            message = message.encode()
            if len(message) == 0:
                return False
            if self.limit and len(message) > self.limit:
                await self.handleLimitExceedance(connection, message, False)
                return False

            # Dispatch message to affected connections as long as we're not shutting down
            if len(connections) > 0 and not self._shutdown:
                writer = asyncio.gather(
                    *[self._dispatchMessage(message, c) for c in connections],
                    loop=self.loop,
                    return_exceptions=True)

                # Adding as many callbacks as connections, so the future (writer) gets removed from all connections
                def cancel_writer(connection, writer):
                    try:
                        connection.write_handlers.remove(writer)
                    except:
                        pass

                for c in connections:
                    # We use functools to properly bind the callback to the connection
                    writer.add_done_callback(
                        functools.partial(cancel_writer, c))
                    c.write_handlers.add(writer)

                # Return behavior
                if not blocking:
                    return True
                else:
                    await writer
                    return all(r for r in writer.result())
            else:
                return False
        except:
            return False
예제 #2
0
 def connected(self):
     '''Shows if a connection is established'''
     if checker.isExactType(self.__connection,
                            'rpyc.core.protocol.Connection'):
         if hasattr(self.__connection, '_closed'):
             if self.__connection._channel:
                 return self.__connection._channel.stream.closed is not True
             else:
                 return False
         else:
             return False
     else:
         return False
예제 #3
0
def run_concurrently(function: Callable,
                     tasks: Iterable[Any],
                     *args,
                     max_threads: int = None,
                     timeout: int = None) -> None:
    '''
    This method runs a given non-coroutine function concurrently for each task item by the help
    of the asyncio module and a concurrent executor. The executor creates a thread
    for each task item if no max_threads is set and cleans up its resources afterwards.
    :param func function: The function to run
    :param Iterable tasks: A list/tuple of items for each of them we run the function.
    The items will also serve as argument to the function. Each further *args parameter will be used for the function
    :param int max_threads: Limit the number of threads. By default we create a thread for each item
    :param int timeout: Limit the execution time by a timeout
    '''
    # Check requirements: A function and a non-empty list
    if checker.is_function(function) and checker.is_iterable(
            tasks) and len(tasks) > 0:
        # First get this threads loop or create a new one
        try:
            loop = asyncio.get_event_loop()
        except RuntimeError:
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
            asyncio.set_event_loop(loop)

        # Create a worker pool for each "task" in the list (which are to be executed parallel
        workers: List[Coroutine[ThreadPoolExecutor, Callable, Tuple]] = []
        with ThreadPoolExecutor(max_workers=max_threads if checker.is_integer(
                max_threads) else len(tasks)) as executor:
            # Create coroutine threads
            for t in tasks:
                workers.append(
                    loop.run_in_executor(executor, function,
                                         *tuple([t] + list(args))))

            # Run all release operations in parallel
            if workers:
                loop.run_until_complete(asyncio.wait(workers, timeout=timeout))
예제 #4
0
 def exposed_printMessage(self, message):
     '''
     Prints a message sent to the stout of the client
     :param str message: The message
     :raises ValueError: If the message exceeds 160 characters
     :raises TypeError: If the message is not a basestring
     '''
     max_length=160
     if checker.isString(message):
         if(len(message) <= max_length):
             print(message)
         else:
             raise ValueError(f'Message exceeds {max_length} characters')
     else:
         raise TypeError('Not a string')
예제 #5
0
 def testAlphaStrings(self):
     tests = [
         ['Hello', True],
         ['Hello World', False],
         [b'Hello', True],
         ['Mažasančiųplūduriuojantisežero', True],
         ['小鴨子湖面上漂浮著', True],
         ['1小鴨子湖面上漂浮著', False],
         ['user-name', False],
         [1, False],
         [1.2, False],
         [{}, False],
     ]
     for t in tests:
         logger.debug(
             f'String "{t[0]}" is{" " if t[1] else " not "}alphabetical')
         if t[1]:
             self.assertTrue(
                 checker.isAlpha(t[0]),
                 f'Testing alphanumeric strings failed for "{t[0]}"')
         else:
             self.assertFalse(
                 checker.isAlpha(t[0]),
                 f'Testing alphanumeric strings failed for "{t[0]}"')
예제 #6
0
    def registerStore(self, store: Union[Dict[str, Any],
                                         Type[DataStore]]) -> bool:
        '''
        Registers a data store to the manager
        :param [dict/py:class::freedm.data.objects.DataStore] store: The data store or a configuration dictionary
        :returns: ``True`` if the store could be registered
        :rtype: bool
        '''
        # Check store
        if (checker.isDict(store)):
            try:
                store = DataStore(**store)
            except TypeError as e:
                self.logger.warn(
                    f'Cannot create data store instance from invalid parameters ({e})'
                )
        elif not isinstance(store, DataStore):
            self.logger.warn(
                f'Cannot register invalid store class "{store}" in data manager "{self}"'
            )

        # Add store
        if not store.alias in self.__stores.keys():
            # Add the store and set the store's path if empty
            self.__stores[store.alias] = store
            if store.path is None:
                store.path = self.path
                # Warn the user from registering same-type stores with the same path
                if len(self.__stores) >= 2:
                    self.logger.warn(
                        f'Registered store uses "{self.path}". Beware of registering same-type stores with an equal path'
                    )
            # Update setters & getters
            self.__updateSettersGetters()
            self.logger.debug(
                f'Registered data store "{store}" in data manager "{self}"')
            return True
        else:
            self.logger.warn(
                f'Cannot register a store with name "{store.alias}" twice in data manager "{self}"'
            )
        # If store could not be registered
        return False
예제 #7
0
 def exposed_getDaemonInfo(self):
     '''
     Return the most important information regarding the daemon instance,
     thus as the class, role, state, etc.
     :return: The daemon instance data
     :rtype: dict
     '''
     if self.daemon:
         return dict(
             type    = checker.getExactType(self.daemon),
             role    = self.daemon.role.capitalize(),
             state   = self.daemon.state.capitalize(),
             pid     = self.daemon.pid,
             version = self.daemon.version,
             address = self.daemon.host if not '127.0.0.1' else 'localhost',
             port    = self.daemon.port
             )
     else:
         return {}
예제 #8
0
    def __init__(self,
                 loop: Optional[Type[asyncio.AbstractEventLoop]] = None,
                 limit: Optional[int] = None,
                 chunksize: Optional[int] = None,
                 max_connections: Optional[int] = None,
                 mode: Optional[ConnectionType] = None,
                 protocol: Optional[Protocol] = None) -> None:

        self.logger = logging.getLogger()
        self.loop = loop or getLoop()
        self.limit = limit
        self.chunksize = chunksize
        self.mode = mode
        self.protocol = protocol

        if not self.name:
            self.name = self.__class__.__name__
        if not self._connection_pool:
            self._connection_pool = ConnectionPool()
        if checker.isInteger(max_connections):
            self._connection_pool.max = max_connections
예제 #9
0
    def __updateSettersGetters(self) -> None:
        '''
        Creates or removes getter/setter methods on the data manager for each registered store
        '''
        # First remove any obsolete getters & setters ...
        attrs = vars(self)
        obsolete = []
        for v in attrs.keys():
            if (v.startswith('get')
                    or v.startswith('set')) and checker.isFunction(attrs[v]):
                try:
                    self.__stores[v[3:]]
                except:
                    obsolete.append(v)
        for o in obsolete:
            delattr(self, o)

        # ... then add getters & setters for all current stores
        created = []
        for alias in self.__stores:
            # Create Getter & Setter
            if not hasattr(self, f'get{alias}') and not hasattr(
                    self, f'set{alias}'):
                setattr(self,
                        f'get{alias}',
                        lambda token, default=None, store=alias: self.
                        __getStoreValue(token, default, store))
                setattr(self,
                        f'set{alias}',
                        lambda token, value=None, store=alias: self.
                        __setStoreValue(token, value, store))
                created.append(alias)

        # Log is we created setter/getter
        for c in created:
            self.logger.debug(
                f'Added data getters & setters for store "{self.__stores[c]}" (Use "get{c}" & "set{c}" for access)'
            )
예제 #10
0
    def __init__(self, path: Union[str, Path] = None):
        try:
            # Set the data location
            if path is not None:
                # Make sure we work with an absolute path
                path = Path(path).resolve() if checker.isString(path) else path
                # Check path availability
                if path is False:
                    pass
                elif os.path.exists(path) and os.path.isdir(
                        path) and os.access(path, os.W_OK):
                    self.__path = path
                else:
                    self.logger.critical(
                        f'Cannot access provided storage path "{path}"')
            else:
                if not os.path.exists(self.__path) or not os.path.isdir(
                        self.__path) or not os.access(self.__path, os.W_OK):
                    self.logger.critical(
                        f'Cannot access default storage path "{self.__path}"')

        except Exception as e:
            self.logger.warn(f'Failed to initialize DataManager ({e})')
        self.logger.debug(f'Using data storage location "{self.__path}"')
예제 #11
0
 def logger(self, logger):
     if checker.isExactType(logger,
                            'logging.Logger') or checker.isExactType(
                                logger, 'logging.RootLogger'):
         self.__logger = logger
예제 #12
0
    def _loadDomain(self, domain: str, path: str) -> None:
        try:
            # Check that we have a FS path for the INI file
            if not path: raise UserWarning(f'No INI file location provided!')

            # The backend file
            inifile = path.joinpath(f'{domain}.{self.filetype}')

            # Read the data from file & convert values
            parser = SafeConfigParser()
            parser.read(inifile)
            inidata = {}
            for s in parser.sections():
                items = []
                # Handle each item
                for item in parser.items(s):
                    # Try to convert item values automatically
                    try:
                        k, v = item
                        if v is not None:
                            # Get integers from config file
                            if v.isdigit():
                                v = parser.getint(s, k)
                            # Get floats from config file
                            elif checker.isFloat(v):
                                v = parser.getfloat(s, k)
                            # Get booleans from config file
                            elif v in ('1', 'yes', 'true', 'on', '0', 'no',
                                       'false', 'off'):
                                v = parser.getboolean(s, k)
                            # Get nested JSON objects from config file
                            elif v.startswith('{') and v.endswith('}'):
                                v = json.loads(v)
                            # Get nested JSON objects from config file
                            elif v.startswith('[') and v.endswith(']'):
                                v = json.loads(v)
                            # Get wrapped values as string
                            elif v[0] in ('"', '\'') and v[-1] in ('"', "'"):
                                v = v[1:-1]
                        items.append((k, v))
                    except Exception as e:
                        print(e.__class__.__name__)
                        if e.__class__.__name__ in ('json.JSONDecodeError',
                                                    'ValueError'):
                            raise UserWarning(
                                f'File "{inifile}" cannot be JSON-decoded ({e})!'
                            )
                        else:
                            items.append(item)
                # Re-add all items & sections
                inidata.update({s: dict(items)})

            # Create data object
            if isinstance(inidata, dict):
                data = DataObject(backend=inifile, **inidata)
                return data
        except FileNotFoundError:
            raise UserWarning(
                f'File "{inifile}" does not exist. Please create this file!')
        except Exception as e:
            raise e