diff --git a/py/tuber/tuber.py b/py/tuber/tuber.py index 90aa192..cea0c4d 100644 --- a/py/tuber/tuber.py +++ b/py/tuber/tuber.py @@ -17,10 +17,10 @@ try: import simplejson as json except ModuleNotFoundError: - import json + import json # type: ignore[no-redef] -async def resolve(objname, hostname): +async def resolve(objname: str, hostname: str): """Create a local reference to a networked resource. This is the recommended way to connect to remote tuberd instances. @@ -36,7 +36,7 @@ async def resolve(objname, hostname): # way to avoid carrying around global state, and requiring that state be # consistent with whatever event loop is running in whichever context it's # used. See https://docs.aiohttp.org/en/stable/faq.html -_clientsession = weakref.WeakKeyDictionary() +_clientsession: weakref.WeakKeyDictionary = weakref.WeakKeyDictionary() class TuberError(Exception): @@ -150,8 +150,8 @@ async def __call__(self): results = [] for f, r in zip(futures, json_out): # Always emit warnings, if any occurred - if hasattr(r, "warning") and r.warning: - for w in r.warning: + if hasattr(r, "warnings") and r.warnings: + for w in r.warnings: warnings.warn(w) # Resolve either a result or an error diff --git a/src/server.cpp b/src/server.cpp index c5f9bf7..f0f2563 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -116,6 +117,20 @@ static inline py::object error_response(std::string const& msg) { return py::dict("error"_a=py::dict("message"_a=msg)); } +std::vector warning_list; +static void showwarning(py::object message, + py::object category, + py::object filename, + py::object lineno, + py::object file, + py::object line) { + + if(verbose & Verbose::NOISY) + fmt::print(stderr, "... captured warning '{}'\n", py::str(message).cast()); + + warning_list.push_back(py::str(message).cast()); +} + static py::dict tuber_server_invoke(py::dict ®istry, py::dict const& call, json_loads_t const& json_loads, @@ -167,13 +182,23 @@ static py::dict tuber_server_invoke(py::dict ®istry, /* Dispatch to Python - failures emerge as exceptions */ timed_scope ts("Python dispatch"); - py::object response = m(*python_args, **python_kwargs); + py::object response = py::none(); + try { + response = py::dict("result"_a=m(*python_args, **python_kwargs)); + } catch(std::exception &e) { + response = error_response(e.what()); + } + + /* Capture warnings, if any */ + if(!warning_list.empty()) { + response["warnings"] = warning_list; + warning_list.clear(); + } if(verbose & Verbose::NOISY) fmt::print(stderr, "... response was {}\n", json_dumps(response)); - /* Cast back to JSON, wrap in a result object, and return */ - return py::dict("result"_a=response); + return response; } if(verbose & Verbose::NOISY) @@ -233,19 +258,27 @@ class DLL_LOCAL tuber_resource : public http_resource { * list to have the expected size. */ py::list result(py::len(request_list)); - for(size_t i=0; i