Executor Features

Exception handling

Exception inside @requests decorated functions can be simply raised. The Flow will handle it.

  1. from jina import Executor, requests, Flow
  2. from jina.types.request import Response
  3. class MyExecutor(Executor):
  4. @requests
  5. def foo(self, **kwargs):
  6. raise NotImplementedError('no time for it')
  7. f = Flow().add(uses=MyExecutor)
  8. def print_why(resp: Response):
  9. print(resp.status.description)
  10. with f:
  11. f.post('', on_error=print_why)
  1. [email protected][L]:ready and listening
  2. [email protected][L]:ready and listening
  3. [email protected][I]:🎉 Flow is ready to use!
  4. 🔗 Protocol: GRPC
  5. 🏠 Local access: 0.0.0.0:49242
  6. 🔒 Private network: 192.168.178.31:49242
  7. 🌐 Public address: 217.70.138.123:49242
  8. [email protected][E]:NotImplementedError('no time for it')
  9. add "--quiet-error" to suppress the exception details
  10. Traceback (most recent call last):
  11. File "/Users/hanxiao/Documents/jina/jina/peapods/runtimes/zmq/zed.py", line 250, in _msg_callback
  12. processed_msg = self._callback(msg)
  13. File "/Users/hanxiao/Documents/jina/jina/peapods/runtimes/zmq/zed.py", line 236, in _callback
  14. msg = self._post_hook(self._handle(self._pre_hook(msg)))
  15. File "/Users/hanxiao/Documents/jina/jina/peapods/runtimes/zmq/zed.py", line 203, in _handle
  16. peapod_name=self.name,
  17. File "/Users/hanxiao/Documents/jina/jina/peapods/runtimes/request_handlers/data_request_handler.py", line 163, in handle
  18. field='groundtruths',
  19. File "/Users/hanxiao/Documents/jina/jina/executors/__init__.py", line 200, in __call__
  20. self, **kwargs
  21. File "/Users/hanxiao/Documents/jina/jina/executors/decorators.py", line 105, in arg_wrapper
  22. return fn(*args, **kwargs)
  23. File "/Users/hanxiao/Documents/jina/toy43.py", line 9, in foo
  24. raise NotImplementedError('no time for it')
  25. NotImplementedError: no time for it
  26. NotImplementedError('no time for it')

Use Executor out of Flow

Executor object can be used directly just like a regular Python object. For example,

  1. from jina import Executor, requests, DocumentArray, Document
  2. class MyExec(Executor):
  3. @requests
  4. def foo(self, docs, **kwargs):
  5. for d in docs:
  6. d.text = 'hello world'
  7. m = MyExec()
  8. da = DocumentArray([Document(text='test')])
  9. m.foo(da)
  10. print(da)
  1. DocumentArray has 1 items:
  2. {'id': '20213a02-bdcd-11eb-abf1-1e008a366d48', 'mime_type': 'text/plain', 'text': 'hello world'}

This is useful in debugging an Executor.

Gracefully close Executor

You might need to execute some logic when your executor’s destructor is called. For example, you want to persist data to the disk (e.g. in-memory indexed data, fine-tuned model,…). To do so, you can overwrite the method close and add your logic.

  1. from jina import Executor, requests, Document, DocumentArray
  2. class MyExec(Executor):
  3. @requests
  4. def foo(self, docs, **kwargs):
  5. for doc in docs:
  6. print(doc.text)
  7. def close(self):
  8. print("closing...")
  9. with MyExec() as executor:
  10. executor.foo(DocumentArray([Document(text='hello world')]))
  1. hello world
  2. closing...

YAML interface

An Executor can be loaded from and stored to a YAML file. The YAML file has the following format:

  1. jtype: MyExecutor
  2. with:
  3. parameter_1: foo
  4. parameter_2: bar
  5. metas:
  6. name: MyExecutor
  7. description: "MyExecutor does a thing to the stuff in your Documents"
  8. workspace: workspace
  9. py_modules:
  10. - executor.py
  11. requests:
  12. index: MyExecutor_index_method
  13. search: MyExecutor_search_method
  14. random: MyExecutor_other_method
  • jtype is a string. Defines the class name, interchangeable with bang mark !;

  • with is a map. Defines kwargs of the class __init__ method

  • metas is a dictionary. It defines the meta information of that class. It contains the following fields:

    • name is a string. Defines the name of the executor;

    • description is a string. Defines the description of this executor. It will be used in automatic docs UI;

    • workspace is a string. Defines the workspace of the executor;

    • py_modules is a list of strings. Defines the Python dependencies of the executor;

  • requests is a map. Defines the mapping from endpoint to class method name;

Load and save Executor config

You can use class method Executor.load_config and object method exec.save_config to load and save YAML config:

  1. from jina import Executor
  2. class MyExecutor(Executor):
  3. def __init__(self, bar: int, **kwargs):
  4. super().__init__(**kwargs)
  5. self.bar = bar
  6. def foo(self, **kwargs):
  7. pass
  8. y_literal = """
  9. jtype: MyExecutor
  10. with:
  11. bar: 123
  12. metas:
  13. name: awesomeness
  14. description: my first awesome executor
  15. requests:
  16. /random_work: foo
  17. """
  18. exec = Executor.load_config(y_literal)
  19. exec.save_config('y.yml')
  20. Executor.load_config('y.yml')

Meta attributes

By default, an Executor object contains two collections of attributes: .metas and .runtime_args. They are both in SimpleNamespace type and contain some key-value information. However, they are defined differently and serve different purposes.

  • .metas are statically defined. “Static” means, e.g. from hard-coded value in the code, from a YAML file.

  • .runtime_args are dynamically determined during runtime. Means that you don’t know the value before running the Executor, e.g. shard_id, replicas. Those values are often related to the system/network environment around the Executor, and less about the Executor itself. They are usually set with the add() method. See the list of options here.

The following fields are valid for metas and runtime_args:

AttributeFields
.metas (static values from hard-coded values, YAML config)name, description, py_modules, workspace
.runtime_args (runtime values from its containers, e.g. Runtime, Pod, Deployment)name, workspace, shard_id, replicas, ‘shards’

Note

The YAML API will ignore .runtime_args during save and load as they are not statically stored

Hint

For any other parametrization of the Executor, you can still access its constructor arguments (defined in the class __init__) and the request parameters

Note

workspace will be retrieved from either metas or runtime_args, in that order

Workspace

Executor’s workspace is inherited according to the following rule (OR is a python or, i.e. first thing first, if NA then second):

../../../_images/workspace-inherit.svg