Пример #1
0
 async def target(self, event, pipeline, context):
     command_name = await self.command_name.read(event, pipeline, context)
     ebnf = await self.ebnf.read(event, pipeline, context)
     #  ---
     if command_name:
         try:
             command = m42pl.command(command_name)
             source = {
                 command_name: command
             }
         except Exception:
             source = {}
     else:
         source = m42pl.commands.ALIASES
     # ---
     for alias, command in source.items():
         yield derive(event, data={
             'command': {
                 'alias': alias,
                 'aliases': command._aliases_,
                 'schema': command._schema_,
                 'about': command._about_,
                 'syntax': command._syntax_,
                 'type': list(filter(None, map(lambda t: issubclass(command, t) and t.__name__ or None, self.types)))[0],
                 'ebnf': ebnf is True and getattr(command, '_ebnf_', '') or ''
             }
         })
Пример #2
0
def run_pipeline(context: str, event: str, chan_read: Queue, chan_write: Queue,
                 chunk: int, chunks: int, modules: dict):
    """Runs a split pipeline.

    :param context:     Source context as JSON string
    :param event:       Source event as JSON string
    :param chan_read:   Multiprocessing queue output (read from);
    :param chan_write:  Multiprocessing queue input (write to);
    :param chunk:       Current chunk number (starts at 0)
    :param chunks:      Total number of chunks (i.e. number of parallel
                        split pipelines / process)
    :param modules:     Modules names and paths to load
    """
    async def run(pipeline, context, event):
        async with context.kvstore:
            async for _ in pipeline(context, event):
                pass

    # Load missing modules
    m42pl.load_modules(names=modules['names'], paths=modules['paths'])
    # Build local context and event from seralized instances
    context = Context.from_dict(json.loads(context))
    # event = Event.from_dict(json.loads(event))
    event = json.loads(event)
    # Get main pipeline
    pipeline = context.pipelines['main']
    # ---
    # Customize main pipeline to read & write to the multiprocessing pipe
    # Intermediate or last command: read input from MPI pipe
    if chan_read:
        pipeline.commands = [
            m42pl.command('mpi-receive')(chan_read),
        ] + pipeline.commands
    # First or intermediate command: write output to MPI pipe
    if chan_write:
        pipeline.commands.append(m42pl.command('mpi-send')(chan_write))
    # ---
    # Rebuild and reconfigure pipeline
    pipeline.build()
    pipeline.set_chunk(chunk, chunks)
    # ---
    # Close stdout and run
    if chan_write is not None:
        sys.stdout = DevNull()
        sys.stderr = DevNull()
    asyncio.run(run(pipeline, context, event))
Пример #3
0
 def __new__(cls, *args, **kwargs) -> tuple:
     """Returns a new (encode, send) commands tuple.
     """
     self = super(MPISend, cls).__new__(cls)
     self.__init__(*args, **kwargs)
     return (
         # m42pl.command('msgpack')(dest_field=self.msgpack_field),
         m42pl.command('encode')(
             codec='msgpack',
             dest=self.msgpack_field
         ),
         self
     )
Пример #4
0
 def __new__(cls, *args, **kwargs) -> tuple:
     """Returns a new (receive, decode) commands tuple.
     """
     self = super(MPIReceive, cls).__new__(cls)
     self.__init__(*args, **kwargs)
     return (
         self,
         # m42pl.command('msgunpack')(src_field=self.msgpack_field)
         m42pl.command('decode')(
             codec='msgpack',
             src=self.msgpack_field
         )
     )
Пример #5
0
    def from_dict(cls, data: dict) -> object:
        """Returns a new :class:`Pipeline` from a :class:`dict`.

        The source map ``data`` must match with the following map:

        .. code-block:: python

            {
                'commands': [               # Commands list
                    {
                        'alias': '...',     # Name (alias)
                        'args': [],         # Arguments
                        'kwargs': {}        # Keyword arguments
                    },
                    # ...                   # Other attrs are ignored
                ]
            }

        **Important**: This function does not invoke the commands'
        `__new__` method: this means that :param:`data` must have been
        generated from an already built pipeline, i.e. whoms commands
        have been instanciated.
        The underlying reason is that commands may overload their
        `__new__` method to return more than one class instance.
        
        :param data:  Source pipeline as :class:`dict`
        """
        # Build the commands list
        commands = []
        for command in data['commands']:
            commands.append(object.__new__(m42pl.command(command['alias'])))
            commands[-1].__init__(*command.get('args', []),
                                  **command.get('kwargs', {}))
        # Remove original commands list from dict
        data.pop('commands')
        # Builds and returns a new pipeline
        # return cls( { **{'commands': commands}, **dc} )
        return cls(commands=commands)
