Skip to content

Commit 882fb68

Browse files
authored
Merge pull request #1023 from compas-dev/proxy-add-location
Add optional search path to proxy
2 parents 372590f + 54b02bd commit 882fb68

File tree

3 files changed

+64
-34
lines changed

3 files changed

+64
-34
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212

1313
* Added `Polyline.extend`, `Polyline.extended`, `Polyline.shorten`, `Polyline.shortened`.
1414
* Added `Data.sha256` for computing a hash value of data objects, for example for comparisons during version control.
15+
* Added optional `path` parameter to `compas.rpc.Proxy` to allow for non-package calls.
1516

1617
### Changed
1718

src/compas/rpc/dispatcher.py

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,13 @@ def _dispatch(self, name, args):
8686
* `'profile'` : A profile of the function execution.
8787
8888
"""
89-
odict = {
90-
'data': None,
91-
'error': None,
92-
'profile': None
93-
}
89+
odict = {"data": None, "error": None, "profile": None}
9490

95-
parts = name.split('.')
91+
if len(args) > 1:
92+
if args[1] not in sys.path:
93+
sys.path.insert(0, args[1])
94+
95+
parts = name.split(".")
9696

9797
functionname = parts[-1]
9898

@@ -110,21 +110,24 @@ def _dispatch(self, name, args):
110110
else:
111111
module = self
112112
except Exception:
113-
odict['error'] = traceback.format_exc()
113+
odict["error"] = traceback.format_exc()
114114

115115
else:
116116
try:
117117
function = getattr(module, functionname)
118118
except AttributeError:
119-
odict['error'] = "This function is not part of the API: {0}".format(functionname)
119+
odict["error"] = "This function is not part of the API: {0}".format(
120+
functionname
121+
)
120122

121123
else:
122124
try:
123125
idict = json.loads(args[0], cls=DataDecoder)
124126
except (IndexError, TypeError):
125-
odict['error'] = (
127+
odict["error"] = (
126128
"API methods require a single JSON encoded dictionary as input.\n"
127-
"For example: input = json.dumps({'param_1': 1, 'param_2': [2, 3]})")
129+
"For example: input = json.dumps({'param_1': 1, 'param_2': [2, 3]})"
130+
)
128131

129132
else:
130133
self._call(function, idict, odict)
@@ -148,21 +151,20 @@ def _call(self, function, idict, odict):
148151
The output dictionary will be modified in place.
149152
150153
"""
151-
args = idict['args']
152-
kwargs = idict['kwargs']
154+
args = idict["args"]
155+
kwargs = idict["kwargs"]
153156

154157
try:
155158
data = function(*args, **kwargs)
156159
except Exception:
157-
odict['error'] = traceback.format_exc()
160+
odict["error"] = traceback.format_exc()
158161
else:
159-
odict['data'] = data
162+
odict["data"] = data
160163

161164
def _call_wrapped(self, function, idict, odict):
162-
"""Does the same as _call, but with profiling enabled.
163-
"""
164-
args = idict['args']
165-
kwargs = idict['kwargs']
165+
"""Does the same as _call, but with profiling enabled."""
166+
args = idict["args"]
167+
kwargs = idict["kwargs"]
166168

167169
try:
168170
profile = Profile()
@@ -178,7 +180,7 @@ def _call_wrapped(self, function, idict, odict):
178180
stats.print_stats(20)
179181

180182
except Exception:
181-
odict['error'] = traceback.format_exc()
183+
odict["error"] = traceback.format_exc()
182184
else:
183-
odict['data'] = data
184-
odict['profile'] = stream.getvalue()
185+
odict["data"] = data
186+
odict["profile"] = stream.getvalue()

