Exemplo n.º 1
0
 def query(self, uri) -> Tuple[bool, str]:
     """Get information on given uri"""
     if uri == "/":
         hlogger.debug("Getting a list of all registered components")
         return True, self._get_comps_data()
     elif comp := self._components.get(uri, None):
         hlogger.debug("Getting component metadata: %s", comp)
         return True, self._get_comp_data(comp)
Exemplo n.º 2
0
 def _prepare_outputs(self, comp, returns) -> Tuple[bool, str]:
     outputs = []
     if not isinstance(returns, tuple):
         returns = (returns, )
     for out_param, out_result in zip(comp.outputs, returns):
         output_data = out_param.from_result(out_result)
         outputs.append(output_data)
     payload = {"values": outputs}
     hlogger.debug("Return payload: %s", payload)
     return True, json.dumps(payload, cls=_HopsEncoder)
Exemplo n.º 3
0
 def do_GET(self):
     # grab the path before url params
     comp_uri = self._get_comp_uri()
     res, results = self.hops.query(uri=comp_uri)
     hlogger.debug(f"{res} : {results}")
     if res:
         self._prep_response()
         self.wfile.write(results.encode(encoding="utf_8"))
     else:
         self._prep_response(status=404)
Exemplo n.º 4
0
 def query(self, uri) -> Tuple[bool, str]:
     """Get information on given uri"""
     if uri == "/":
         hlogger.debug("Getting a list of all registered components")
         return True, self._get_comps_data()
     else:
         comp = self._components.get(uri, None)
         if comp:
             hlogger.debug("Getting component metadata: %s", comp)
             return True, self._get_comp_data(comp)
     return False, self._return_with_err("Unknown Hops url")
Exemplo n.º 5
0
 def do_POST(self):
     # read the message and convert it into a python dictionary
     comp_uri = self._get_comp_uri()
     length = int(self.headers.get("Content-Length"))
     data = self.rfile.read(length)
     res, results = self.hops.solve(uri=comp_uri, payload=data)
     hlogger.debug(f"{res} : {results}")
     if res:
         self._prep_response()
         self.wfile.write(results.encode(encoding="utf_8"))
     else:
         # TODO: write proper errors
         self._prep_response(500, "Execution Error")
         self.wfile.write(results.encode(encoding="utf_8"))
Exemplo n.º 6
0
    def solve(self, uri, payload) -> Tuple[bool, str]:
        """Perform Solve on given uri"""
        if uri == "/":
            hlogger.debug("Nothing to solve on root")
            return False, self._return_with_err("Nothing to solve on root")

        # FIXME: remove support for legacy solve behaviour
        elif uri == "/solve":
            data = json.loads(payload)
            comp_name = data["pointer"]
            for comp in self._components.values():
                if comp_name == comp.uri.replace("/", ""):
                    hlogger.info("Solving using legacy API: %s", comp)
                    return self._process_solve_request(comp, payload)

        # FIXME: test this new api
        elif comp := self._components.get(uri, None):
            hlogger.info("Solving: %s", comp)
            return self._process_solve_request(comp, payload)
Exemplo n.º 7
0
    def _process_solve_request(self, comp, payload) -> Tuple[bool, str]:
        # parse payload for inputs
        res, inputs = self._prepare_inputs(comp, payload)
        if not res:
            hlogger.debug("Bad inputs: %s", inputs)
            return res, self._return_with_err("Bad inputs")

        # run
        try:
            solve_returned = self._solve(comp, inputs)
            hlogger.debug("Return data: %s", solve_returned)
            res, outputs = self._prepare_outputs(comp, solve_returned)
            return (
                res,
                outputs if res else self._return_with_err("Bad outputs"),
            )
        except Exception as solve_ex:
            # try to grab traceback data and create err msg
            _, _, exc_traceback = sys.exc_info()
            try:
                fmt_tb = traceback.format_tb(exc_traceback)
                # FIXME: can we safely assume we are only 2 levels in stack?
                ex_msg = "\n".join(fmt_tb[2:])
                ex_msg = str(solve_ex) + f"\n{ex_msg}"
            except Exception:
                # otherwise use exception str as msg
                ex_msg = str(solve_ex)

            hlogger.debug("Exception occured in handler: %s", ex_msg)
            return False, self._return_with_err(
                "Exception occured in handler:\n%s" % ex_msg)
