pyjuiz 0.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pyjuiz-0.0.2/PKG-INFO ADDED
@@ -0,0 +1,21 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyjuiz
3
+ Version: 0.0.2
4
+ Summary: Python binding interface for JUIZ robot middleware components
5
+ Home-page: https://github.com/juiz
6
+ Author: ysuga
7
+ Author-email: ysuga@ysuga.net
8
+ License: MIT
9
+ Keywords: robot,middleware
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Requires-Dist: yaml
12
+ Requires-Dist: httpx
13
+ Requires-Dist: pillow
14
+ Dynamic: author
15
+ Dynamic: author-email
16
+ Dynamic: classifier
17
+ Dynamic: home-page
18
+ Dynamic: keywords
19
+ Dynamic: license
20
+ Dynamic: requires-dist
21
+ Dynamic: summary
pyjuiz-0.0.2/README.md ADDED
File without changes
@@ -0,0 +1,5 @@
1
+
2
+ # from . import proxy
3
+ from .juiz import *
4
+
5
+ from .decorators import juiz_process, juiz_container, juiz_container_process, juiz_component_container, juiz_component_container_process
@@ -0,0 +1,131 @@
1
+
2
+ import argparse
3
+ import juiz
4
+ import json
5
+
6
+ def _get_system(option):
7
+ return juiz.proxy.SystemProxy(juiz.proxy.HttpProxy(option.host, option.port))
8
+
9
+ def sys_handler(args):
10
+ print('system:', args)
11
+
12
+
13
+
14
+ def proc_list_handler(option):
15
+ s = _get_system(option)
16
+ ps = s.processes()
17
+ print([p.identifier() for p in ps])
18
+
19
+ def proc_call_handler(option):
20
+ s = _get_system(option)
21
+ p = s.process(option.identifier)
22
+ body = json.loads(option.body)
23
+ print(p.call(body))
24
+
25
+ def proc_exec_handler(option):
26
+ s = _get_system(option)
27
+ p = s.process(option.identifier)
28
+ print(p.execute())
29
+
30
+ def proc_prof_handler(option):
31
+ s = _get_system(option)
32
+ p = s.process(option.identifier)
33
+ print(p.profile_full())
34
+
35
+ def setup_process_parser(proc_parser):
36
+ proc_subparsers = proc_parser.add_subparsers()
37
+ p_l_parser = proc_subparsers.add_parser('list')
38
+ p_l_parser.set_defaults(handler=proc_list_handler)
39
+
40
+ p_p_parser = proc_subparsers.add_parser('profile')
41
+ p_p_parser.add_argument('identifier', help='process ID')
42
+ p_p_parser.set_defaults(handler=proc_prof_handler)
43
+
44
+ p_c_parser = proc_subparsers.add_parser('call')
45
+ p_c_parser.add_argument('identifier', help='process ID')
46
+ p_c_parser.add_argument('body', help='body json')
47
+ p_c_parser.set_defaults(handler=proc_call_handler)
48
+
49
+
50
+ def cont_list_handler(option):
51
+ s = _get_system(option)
52
+ cs = s.containers()
53
+ print([c.identifier() for c in cs])
54
+
55
+ def cont_prof_handler(option):
56
+ s = _get_system(option)
57
+ c = s.container(option.identifier)
58
+ print(c.profile_full())
59
+
60
+ def setup_container_parser(cont_parser):
61
+ cont_subparsers = cont_parser.add_subparsers()
62
+ c_l_parser = cont_subparsers.add_parser('list')
63
+ c_l_parser.set_defaults(handler=cont_list_handler)
64
+
65
+ c_p_parser = cont_subparsers.add_parser('profile')
66
+ c_p_parser.add_argument('identifier', help='container ID')
67
+ c_p_parser.set_defaults(handler=cont_prof_handler)
68
+
69
+ setup_container_process_parser(cont_subparsers.add_parser('process'))
70
+
71
+
72
+
73
+ def c_p_l_handler(option):
74
+ s = _get_system(option)
75
+ c = s.container(option.identifier)
76
+ ps = c.processes()
77
+ print([p.identifier() for p in ps])
78
+
79
+
80
+ def c_p_c_handler(option):
81
+ s = _get_system(option)
82
+ c = s.container(option.identifier)
83
+ p = c.process(option.process_identifier)
84
+ body = json.loads(option.body)
85
+ print(p.call(body))
86
+
87
+ def c_p_e_handler(option):
88
+ s = _get_system(option)
89
+ c = s.container(option.identifier)
90
+ p = c.process(option.process_identifier)
91
+ print(p.execute())
92
+
93
+ def setup_container_process_parser(c_ps_parser):
94
+ c_ps_parser.add_argument('identifier', help='container ID')
95
+ c_ps_subparsers = c_ps_parser.add_subparsers()
96
+
97
+ c_ps_list_parser = c_ps_subparsers.add_parser('list')
98
+ c_ps_list_parser.set_defaults(handler=c_p_l_handler)
99
+
100
+ c_ps_call_parser = c_ps_subparsers.add_parser('call')
101
+ c_ps_call_parser.add_argument('process_identifier', help='container process id')
102
+ c_ps_call_parser.add_argument('body', help='data')
103
+ c_ps_call_parser.set_defaults(handler=c_p_c_handler)
104
+
105
+ c_ps_e_parser = c_ps_subparsers.add_parser('execute')
106
+ c_ps_e_parser.add_argument('process_identifier', help='container process id')
107
+ c_ps_e_parser.set_defaults(handler=c_p_e_handler)
108
+
109
+ def main():
110
+ parser = argparse.ArgumentParser(
111
+ prog='juiz',
112
+ description='juiz test program',
113
+ )
114
+ parser.add_argument('--host', default='localhost')
115
+ parser.add_argument('--port', default=8000)
116
+ subparsers = parser.add_subparsers()
117
+ sysparser = subparsers.add_parser('system', help='system command')
118
+ sysparser.add_argument('profile', help='system profile')
119
+ sysparser.set_defaults(handler=sys_handler)
120
+
121
+ setup_process_parser(subparsers.add_parser('process', help='process command'))
122
+ setup_container_parser(subparsers.add_parser('container', help='container command'))
123
+
124
+
125
+ option = parser.parse_args()
126
+
127
+ if hasattr(option, 'handler'):
128
+ option.handler(option)
129
+
130
+ if __name__ == '__main__':
131
+ main()
@@ -0,0 +1,248 @@
1
+ import datetime
2
+ import inspect
3
+ import functools
4
+ import io
5
+
6
+ from .juiz import *
7
+ from PIL.Image import Image
8
+ class PyJuizProcessArgumentUnknownTypeError(Exception): pass
9
+ class PyJuizContainerArgumentUnknownTypeError(Exception): pass
10
+
11
+ def allow_no_arg_decorator(decorator_function):
12
+ def wrapper(*args, **kwargs):
13
+
14
+ if len(args) != 0 and callable(args[0]):
15
+ decorated_function= args[0]
16
+ return functools.wraps(decorated_function)(decorator_function(decorated_function))
17
+ else:
18
+ def _wrapper(decorated_function):
19
+ return functools.wraps(decorated_function)(decorator_function(decorated_function, *args, **kwargs))
20
+ return _wrapper
21
+ return wrapper
22
+
23
+
24
+ class JuizProcess(object):
25
+ def __init__(self, proc):
26
+ self.__proc = proc
27
+ self.name = proc.__name__
28
+ self.signature = inspect.signature(proc)
29
+ self._manifest = ProcessManifest.new(self.name).set_description(proc.__doc__)
30
+ for p in self.signature.parameters:
31
+ param = self.signature.parameters[p]
32
+ param_type = param.annotation
33
+ param_default = param.default
34
+ if param_type is inspect._empty:
35
+ if isinstance(param_default, int):
36
+ param_type = int
37
+ elif isinstance(param_default, bool):
38
+ param_type = bool
39
+ elif isinstance(param_default, float):
40
+ param_type = float
41
+ elif isinstance(param_default, list):
42
+ param_type = list
43
+ elif isinstance(param_default, str):
44
+ param_type = str
45
+ elif isinstance(param_default, dict):
46
+ param_type = dict
47
+ else:
48
+ raise PyJuizProcessArgumentUnknownTypeError()
49
+ if param_default is inspect._empty:
50
+ if param_type == int:
51
+ param_default = 0
52
+ elif param_type == float:
53
+ param_default = 0.0
54
+ elif param_type == dict:
55
+ param_default = {}
56
+ elif param_type == list:
57
+ param_default = []
58
+ elif param_type == bool:
59
+ param_default = False
60
+ elif param_type == str:
61
+ param_default = ""
62
+ elif param_type == Image:
63
+ param_default = None
64
+ else:
65
+ raise PyJuizProcessArgumentUnknownTypeError()
66
+
67
+ # print('p:', param.name, param.default, param.kind, param.annotation, param)
68
+ if param_type == int:
69
+ self._manifest.add_int_arg(param.name, "", param_default)
70
+ elif param_type == float:
71
+ self._manifest.add_float_arg(param.name, "", param_default)
72
+ elif param_type == bool:
73
+ self._manifest.add_bool_arg(param.name, "", param_default)
74
+ elif param_type == str:
75
+ self._manifest.add_string_arg(param.name, "", param_default)
76
+ elif param_type == dict:
77
+ self._manifest.add_object_arg(param.name, "", param_default)
78
+ elif param_type == list:
79
+ self._manifest.add_array_arg(param.name, "", param_default)
80
+ elif param_type == Image:
81
+ self._manifest.add_image_arg(param.name, "", param_default)
82
+ def manifest(self):
83
+ return self._manifest.into_value()
84
+
85
+ def __call__(self, *args, **kwargs):
86
+ return convert_process_result(self.__proc(*args, **kwargs))
87
+
88
+ @allow_no_arg_decorator
89
+ def juiz_process(process_function, description="Default Description"):
90
+ return JuizProcess(process_function)
91
+
92
+ class JuizContainer(object):
93
+ def __init__(self, proc):
94
+ self.__proc = proc
95
+ self.name = proc.__name__
96
+ self.signature = inspect.signature(proc)
97
+ self._manifest = ContainerManifest.new(self.name).set_description(proc.__doc__)
98
+ for p in self.signature.parameters:
99
+ param = self.signature.parameters[p]
100
+ param_type = param.annotation
101
+ param_default = param.default
102
+ if param_type is inspect._empty:
103
+ if isinstance(param_default, int):
104
+ param_type = int
105
+ elif isinstance(param_default, bool):
106
+ param_type = bool
107
+ elif isinstance(param_default, float):
108
+ param_type = float
109
+ elif isinstance(param_default, list):
110
+ param_type = list
111
+ elif isinstance(param_default, str):
112
+ param_type = str
113
+ elif isinstance(param_default, dict):
114
+ param_type = dict
115
+ elif isinstance(param_default, Image):
116
+ param_type = Image
117
+ else:
118
+ raise PyJuizProcessArgumentUnknownTypeError()
119
+ if param_default is inspect._empty:
120
+ if param_type == int:
121
+ param_default = 0
122
+ elif param_type == float:
123
+ param_default = 0.0
124
+ elif param_type == dict:
125
+ param_default = {}
126
+ elif param_type == list:
127
+ param_default = []
128
+ elif param_type == bool:
129
+ param_default = False
130
+ elif param_type == str:
131
+ param_default = ""
132
+ elif param_type == Image:
133
+ param_default = None
134
+ else:
135
+ raise PyJuizProcessArgumentUnknownTypeError()
136
+
137
+ # print('p:', param.name, param.default, param.kind, param.annotation, param)
138
+ if param_type == int:
139
+ self._manifest.add_int_arg(param.name, "", param_default)
140
+ elif param_type == float:
141
+ self._manifest.add_float_arg(param.name, "", param_default)
142
+ elif param_type == bool:
143
+ self._manifest.add_bool_arg(param.name, "", param_default)
144
+ elif param_type == str:
145
+ self._manifest.add_string_arg(param.name, "", param_default)
146
+ elif param_type == dict:
147
+ self._manifest.add_object_arg(param.name, "", param_default)
148
+ elif param_type == list:
149
+ self._manifest.add_array_arg(param.name, "", param_default)
150
+ elif param_type == Image:
151
+ self._manifest.add_image_arg(param.name, "", param_default)
152
+ def manifest(self):
153
+ return self._manifest.into_value()
154
+
155
+ def __call__(self, *args, **kwargs):
156
+ return self.__proc(*args, **kwargs)
157
+
158
+ @allow_no_arg_decorator
159
+ def juiz_container(constructor_function, description="Default Description"):
160
+ return JuizContainer(constructor_function)
161
+
162
+ @allow_no_arg_decorator
163
+ def juiz_component_container(constructor_function, description="Default Description"):
164
+ return JuizContainer(constructor_function)
165
+
166
+ class JuizContainerProcess(object):
167
+ def __init__(self, proc, container_type):
168
+ self.__proc = proc
169
+ self.name = proc.__name__
170
+ self.signature = inspect.signature(proc)
171
+ self._manifest = ProcessManifest.new(self.name).set_description(proc.__doc__).set_container_type(container_type)
172
+ for i, p in enumerate(self.signature.parameters):
173
+ if i == 0:
174
+ continue
175
+ param = self.signature.parameters[p]
176
+ param_type = param.annotation
177
+ param_default = param.default
178
+ if param_type is inspect._empty:
179
+ if isinstance(param_default, int):
180
+ param_type = int
181
+ elif isinstance(param_default, bool):
182
+ param_type = bool
183
+ elif isinstance(param_default, float):
184
+ param_type = float
185
+ elif isinstance(param_default, list):
186
+ param_type = list
187
+ elif isinstance(param_default, str):
188
+ param_type = str
189
+ elif isinstance(param_default, dict):
190
+ param_type = dict
191
+ elif isinstance(param_default, Image):
192
+ param_type = Image
193
+ else:
194
+ raise PyJuizProcessArgumentUnknownTypeError()
195
+ if param_default is inspect._empty:
196
+ if param_type == int:
197
+ param_default = 0
198
+ elif param_type == float:
199
+ param_default = 0.0
200
+ elif param_type == dict:
201
+ param_default = {}
202
+ elif param_type == list:
203
+ param_default = []
204
+ elif param_type == bool:
205
+ param_default = False
206
+ elif param_type == str:
207
+ param_default = ""
208
+ elif param_type == Image:
209
+ param_default = None
210
+ else:
211
+ print('Param Type Unknown:', param_type)
212
+ raise PyJuizProcessArgumentUnknownTypeError()
213
+
214
+ # print('p:', param.name, param.default, param.kind, param.annotation, param)
215
+ if param_type == int:
216
+ self._manifest.add_int_arg(param.name, "", param_default)
217
+ elif param_type == float:
218
+ self._manifest.add_float_arg(param.name, "", param_default)
219
+ elif param_type == bool:
220
+ self._manifest.add_bool_arg(param.name, "", param_default)
221
+ elif param_type == str:
222
+ self._manifest.add_string_arg(param.name, "", param_default)
223
+ elif param_type == dict:
224
+ self._manifest.add_object_arg(param.name, "", param_default)
225
+ elif param_type == list:
226
+ self._manifest.add_array_arg(param.name, "", param_default)
227
+ elif param_type == Image:
228
+ self._manifest.add_image_arg(param.name, "", param_default)
229
+ def manifest(self):
230
+ return self._manifest.into_value()
231
+
232
+ def __call__(self, *args, **kwargs):
233
+ return convert_process_result(self.__proc(*args, **kwargs))
234
+
235
+ def convert_process_result(retval):
236
+ # if isinstance(retval, Image):
237
+ # output = io.BytesIO()
238
+ # retval.save(output, format='PNG')
239
+ # return output.getvalue() # Hex Data
240
+ return retval
241
+
242
+ @allow_no_arg_decorator
243
+ def juiz_container_process(constructor_function, container_type:str, description="Default Description"):
244
+ return JuizContainerProcess(constructor_function, container_type)
245
+
246
+ @allow_no_arg_decorator
247
+ def juiz_component_container_process(constructor_function, container_type:str, description="Default Description"):
248
+ return JuizContainerProcess(constructor_function, container_type)
@@ -0,0 +1,178 @@
1
+ from dataclasses import dataclass, asdict
2
+ from typing import List, Any, Optional
3
+ from PIL.Image import Image
4
+
5
+ @dataclass
6
+ class ArgumentManifest:
7
+ type_name: str
8
+ name: str
9
+ default: Any
10
+ description: str = ""
11
+
12
+ @classmethod
13
+ def new(cls, type_name, name, description, default_value):
14
+ return ArgumentManifest(type_name=type_name, name=name, default=default_value, description=description )
15
+
16
+ @dataclass
17
+ class ProcessManifest:
18
+
19
+ type_name: str
20
+ arguments: List[ArgumentManifest]
21
+ description: str = ""
22
+ factory: str = "process_factory"
23
+ use_demo: bool = False
24
+ language: str = "python"
25
+ name: Optional[str] = None
26
+ container_name: Optional[str] = None
27
+ container_type: Optional[str] = None
28
+
29
+ @classmethod
30
+ def new(cls, type_name):
31
+ return ProcessManifest(type_name=type_name, arguments=[])
32
+
33
+ def set_name(self, n):
34
+ self.name = n
35
+ return self
36
+
37
+ def set_description(self, desc):
38
+ self.description = desc
39
+ return self
40
+
41
+ def set_language(self, language:str):
42
+ self.language = language
43
+ return self
44
+
45
+ def set_factory(self, factory:str):
46
+ self.factory = factory
47
+ return self
48
+
49
+ def set_container_type(self, container_type:str):
50
+ self.container_type = container_type
51
+ return self
52
+
53
+ def set_container_name(self, container_name:Optional[str]):
54
+ self.container_name = container_name
55
+ return self
56
+
57
+ def add_argument(self, argument_manifest):
58
+ self.arguments.append(argument_manifest)
59
+ return self
60
+
61
+ def add_bool_arg(self, name, description, default_value:bool):
62
+ self.add_argument(ArgumentManifest.new("Bool", name, description, default_value))
63
+ return self
64
+
65
+ def add_int_arg(self, name, description, default_value:int):
66
+ self.add_argument(ArgumentManifest.new("Int", name, description, default_value))
67
+ return self
68
+
69
+ def add_float_arg(self, name, description, default_value:float):
70
+ self.add_argument(ArgumentManifest.new("Float", name, description, default_value))
71
+ return self
72
+
73
+ def add_string_arg(self, name, description, default_value:str):
74
+ self.add_argument(ArgumentManifest.new("String", name, description, default_value))
75
+ return self
76
+
77
+ def add_object_arg(self, name, description, default_value: dict):
78
+ self.add_argument(ArgumentManifest.new("Object", name, description, default_value))
79
+ return self
80
+
81
+ def add_array_arg(self, name, description, default_value: list):
82
+ self.add_argument(ArgumentManifest.new("Array", name, description, default_value))
83
+ return self
84
+
85
+ def add_image_arg(self, name, description, default_value: object):
86
+ self.add_argument(ArgumentManifest.new("Image", name, description, default_value))
87
+
88
+ def into_value(self):
89
+ return asdict(self, dict_factory=lambda x: {k: v for (k, v) in x if v is not None})
90
+
91
+ @dataclass
92
+ class ContainerManifest:
93
+ type_name: str
94
+ args: dict
95
+ processes: List[ProcessManifest]
96
+ arguments: List[ArgumentManifest]
97
+ language: str = "python"
98
+ factory: str = "container_factory"
99
+ description: str = ""
100
+ parent_type_name: Optional[str] = None
101
+ parent_name: Optional[str] = None
102
+ name: Optional[str] = None
103
+
104
+ @classmethod
105
+ def new(cls, type_name):
106
+ return ContainerManifest(type_name=type_name, args={}, processes=[], arguments=[])
107
+
108
+ def add_process(self, process_manifest: ProcessManifest):
109
+ self.processes.append(process_manifest\
110
+ .set_container_type(self.type_name)\
111
+ .set_container_name(self.name))
112
+ return self
113
+
114
+ def set_description(self, desc):
115
+ self.description = desc
116
+ return self
117
+
118
+ def into_value(self):
119
+ return asdict(self, dict_factory=lambda x: {k: v for (k, v) in x if v is not None})
120
+
121
+ def add_argument(self, argument_manifest):
122
+ self.arguments.append(argument_manifest)
123
+ return self
124
+
125
+ def add_bool_arg(self, name, description, default_value:bool):
126
+ self.add_argument(ArgumentManifest.new("bool", name, description, default_value))
127
+ return self
128
+
129
+ def add_int_arg(self, name, description, default_value:int):
130
+ self.add_argument(ArgumentManifest.new("int", name, description, default_value))
131
+ return self
132
+
133
+ def add_float_arg(self, name, description, default_value:float):
134
+ self.add_argument(ArgumentManifest.new("float", name, description, default_value))
135
+ return self
136
+
137
+ def add_string_arg(self, name, description, default_value:str):
138
+ self.add_argument(ArgumentManifest.new("string", name, description, default_value))
139
+ return self
140
+
141
+ def add_object_arg(self, name, description, default_value: dict):
142
+ self.add_argument(ArgumentManifest.new("object", name, description, default_value))
143
+ return self
144
+
145
+ def add_array_arg(self, name, description, default_value: list):
146
+ self.add_argument(ArgumentManifest.new("array", name, description, default_value))
147
+ return self
148
+
149
+ def add_image_arg(self, name, description, default_value: object):
150
+ self.add_argument(ArgumentManifest.new("image", name, description, default_value))
151
+
152
+ @dataclass
153
+ class ComponentManifest:
154
+ type_name: str
155
+ containers: List[ContainerManifest]
156
+ processes: List[ProcessManifest]
157
+ description: str = ""
158
+ language: str = "python"
159
+
160
+ @classmethod
161
+ def new(cls, type_name):
162
+ return ComponentManifest(type_name=type_name, containers=[], processes=[])
163
+
164
+ def set_description(self, desc):
165
+ self.description = desc
166
+ return self
167
+
168
+ def set_language(self, lang):
169
+ self.language = lang
170
+ return self
171
+
172
+ def add_container(self, c):
173
+ self.containers.append(c)
174
+ return self
175
+
176
+ def add_process(self, p: ProcessManifest):
177
+ self.processes.append(p)
178
+ return self
@@ -0,0 +1,403 @@
1
+ import httpx
2
+ import json, io
3
+ from PIL import Image
4
+ from io import BytesIO
5
+
6
+
7
+ class UnsupportedMediaTypeError(Exception): pass
8
+ class BadRequestError(Exception): pass
9
+ class ForbiddenError(Exception): pass
10
+ class NotFoundError(Exception): pass
11
+ class RequestTimeoutError(Exception): pass
12
+ class InternalServerError(Exception): pass
13
+
14
+
15
+ error_code_and_ex={
16
+ 415: UnsupportedMediaTypeError,
17
+ 400: BadRequestError,
18
+ 403: ForbiddenError,
19
+ 404: NotFoundError,
20
+ 408: RequestTimeoutError,
21
+ 500: InternalServerError,
22
+ }
23
+
24
+ class CRUDProxy():
25
+
26
+ def __init__(self):
27
+ pass
28
+
29
+ def _url(self, class_name, function_name, param_dict: dict=None):
30
+ if param_dict is None:
31
+ return f'/api/{class_name}/{function_name}'
32
+ else:
33
+ return f'/api/{class_name}/{function_name}?' + '&'.join([f'{k}={v}' for k, v in param_dict.items()])
34
+
35
+ def read(self, class_name, function_name, param_dict={}):
36
+ pass
37
+
38
+ def delete(self, class_name, function_name, param_dict={}):
39
+ pass
40
+
41
+ def create(self, class_name, function_name, body, param_dict={}):
42
+ pass
43
+
44
+ def update(self, class_name, function_name, body, param_dict={}):
45
+ pass
46
+
47
+ class HttpProxy(CRUDProxy):
48
+
49
+ def __init__(self, host, port):
50
+ self._host = host
51
+ self._port = port
52
+ self._base_url = 'http://' + self._host + ':' + str(self._port)
53
+ pass
54
+
55
+ def _do_response(self, response):
56
+ if response.status_code == 200:
57
+ ct = response.headers.get('content-type', None)
58
+ if ct and ct == 'application/json':
59
+ return response.json()
60
+ elif ct and ct == 'image/png':
61
+ return Image.open(BytesIO(response.content)).convert('RGB')
62
+ else:
63
+ raise error_code_and_ex[response.status_code]()
64
+
65
+ def read(self, class_name, function_name, param_dict={}):
66
+ url = self._url(class_name, function_name, param_dict)
67
+ return self._get(url)
68
+
69
+ def delete(self, class_name, function_name, param_dict={}):
70
+ url = self._url(class_name, function_name, param_dict)
71
+ return self._delete(url)
72
+
73
+ def create(self, class_name, function_name, body: dict, param_dict: dict={}):
74
+ url = self._url(class_name, function_name, param_dict)
75
+ return self._post(url, body)
76
+
77
+ def update(self, class_name, function_name, body, param_dict={}, profile=None):
78
+ url = self._url(class_name, function_name, param_dict)
79
+ return self._patch(url, body, profile=profile)
80
+
81
+ def _get(self, url):
82
+ with httpx.Client() as client:
83
+ return self._do_response(client.get(self._base_url + url))
84
+ return None
85
+
86
+ def _delete(self, url):
87
+ with httpx.Client() as client:
88
+ return self._do_response(client.delete(self._base_url + url))
89
+ return None
90
+
91
+ def create(self, url, data):
92
+ with httpx.Client() as client:
93
+ return self._do_response(client.create(self._base_url + url,
94
+ data=json.dumps(data),
95
+ headers={"Content-Type": "application/json"}
96
+ ))
97
+ return None
98
+
99
+ def _post(self, url, data):
100
+ with httpx.Client() as client:
101
+ return self._do_response(client.post(self._base_url + url,
102
+ data=json.dumps(data),
103
+ headers={"Content-Type": "application/json"}
104
+ ))
105
+ return None
106
+
107
+ def _patch(self, url, data, profile=None):
108
+ with httpx.Client() as client:
109
+ img_key_value = None
110
+ serializable_data = {}
111
+ if isinstance(data, Image.Image):
112
+ #print('prof:', profile)
113
+ pass
114
+ else:
115
+ for k, v in data.items():
116
+ if isinstance(v, Image.Image):
117
+ img_key_value = (k, v)
118
+ else:
119
+ serializable_data[k] = v
120
+ if img_key_value is not None:
121
+ output = io.BytesIO()
122
+ img_key_value[1].save(output, 'PNG')
123
+ files = {}
124
+ for k, v in serializable_data.items():
125
+ files[k] = (None, v, "application/json")
126
+ files[img_key_value[0]] = (None, output.getvalue(), "image/png")
127
+ return self._do_response(client.put(self._base_url + url,
128
+ # data=json.dumps(serializable_data),
129
+ headers={"Content-Type": "multipart/form-data; boundary=+++"},
130
+ #files={img_key_value[0]: (None, output.getvalue(), "image/png")}))
131
+ files=files))
132
+ else:
133
+ return self._do_response(client.patch(self._base_url + url,
134
+ data=json.dumps(data),
135
+ headers={"Content-Type": "application/json"}
136
+ ))
137
+ return None
138
+
139
+ class SystemProxy():
140
+
141
+ def __init__(self, base_proxy: CRUDProxy):
142
+ self.crud = base_proxy
143
+ self._processes = None
144
+ self._containers = None
145
+ self._container_processes = None
146
+ pass
147
+
148
+ def profile_full(self, update=False):
149
+ return self.crud.read('system', 'profile_full')
150
+
151
+ def create_process(self, type_name, name, use_memo=True):
152
+ return self.crud.create('/api/process/', {
153
+ "type_name": type_name,
154
+ "name": name,
155
+ "use_memo": use_memo})
156
+
157
+ def processes(self):
158
+ ps = self.crud.read('process', 'list')
159
+ self._processes = [ProcessProxy.new(self, pid) for pid in ps]
160
+ return self._processes.copy()
161
+
162
+ def process(self, id):
163
+ ps = self.crud.read('process', 'list')
164
+ for pid in ps:
165
+ if pid == id:
166
+ return ProcessProxy.new(self, pid)
167
+ return None
168
+
169
+ def containers(self, update=False):
170
+ ps = self.crud.read('container', 'list')
171
+ self._containers = [ContainerProxy.new(self, pid) for pid in ps]
172
+ return self._containers
173
+
174
+ def container(self, id):
175
+ cs = self.crud.read('container', 'list')
176
+ for pid in cs:
177
+ if pid == id:
178
+ return ContainerProxy.new(self, pid)
179
+ return None
180
+
181
+ def container_processes(self, update=False):
182
+ ps = self.crud.read('container_process','list')
183
+ if self._container_processes is None or update:
184
+ self._container_processes = [ContainerProcessProxy(self, p) for p in ps]
185
+ return self._container_processes
186
+
187
+
188
+ class ObjectProxy(object):
189
+
190
+ def __init__(self, system_proxy, class_name, identifier):
191
+ self.system = system_proxy
192
+ self._id = identifier
193
+ self._cn = class_name
194
+ self._profile = None
195
+ self._name = id_to_name(identifier) if identifier is not None else None
196
+ self._type_name = None
197
+
198
+ def profile_full(self, update=False):
199
+ if self._profile is None or update:
200
+ self._profile = self.system.crud.read(self._cn, 'profile_full', {'identifier': self._id})
201
+ return self._profile
202
+
203
+ def identifier(self, update=False):
204
+ return self._id
205
+
206
+ def name(self):
207
+ if self._name is None:
208
+ self._name = id_to_name(self._id)
209
+ return self._name
210
+
211
+ def type_name(self):
212
+ if self._type_name is None:
213
+ self._type_name = id_to_type_name(self._id)
214
+ return self._type_name
215
+
216
+
217
+ def destroy(self):
218
+ return self._system_proxy.delete('/api/' + self._class_name + '/destroy?identifier=' + self._identifier)
219
+
220
+ class ProcessProxy(ObjectProxy):
221
+ def __init__(self, name, host, port, protocol='http'):
222
+ if protocol is None:
223
+ super().__init__(None, 'process', None)
224
+ self._name = name
225
+ elif protocol == 'http':
226
+ super().__init__(SystemProxy(HttpProxy(host,port)), 'process', None)
227
+ self._name = None
228
+ for p in self.system.processes():
229
+ if p.name() == name:
230
+ self._id = p.identifier()
231
+ return
232
+
233
+ def __repr__(self):
234
+ return f'ProcessProxy("{self.identifier()}")'
235
+
236
+ @classmethod
237
+ def new(cls, system_proxy, identifier):
238
+ p = ProcessProxy(None, None, None, protocol=None)
239
+ p.system = system_proxy
240
+ p._id = identifier
241
+ return p
242
+
243
+ def call(self, *args, **kwargs):
244
+ prof = self.profile_full()
245
+ if len(args) == 1:
246
+ arg_prof = prof['arguments']
247
+ body = {
248
+ arg_prof[0]['name']: args[0]
249
+ }
250
+ elif 'body' in kwargs.keys():
251
+ body = kwargs['body']
252
+ else:
253
+ body = {}
254
+ args_prof = prof['arguments']
255
+ for a, a_prof in zip(args, args_prof):
256
+ if isinstance(a, Image.Image):
257
+ body[a_prof['name']] = a
258
+ else:
259
+ body[a_prof['name']] = json.dumps(a)
260
+ for k, v in kwargs.items():
261
+ body[k] = v
262
+ return self.system.crud.update(self._cn, 'call', body, {'identifier': self._id}, profile=self._profile)
263
+
264
+ def __call__(self, *args, **kwargs):
265
+ return self.call(*args, **kwargs)
266
+
267
+ def execute(self):
268
+ return self.system.crud.update(self._cn, 'execute', {}, {'identifier': self._id})
269
+
270
+ def p_apply(self, arg_name: str, value: dict):
271
+ return self.system.crud.update(self._cn, 'p_apply', {'arg_name': arg_name, 'value': value}, {'identifier': self._id})
272
+
273
+ class ContainerProxy(ObjectProxy):
274
+
275
+ def __init__(self, name, host, port, protocol='http'):
276
+ if protocol is None:
277
+ super().__init__(None, 'container', None)
278
+ self._name = name
279
+ elif protocol == 'http':
280
+ super().__init__(SystemProxy(HttpProxy(host,port)), 'container', None)
281
+ self._name = None
282
+ for c in self.system.containers():
283
+ # print(c.type_name(), name)
284
+ if c.type_name() == name:
285
+ self._id = c.identifier()
286
+ return
287
+
288
+ @classmethod
289
+ def new(cls, system_proxy, identifier):
290
+ p = ContainerProxy(None, None, None, protocol=None)
291
+ p.system = system_proxy
292
+ p._id = identifier
293
+ return p
294
+
295
+ def __repr__(self):
296
+ return f'ContainerProxy("{self.identifier()}")'
297
+
298
+ def processes(self):
299
+ pids = self.profile_full()['processes']
300
+ return [ContainerProcessProxy(self.system, pid) for pid in pids]
301
+
302
+ def process(self, id):
303
+ pids = self.profile_full()['processes']
304
+ for pid in pids:
305
+ if pid == id:
306
+ return ContainerProcessProxy(self.system, pid)
307
+ return None
308
+
309
+ def __getattr__(self, name):
310
+ # search process by name:
311
+ ps = self.processes()
312
+ for p in ps:
313
+ if p.type_name() == name:
314
+ return p
315
+ for p in ps:
316
+ if p.name() == name:
317
+ return p
318
+ raise AttributeError(self, name)
319
+
320
+
321
+ class ContainerProcessProxy(ObjectProxy):
322
+ def __init__(self, system_proxy, identifier):
323
+ super().__init__(system_proxy, 'container_process', identifier)
324
+
325
+ def __repr__(self):
326
+ return f'ContainerProcess("{self.identifier()}")'
327
+
328
+ def call(self, *args, **kwargs):
329
+ prof = self.profile_full()
330
+ if len(args) == 1:
331
+ arg_prof = prof['arguments']
332
+ body = {
333
+ arg_prof[0]['name']: args[0]
334
+ }
335
+ #body = args[0]
336
+ elif 'body' in kwargs.keys():
337
+ body = kwargs['body']
338
+ else:
339
+ body = {}
340
+ args_prof = prof['arguments']
341
+ for a, a_prof in zip(args, args_prof):
342
+ if isinstance(a, Image.Image):
343
+ body[a_prof['name']] = a
344
+ else:
345
+ body[a_prof['name']] = json.dumps(a)
346
+ for k, v in kwargs.items():
347
+ body[k] = v
348
+ #body = kwargs
349
+ return self.system.crud.update(self._cn, 'call', body, {'identifier': self._id}, profile=self.profile_full())
350
+
351
+ def __call__(self, *args, **kwargs):
352
+ return self.call(*args, **kwargs)
353
+
354
+ def execute(self):
355
+ return self.system.crud.update(self._cn, 'execute', {}, {'identifier': self._id}, profile=self._profile)
356
+
357
+ def p_apply(self, arg_name: str, value: dict):
358
+ return self.system.crud.update(self._cn, 'p_apply', {'arg_name': arg_name, 'value': value}, {'identifier': self._id})
359
+
360
+
361
+ class IdentifierStruct(object):
362
+
363
+ def __init__(self, broker_type, broker_name, class_name, type_name, name):
364
+ self.broker_type = broker_type
365
+ self.broker_name = broker_name
366
+ self.class_name = class_name
367
+ self.type_name = type_name
368
+ self.name = name
369
+
370
+ @classmethod
371
+ def fromIdentifier(self, id):
372
+ s, type_name = id.split('::')
373
+ broker_type, s2 = s.split('://')
374
+ broker_name, class_name, name = s2.split('/')
375
+ return IdentifierStruct(broker_type, broker_name, class_name, type_name, name)
376
+
377
+ def to_id(self):
378
+ return f'{self.broker_type}://{self.broker_name}/{self.class_name}/{self.name}::{self.type_name}'
379
+
380
+ def __repr__(self):
381
+ return f'IdentifierStruct(broker_type="{self.broker_type}",broker_name="{self.broker_name}",class_name="{self.class_name}",type_name="{self.type_name}",name="{self.name}")'
382
+
383
+
384
+ def id_to_name(id: str):
385
+ if id.find('://') > 0: # 通常IDフォーマット。プロトコルあり
386
+ # print('id=', id)
387
+ broker_type, id_body = id.split('://')
388
+ broker_name, class_name, name_and_type = id_body.split('/')
389
+ name, type_name = name_and_type.split('::')
390
+ return name
391
+
392
+
393
+ def id_to_type_name(id: str):
394
+ if id.find('://') > 0: # 通常IDフォーマット。プロトコルあり
395
+ # print('id=', id)
396
+ broker_type, id_body = id.split('://')
397
+ broker_name, class_name, name_and_type = id_body.split('/')
398
+ name, type_name = name_and_type.split('::')
399
+ return type_name
400
+
401
+ if __name__ == '__main__':
402
+ i = 'core://core/process/hoge_process_name0::hoge_process'
403
+ print(id_to_name(i))
@@ -0,0 +1,21 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyjuiz
3
+ Version: 0.0.2
4
+ Summary: Python binding interface for JUIZ robot middleware components
5
+ Home-page: https://github.com/juiz
6
+ Author: ysuga
7
+ Author-email: ysuga@ysuga.net
8
+ License: MIT
9
+ Keywords: robot,middleware
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Requires-Dist: yaml
12
+ Requires-Dist: httpx
13
+ Requires-Dist: pillow
14
+ Dynamic: author
15
+ Dynamic: author-email
16
+ Dynamic: classifier
17
+ Dynamic: home-page
18
+ Dynamic: keywords
19
+ Dynamic: license
20
+ Dynamic: requires-dist
21
+ Dynamic: summary
@@ -0,0 +1,12 @@
1
+ README.md
2
+ setup.py
3
+ juiz/__init__.py
4
+ juiz/__main__.py
5
+ juiz/decorators.py
6
+ juiz/juiz.py
7
+ juiz/proxy/__init__.py
8
+ pyjuiz.egg-info/PKG-INFO
9
+ pyjuiz.egg-info/SOURCES.txt
10
+ pyjuiz.egg-info/dependency_links.txt
11
+ pyjuiz.egg-info/requires.txt
12
+ pyjuiz.egg-info/top_level.txt
@@ -0,0 +1,3 @@
1
+ yaml
2
+ httpx
3
+ pillow
@@ -0,0 +1 @@
1
+ juiz
pyjuiz-0.0.2/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
pyjuiz-0.0.2/setup.py ADDED
@@ -0,0 +1,20 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name="pyjuiz",
5
+ version="0.0.2",
6
+ description='Python binding interface for JUIZ robot middleware components',
7
+ url='https://github.com/juiz',
8
+ author='ysuga',
9
+ author_email='ysuga@ysuga.net',
10
+ license='MIT',
11
+ keywords='robot,middleware',
12
+ install_requires=["yaml", "httpx", "pillow"],
13
+ extras_require={
14
+ #"develop": ["dev-packageA", "dev-packageB"]
15
+ },
16
+ packages=find_packages(),
17
+ classifiers=[
18
+ 'Programming Language :: Python :: 3.10',
19
+ ],
20
+ )