Executor Features
Exception handling
Exception inside @requests
decorated functions can be simply raised. The Flow will handle it.
from jina import Executor, requests, Flow
from jina.types.request import Response
class MyExecutor(Executor):
@requests
def foo(self, **kwargs):
raise NotImplementedError('no time for it')
f = Flow().add(uses=MyExecutor)
def print_why(resp: Response):
print(resp.status.description)
with f:
f.post('', on_error=print_why)
[email protected][L]:ready and listening
[email protected][L]:ready and listening
[email protected][I]:🎉 Flow is ready to use!
🔗 Protocol: GRPC
🏠 Local access: 0.0.0.0:49242
🔒 Private network: 192.168.178.31:49242
🌐 Public address: 217.70.138.123:49242
[email protected][E]:NotImplementedError('no time for it')
add "--quiet-error" to suppress the exception details
Traceback (most recent call last):
File "/Users/hanxiao/Documents/jina/jina/peapods/runtimes/zmq/zed.py", line 250, in _msg_callback
processed_msg = self._callback(msg)
File "/Users/hanxiao/Documents/jina/jina/peapods/runtimes/zmq/zed.py", line 236, in _callback
msg = self._post_hook(self._handle(self._pre_hook(msg)))
File "/Users/hanxiao/Documents/jina/jina/peapods/runtimes/zmq/zed.py", line 203, in _handle
peapod_name=self.name,
File "/Users/hanxiao/Documents/jina/jina/peapods/runtimes/request_handlers/data_request_handler.py", line 163, in handle
field='groundtruths',
File "/Users/hanxiao/Documents/jina/jina/executors/__init__.py", line 200, in __call__
self, **kwargs
File "/Users/hanxiao/Documents/jina/jina/executors/decorators.py", line 105, in arg_wrapper
return fn(*args, **kwargs)
File "/Users/hanxiao/Documents/jina/toy43.py", line 9, in foo
raise NotImplementedError('no time for it')
NotImplementedError: no time for it
NotImplementedError('no time for it')
Use Executor out of Flow
Executor
object can be used directly just like a regular Python object. For example,
from jina import Executor, requests, DocumentArray, Document
class MyExec(Executor):
@requests
def foo(self, docs, **kwargs):
for d in docs:
d.text = 'hello world'
m = MyExec()
da = DocumentArray([Document(text='test')])
m.foo(da)
print(da)
DocumentArray has 1 items:
{'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.
from jina import Executor, requests, Document, DocumentArray
class MyExec(Executor):
@requests
def foo(self, docs, **kwargs):
for doc in docs:
print(doc.text)
def close(self):
print("closing...")
with MyExec() as executor:
executor.foo(DocumentArray([Document(text='hello world')]))
hello world
closing...
YAML interface
An Executor can be loaded from and stored to a YAML file. The YAML file has the following format:
jtype: MyExecutor
with:
parameter_1: foo
parameter_2: bar
metas:
name: MyExecutor
description: "MyExecutor does a thing to the stuff in your Documents"
workspace: workspace
py_modules:
- executor.py
requests:
index: MyExecutor_index_method
search: MyExecutor_search_method
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__
methodmetas
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:
from jina import Executor
class MyExecutor(Executor):
def __init__(self, bar: int, **kwargs):
super().__init__(**kwargs)
self.bar = bar
def foo(self, **kwargs):
pass
y_literal = """
jtype: MyExecutor
with:
bar: 123
metas:
name: awesomeness
description: my first awesome executor
requests:
/random_work: foo
"""
exec = Executor.load_config(y_literal)
exec.save_config('y.yml')
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 theExecutor
, e.g.shard_id
,replicas
. Those values are often related to the system/network environment around theExecutor
, and less about theExecutor
itself. They are usually set with theadd()
method. See the list of options here.
The following fields are valid for metas
and runtime_args
:
Attribute | Fields |
---|---|
.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):