src/compas/rpc/proxy.py

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,18 @@ class Proxy(object):
9898
Stopping the server proxy. # doctest: +SKIP
9999
"""
100100

101-
def __init__(self, package=None, python=None, url='http://127.0.0.1', port=1753, service=None, max_conn_attempts=100, autoreload=True, capture_output=True):
101+
def __init__(
102+
self,
103+
package=None,
104+
python=None,
105+
url="http://127.0.0.1",
106+
port=1753,
107+
service=None,
108+
max_conn_attempts=100,
109+
autoreload=True,
110+
capture_output=True,
111+
path=None,
112+
):
102113
self._package = None
103114
self._python = compas._os.select_python(python)
104115
self._url = url
@@ -108,6 +119,7 @@ def __init__(self, package=None, python=None, url='http://127.0.0.1', port=1753,
108119
self._process = None
109120
self._function = None
110121
self._profile = None
122+
self._path = path
111123

112124
self.service = service
113125
self.package = package
@@ -151,7 +163,7 @@ def service(self):
151163
@service.setter
152164
def service(self, service):
153165
if not service:
154-
self._service = 'compas.rpc.services.default'
166+
self._service = "compas.rpc.services.default"
155167
else:
156168
self._service = service
157169

@@ -262,14 +274,25 @@ def start_server(self):
262274
self._process.StartInfo.RedirectStandardOutput = self.capture_output
263275
self._process.StartInfo.RedirectStandardError = self.capture_output
264276
self._process.StartInfo.FileName = self.python
265-
self._process.StartInfo.Arguments = '-m {0} --port {1} --{2}autoreload'.format(self.service, self._port, '' if self.autoreload else 'no-')
277+
self._process.StartInfo.Arguments = (
278+
"-m {0} --port {1} --{2}autoreload".format(
279+
self.service, self._port, "" if self.autoreload else "no-"
280+
)
281+
)
266282
self._process.Start()
267283
else:
268-
args = [self.python, '-m', self.service, '--port', str(self._port), '--{}autoreload'.format('' if self.autoreload else 'no-')]
284+
args = [
285+
self.python,
286+
"-m",
287+
self.service,
288+
"--port",
289+
str(self._port),
290+
"--{}autoreload".format("" if self.autoreload else "no-"),
291+
]
269292
kwargs = dict(env=env)
270293
if self.capture_output:
271-
kwargs['stdout'] = PIPE
272-
kwargs['stderr'] = PIPE
294+
kwargs["stdout"] = PIPE
295+
kwargs["stderr"] = PIPE
273296

274297
self._process = Popen(args, **kwargs)
275298
# this starts the client side
@@ -285,7 +308,11 @@ def start_server(self):
285308
except Exception:
286309
time.sleep(0.1)
287310
attempt_count += 1
288-
print(" {} attempts left.".format(self.max_conn_attempts - attempt_count))
311+
print(
312+
" {} attempts left.".format(
313+
self.max_conn_attempts - attempt_count
314+
)
315+
)
289316
else:
290317
success = True
291318
break
@@ -374,15 +401,15 @@ def _proxy(self, *args, **kwargs):
374401
Numpy objects are automatically converted to their built-in Python equivalents.
375402
376403
"""
377-
idict = {'args': args, 'kwargs': kwargs}
404+
idict = {"args": args, "kwargs": kwargs}
378405
istring = json.dumps(idict, cls=DataEncoder)
379406
# it makes sense that there is a broken pipe error
380407
# because the process is not the one receiving the feedback
381408
# when there is a print statement on the server side
382409
# this counts as output
383410
# it should be sent as part of RPC communication
384411
try:
385-
ostring = self._function(istring)
412+
ostring = self._function(istring, self._path or '')
386413
except Exception:
387414
# not clear what the point of this is
388415
# self.stop_server()
@@ -395,8 +422,8 @@ def _proxy(self, *args, **kwargs):
395422

396423
result = json.loads(ostring, cls=DataDecoder)
397424

398-
if result['error']:
399-
raise RPCServerError(result['error'])
425+
if result["error"]:
426+
raise RPCServerError(result["error"])
400427

401-
self.profile = result['profile']
402-
return result['data']
428+
self.profile = result["profile"]
429+
return result["data"]

0 commit comments

Comments
 (0)