|
| 1 | +Plugins |
| 2 | +======= |
| 3 | + |
| 4 | +In addition to using ``@Stream.register_api()`` decorator, custom stream nodes can |
| 5 | +be added to Streamz by installing 3rd-party Python packages. |
| 6 | + |
| 7 | + |
| 8 | +Known plugins |
| 9 | +------------- |
| 10 | + |
| 11 | +Extras |
| 12 | +++++++ |
| 13 | + |
| 14 | +These plugins are supported by the Streamz community and can be installed as extras, |
| 15 | +e.g. ``pip install streamz[kafka]``. |
| 16 | + |
| 17 | +There are no plugins here yet, but hopefully soon there will be. |
| 18 | + |
| 19 | +.. only:: comment |
| 20 | + ================= ====================================================== |
| 21 | + Extra name Description |
| 22 | + ================= ====================================================== |
| 23 | + ``files`` Advanced filesystem operations: listening for new |
| 24 | + files in a directory, writing to multiple files etc. |
| 25 | + ``kafka`` Reading from and writing to Kafka topics. |
| 26 | + ================= ====================================================== |
| 27 | + |
| 28 | + |
| 29 | +Entry points |
| 30 | +------------ |
| 31 | + |
| 32 | +Plugins register themselves with Streamz by using ``entry_points`` argument |
| 33 | +in ``setup.py``: |
| 34 | + |
| 35 | +.. code-block:: Python |
| 36 | +
|
| 37 | + # setup.py |
| 38 | +
|
| 39 | + from setuptools import setup |
| 40 | +
|
| 41 | + setup( |
| 42 | + name="streamz_example_plugin", |
| 43 | + version="0.0.1", |
| 44 | + entry_points={ |
| 45 | + "streamz.nodes": [ |
| 46 | + "repeat = streamz_example_plugin:RepeatNode" |
| 47 | + ] |
| 48 | + } |
| 49 | + ) |
| 50 | +
|
| 51 | +In this example, ``RepeatNode`` class will be imported from |
| 52 | +``streamz_example_plugin`` package and will be available as ``Stream.repeat``. |
| 53 | +In practice, class name and entry point name (the part before ``=`` in entry point |
| 54 | +definition) are usually the same, but they `can` be different. |
| 55 | + |
| 56 | +Different kinds of add-ons go into different entry point groups: |
| 57 | + |
| 58 | +=========== ======================= ===================== |
| 59 | + Node type Required parent class Entry point group |
| 60 | +=========== ======================= ===================== |
| 61 | + Source ``streamz.Source`` ``streamz.sources`` |
| 62 | + Node ``streamz.Stream`` ``streamz.nodes`` |
| 63 | + Sink ``streamz.Stream`` ``streamz.sinks`` |
| 64 | +=========== ======================= ===================== |
| 65 | + |
| 66 | + |
| 67 | +Lazy loading |
| 68 | +++++++++++++ |
| 69 | + |
| 70 | +Streamz will attach methods from existing plugins to the ``Stream`` class when it's |
| 71 | +imported, but actual classes will be loaded only when the corresponding ``Stream`` |
| 72 | +method is first called. Streamz will also validate the loaded class before attaching it |
| 73 | +and will raise an appropriate exception if validation fails. |
| 74 | + |
| 75 | + |
| 76 | +Reference implementation |
| 77 | +------------------------ |
| 78 | + |
| 79 | +Let's look at how stream nodes can be implemented. |
| 80 | + |
| 81 | +.. code-block:: Python |
| 82 | +
|
| 83 | + # __init__.py |
| 84 | +
|
| 85 | + from tornado import gen |
| 86 | + from streamz import Stream |
| 87 | +
|
| 88 | +
|
| 89 | + class RepeatNode(Stream): |
| 90 | +
|
| 91 | + def __init__(self, upstream, n, **kwargs): |
| 92 | + super().__init__(upstream, ensure_io_loop=True, **kwargs) |
| 93 | + self._n = n |
| 94 | +
|
| 95 | + @gen.coroutine |
| 96 | + def update(self, x, who=None, metadata=None): |
| 97 | + for _ in range(self._n): |
| 98 | + yield self._emit(x, metadata=metadata) |
| 99 | +
|
| 100 | +As you can see, implementation is the same as usual, but there's no |
| 101 | +``@Stream.register_api()`` — Streamz will take care of that when loading the plugin. |
| 102 | +It will still work if you add the decorator, but you don't have to. |
0 commit comments