Exemplo n.º 8
0
    def __new__(cls, app=None, debug=False, *args, **kwargs) -> base.HopsBase:
        # set logger level
        hlogger.setLevel(logging.DEBUG if debug else logging.INFO)

        # determine the correct middleware base on the source app being wrapped
        # when running standalone with no source apps
        if app is None:
            hlogger.debug("Using Hops default http server")
            params._init_rhino3dm()
            return hmw.HopsDefault()

        # if wrapping another app
        app_type = repr(app)
        # if app is Flask
        if app_type.startswith("<Flask"):
            hlogger.debug("Using Hops Flask middleware")
            params._init_rhino3dm()
            return hmw.HopsFlask(app, *args, **kwargs)

        # if wrapping rhinoinside
        # paractically this is not necessary. it is implemented this way
        # mostly to provide a level of consistency on how Hops is used
        elif app_type.startswith("<module 'rhinoinside'"):
            # detemine if running with rhino.inside.cpython
            # and init the param module accordingly
            if not Hops.is_inside():
                raise Exception("rhinoinside is not loaded yet")
            hlogger.debug("Using Hops default http server with rhinoinside")
            params._init_rhinoinside()
            return hmw.HopsDefault(*args, **kwargs)

        raise Exception("Unsupported app")
Exemplo n.º 9
0
 def __func_wrapper__(comp_func):
     # register python func as Hops component
     # determine name, and uri
     comp_name = name or comp_func.__qualname__
     uri = rule or f"/{comp_name}"
     # create component instance
     comp = HopsComponent(
         uri=uri,
         name=comp_name,
         nickname=nickname,
         desc=description or comp_func.__doc__,
         cat=category or DEFAULT_CATEGORY,
         subcat=subcategory or DEFAULT_SUBCATEGORY,
         icon=self._prepare_icon(icon) if icon is not None else None,
         inputs=inputs or [],
         outputs=outputs or [],
         handler=comp_func,
     )
     hlogger.debug("Component registered: %s", comp)
     # register by uri and solve uri, for fast lookup on query and solve
     self._components[uri] = comp
     self._components[comp.solve_uri] = comp
     return comp_func
Exemplo n.º 10
0
        def __func_wrapper__(comp_func):
            # register python func as Hops component
            if inputs:
                # inspect default parameters in function signature
                f_sig = inspect.signature(comp_func)
                f_params = f_sig.parameters.values()
                if len(inputs) != len(f_params):
                    raise Exception("Number of function parameters is "
                                    "different from defined Hops inputs")
                # apply function param default values in order
                # to defined Hops inputs. this will override any
                # previously defined default values
                for hinput, fparam in zip(inputs, f_params):
                    if fparam.default != inspect.Parameter.empty:
                        hinput.default = fparam.default

            # determine name, and uri
            comp_name = name or comp_func.__qualname__
            uri = rule or f"/{comp_name}"
            # create component instance
            comp = HopsComponent(
                uri=uri,
                name=comp_name,
                nickname=nickname,
                desc=description or comp_func.__doc__,
                cat=category or DEFAULT_CATEGORY,
                subcat=subcategory or DEFAULT_SUBCATEGORY,
                icon=self._prepare_icon(icon) if icon is not None else None,
                inputs=inputs or [],
                outputs=outputs or [],
                handler=comp_func,
            )
            hlogger.debug("Component registered: %s", comp)
            # register by uri and solve uri, for fast lookup on query and solve
            self._components[uri] = comp
            self._components[comp.solve_uri] = comp
            return comp_func