Skip to content

Commit 30f706c

Browse files
authored
ML Streaming docstrings (#118)
* docstring for `RESTClientCommunicator` class added * docstring for `RESTServerCommunicator` class added * docstring for `DummyCompressor` class added * docstring for `DummyEncryptor` class added * docstring for `Compressor` + `Encryptor` + `ClientCommunicator` + classes added * docstring for `PymiloClient` class added * docstring for `PymiloServer` class added * tiny refactoring in `serialize_cfnode` function * add module level docstrings + merge one line docstrings with their quotes * add "." at the end of docstring descriptions * docstrings fixes * Fix docstring issues * docstring feedbacks applied * change `overload` to `overwrite`
1 parent edcfba0 commit 30f706c

File tree

8 files changed

+185
-6
lines changed

8 files changed

+185
-6
lines changed

pymilo/streaming/communicator.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# -*- coding: utf-8 -*-
2+
"""PyMilo RESTFull Communication Mediums."""
13
import uvicorn
24
import requests
35
from fastapi import FastAPI, Request
@@ -6,8 +8,16 @@
68

79

810
class RESTClientCommunicator(ClientCommunicator):
11+
"""Facilitate working with the communication medium from the client side for the REST protocol."""
912

1013
def __init__(self, server_url):
14+
"""
15+
Initialize the Pymilo RESTClientCommunicator instance.
16+
17+
:param server_url: the url to which PyMilo Server listens
18+
:type server_url: str
19+
:return: an instance of the Pymilo RESTClientCommunicator class
20+
"""
1121
self._server_url = server_url
1222
self.session = requests.Session()
1323
retries = requests.adapters.Retry(
@@ -19,31 +29,65 @@ def __init__(self, server_url):
1929
self.session.mount('https://', requests.adapters.HTTPAdapter(max_retries=retries))
2030

2131
def download(self, payload):
32+
"""
33+
Request for the remote ML model to download.
34+
35+
:param payload: download request payload
36+
:type payload: dict
37+
:return: response of pymilo server
38+
"""
2239
return self.session.get(url=self._server_url + "/download/", json=payload, timeout=5)
2340

2441
def upload(self, payload):
42+
"""
43+
Upload the local ML model to the remote server.
44+
45+
:param payload: upload request payload
46+
:type payload: dict
47+
:return: response of pymilo server
48+
"""
2549
return self.session.post(url=self._server_url + "/upload/", json=payload, timeout=5)
2650

2751
def attribute_call(self, payload):
52+
"""
53+
Delegate the requested attribute call to the remote server.
54+
55+
:param payload: attribute call request payload
56+
:type payload: dict
57+
:return: response of pymilo server
58+
"""
2859
return self.session.post(url=self._server_url + "/attribute_call/", json=payload, timeout=5)
2960

3061

3162

3263
class RESTServerCommunicator():
64+
"""Facilitate working with the communication medium from the server side for the REST protocol."""
3365

3466
def __init__(
3567
self,
3668
ps,
3769
host: str = "127.0.0.1",
3870
port: int = 8000,
3971
):
72+
"""
73+
Initialize the Pymilo RESTServerCommunicator instance.
74+
75+
:param ps: reference to the PyMilo server
76+
:type ps: pymilo.streaming.PymiloServer
77+
:param host: the url to which PyMilo Server listens
78+
:type host: str
79+
:param port: the port to which PyMilo Server listens
80+
:type port: int
81+
:return: an instance of the Pymilo RESTServerCommunicator class
82+
"""
4083
self.app = FastAPI()
4184
self.host = host
4285
self.port = port
4386
self._ps = ps
4487
self.setup_routes()
4588

4689
def setup_routes(self):
90+
"""Configure endpoints to handle RESTClientCommunicator requests."""
4791
class StandardPayload(BaseModel):
4892
client_id: str
4993
model_id: str
@@ -91,11 +135,19 @@ async def attribute_call(request: Request):
91135
}
92136

93137
def parse(self, body):
138+
"""
139+
Parse the compressed encrypted body of the request.
140+
141+
:param body: request body
142+
:type body: str
143+
:return: the extracted decrypted version
144+
"""
94145
return self._ps._compressor.extract(
95146
self._ps._encryptor.decrypt(
96147
body
97148
)
98149
)
99150

100151
def run(self):
152+
"""Run internal fastapi server."""
101153
uvicorn.run(self.app, host=self.host, port=self.port)

pymilo/streaming/compressor.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
1+
# -*- coding: utf-8 -*-
2+
"""Implementations of Compressor interface."""
13
from .interfaces import Compressor
24

35

46
class DummyCompressor(Compressor):
7+
"""A dummy implementation of the Compressor interface."""
58

69
@staticmethod
710
def compress(payload):
11+
"""Compress the given payload in a dummy way, simply just return it (no compression applied)."""
812
return payload
913

1014
@staticmethod
1115
def extract(payload):
16+
"""Extract the given payload in a dummy way, simply just return it (no Extraction applied)."""
1217
return payload

pymilo/streaming/encryptor.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
1+
# -*- coding: utf-8 -*-
2+
"""Implementations of Encryptor interface."""
13
from .interfaces import Encryptor
24

35

46
class DummyEncryptor(Encryptor):
7+
"""A dummy implementation of the Encryptor interface."""
58

69
@staticmethod
710
def encrypt(payload):
11+
"""Encrypt the given payload in a dummy way, simply just return it (no encryption applied)."""
812
return payload
913

1014
@staticmethod
1115
def decrypt(payload):
16+
"""Decrypt the given payload in a dummy way, simply just return it (no decryption applied)."""
1217
return payload

pymilo/streaming/interfaces.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,44 +4,94 @@
44

55

66
class Compressor(ABC):
7+
"""
8+
Compressor Interface.
9+
10+
Each Compressor has methods to compress the given payload or extract it back to the original one.
11+
"""
712

813
@abstractmethod
914
def compress(payload):
1015
"""
16+
Compress the given payload.
17+
18+
:param payload: payload to get compressed
19+
:type payload: str
20+
:return: the compressed version
1121
"""
1222

1323
@abstractmethod
1424
def extract(payload):
1525
"""
26+
Extract the given previously compressed payload.
27+
28+
:param payload: payload to get extracted
29+
:type payload: str
30+
:return: the extracted version
1631
"""
1732

1833

1934
class Encryptor(ABC):
35+
"""
36+
Encryptor Interface.
37+
38+
Each Encryptor has methods to encrypt the given payload or decrypt it back to the original one.
39+
"""
2040

2141
@abstractmethod
2242
def encrypt(payload):
2343
"""
44+
Encrypt the given payload.
45+
46+
:param payload: payload to get encrypted
47+
:type payload: str
48+
:return: the encrypted version
2449
"""
2550

2651
@abstractmethod
2752
def decrypt(payload):
2853
"""
54+
Decrypt the given previously encrypted payload.
55+
56+
:param payload: payload to get decrypted
57+
:type payload: str
58+
:return: the decrypted version
2959
"""
3060

3161

3262
class ClientCommunicator(ABC):
63+
"""
64+
ClientCommunicator Interface.
65+
66+
Each ClientCommunicator has methods to upload the local ML model, download the remote ML model and delegate attribute call to the remote server.
67+
"""
3368

3469
@abstractmethod
3570
def upload(self, payload):
3671
"""
72+
Upload the given payload to the remote server.
73+
74+
:param payload: request payload
75+
:type payload: dict
76+
:return: remote server response
3777
"""
3878

3979
@abstractmethod
4080
def download(self, payload):
4181
"""
82+
Download the remote ML model to local.
83+
84+
:param payload: request payload
85+
:type payload: dict
86+
:return: remote server response
4287
"""
4388

4489
@abstractmethod
4590
def attribute_call(self, payload):
4691
"""
92+
Execute an attribute call on the remote server.
93+
94+
:param payload: request payload
95+
:type payload: dict
96+
:return: remote server response
4797
"""

pymilo/streaming/pymilo_client.py

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# -*- coding: utf-8 -*-
2+
"""PyMiloClient for RESTFull Protocol."""
13
from enum import Enum
24
from .encryptor import DummyEncryptor
35
from .compressor import DummyCompressor
@@ -14,6 +16,7 @@ class Mode(Enum):
1416

1517

1618
class PymiloClient:
19+
"""Facilitate working with the PyMilo server."""
1720

1821
def __init__(
1922
self,
@@ -22,6 +25,19 @@ def __init__(
2225
server="http://127.0.0.1",
2326
port= 8000
2427
):
28+
"""
29+
Initialize the Pymilo PymiloClient instance.
30+
31+
:param model: the ML model PyMiloClient wrapped around
32+
:type model: Any
33+
:param mode: the mode in which PymiloClient should work, either LOCAL mode or DELEGATE
34+
:type mode: str (LOCAL|DELEGATE)
35+
:param server: the url to which PyMilo Server listens
36+
:type server: str
37+
:param port: the port to which PyMilo Server listens
38+
:type port: int
39+
:return: an instance of the Pymilo PymiloClient class
40+
"""
2541
self._client_id = "0x_client_id"
2642
self._model_id = "0x_model_id"
2743
self._model = model
@@ -32,12 +48,23 @@ def __init__(
3248
server_url="{}:{}".format(server, port)
3349
)
3450

51+
3552
def toggle_mode(self, mode=Mode.LOCAL):
53+
"""
54+
Toggle the PyMiloClient mode, either from LOCAL to DELEGATE or vice versa.
55+
56+
:return: None
57+
"""
3658
if mode not in Mode.__members__.values():
3759
raise Exception("Invalid mode, the given mode should be either `LOCAL`[default] or `DELEGATE`.")
3860
self._mode = mode
3961

4062
def download(self):
63+
"""
64+
Request for the remote ML model to download.
65+
66+
:return: None
67+
"""
4168
response = self._communicator.download({
4269
"client_id": self._client_id,
4370
"model_id": self._model_id
@@ -50,6 +77,11 @@ def download(self):
5077
print("Local model updated successfully.")
5178

5279
def upload(self):
80+
"""
81+
Upload the local ML model to the remote server.
82+
83+
:return: None
84+
"""
5385
response = self._communicator.upload({
5486
"client_id": self._client_id,
5587
"model_id": self._model_id,
@@ -61,17 +93,25 @@ def upload(self):
6193
print("Local model upload failed.")
6294

6395
def __getattr__(self, attribute):
96+
"""
97+
Overwrite the __getattr__ default function to extract requested.
98+
99+
1. If self._mode is LOCAL, extract the requested from inner ML model and returns it
100+
2. If self._mode is DELEGATE, returns a wrapper relayer which delegates the request to the remote server by execution
101+
102+
:return: Any
103+
"""
64104
if self._mode == Mode.LOCAL:
65105
if attribute in dir(self._model):
66106
return getattr(self._model, attribute)
67107
else:
68-
raise AttributeError("This attribute doesn't exist either in PymiloClient or the inner ML model.")
108+
raise AttributeError("This attribute doesn't exist in either PymiloClient or the inner ML model.")
69109
elif self._mode == Mode.DELEGATE:
70110
gdst = GeneralDataStructureTransporter()
71111
def relayer(*args, **kwargs):
72112
print(f"Method '{attribute}' called with args: {args} and kwargs: {kwargs}")
73113
payload = {
74-
"client_id": self._client_id,
114+
"client_id": self._client_id,
75115
"model_id": self._model_id,
76116
'attribute': attribute,
77117
'args': args,

pymilo/streaming/pymilo_server.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# -*- coding: utf-8 -*-
2+
"""PyMiloServer for RESTFull protocol."""
13
from ..pymilo_obj import Export, Import
24
from .compressor import DummyCompressor
35
from .encryptor import DummyEncryptor
@@ -6,21 +8,47 @@
68

79

810
class PymiloServer:
11+
"""Facilitate streaming the ML models."""
912

1013
def __init__(self, port=8000):
14+
"""
15+
Initialize the Pymilo PymiloServer instance.
16+
17+
:param port: the port to which PyMiloServer listens
18+
:type port: int
19+
:return: an instance of the PymiloServer class
20+
"""
1121
self._model = None
1222
self._compressor = DummyCompressor()
1323
self._encryptor = DummyEncryptor()
1424
self._communicator = RESTServerCommunicator(ps=self, port=port)
15-
self._communicator.run()
1625

1726
def export_model(self):
27+
"""
28+
Export the ML model to string json dump using PyMilo Export class.
29+
30+
:return: str
31+
"""
1832
return Export(self._model).to_json()
1933

2034
def update_model(self, serialized_model):
35+
"""
36+
Update the PyMilo Server's ML model.
37+
38+
:param serialized_model: the json dump of a pymilo export ml model
39+
:type serialized_model: str
40+
:return: None
41+
"""
2142
self._model = Import(file_adr=None, json_dump=serialized_model).to_model()
2243

2344
def execute_model(self, request):
45+
"""
46+
Execute the request attribute call from PyMilo Client.
47+
48+
:param request: request obj containing requested attribute to call with the associated args and kwargs
49+
:type request: obj
50+
:return: str | dict
51+
"""
2452
gdst = GeneralDataStructureTransporter()
2553
attribute = request.attribute
2654
retrieved_attribute = getattr(self._model, attribute, None)

0 commit comments

Comments
 (0)