Пример #6
0
        def command(self, items):

            def configure_command(cmd, items):
                if isinstance(cmd, (list, tuple)):
                    for c in cmd:
                        configure_command(c, items)
                else:
                    cmd._lncol_ = items[0].line, items[0].column
                    # --- Depreciation warning ---
                    # cmd._offset_ = items[0].pos_in_stream
                    cmd._offset_ = items[0].start_pos
                    cmd._name_ = command_name

            # Extract command name and body
            command_name = str(items[0])
            command_body = ' '.join(
                filter(
                    None,
                    len(items) > 1 and items[1:] or []
                )
            )
            # Instanciate new command
            try:
                cmd = m42pl.command(command_name).from_script(command_body)
            except Exception as error:
                raise errors.ScriptError(
                    line=items[0].line,
                    column=items[0].column,
                    # --- Depreciation warning ---
                    # offset=items[0].pos_in_stream,
                    offset=items[0].start_pos,
                    message=str(error)
                )
            # Setup command instance
            configure_command(cmd, items)
            # Done
            return cmd
Пример #7
0
 def __call__(self, args):
     super().__call__(args)
     with open(args.source) as fd:
         print(m42pl.command(self.command)(fd.read())())
Пример #8
0
    def __init_subclass__(cls):
        def new_testcase(test_script: TestScript):
            """Generic test case factory.

            Returns a new test case method from a :class:`TestScript`
            instance.
            """
            def testcase(self):
                """Tests command execution and results.

                This generic test case method performs the following
                operations and tests:

                * Generate the test case source script
                * Initialize M42PL
                * Run the previously generated source script
                * Assert the number of results match the expected
                * Assert the content of results
                """
                # ---
                # Generate the test case source script by merging the
                # test case class's script's snippets and the test
                # script code.
                source = dedent(f'''\
                    {cls.script_begin}
                    {test_script.source}
                    {cls.script_end}
                ''')
                # ---
                # Local copy the test script parameters
                expected = test_script.expected
                fields_in = test_script.fields_in
                fields_out = test_script.fields_out
                first_event = test_script.first_event
                # ---
                # Initialize M42PL
                kvstore = m42pl.kvstore('local')()
                dispatcher = m42pl.dispatcher('local_test')()
                # ---
                # Run the the test script
                results = dispatcher(source, kvstore, first_event)
                # ---
                # Test results length
                self.assertEqual(len(results), len(expected))
                # ---
                # Test results content
                # Events are compared on-by-one with each other from
                # the results set and the expected set.
                for res, exp in zip(results, expected):
                    # Clean up all fields but the selected one
                    for dataset in (res, exp):
                        # Keep only fields named in fields_in
                        if len(fields_in):
                            for field in [
                                    k for k, v in dataset['data'].items()
                                    if k not in fields_in
                            ]:
                                dataset['data'].pop(field)
                        # Remove fields named in fields_out
                        if len(fields_out):
                            for field in fields_out:
                                dataset['data'].pop(field)
                    # Assert equality between result and expected
                    self.assertDictEqual(res['data'], exp['data'])

            # ---
            # Return the generated test case function `testcase`
            return testcase

        # Generated tests methods
        for test_scripts in [cls.expected_success, cls.expected_failure]:
            for test_script in test_scripts:
                setattr(cls, f'test_script_{test_script.name}',
                        new_testcase(test_script))

        # Initialize M42PL and setup command class only if we're
        # targetting a final test case class (having a command alias)
        if len(cls.command_alias):
            m42pl.load_modules()
            cls.command = m42pl.command(cls.command_alias)
Пример #9
0
 def __call__(self, args):
     super().__call__(args)
     print(m42pl.command(args.command)._ebnf_)