kedro-org / kedro-viz

Visualise your Kedro data and machine-learning pipelines and track your experiments.
https://demo.kedro.org
Apache License 2.0
672 stars 110 forks source link

Defining and using a custom resolver breaks the "kedro viz run" command #1712

Open francisduval opened 8 months ago

francisduval commented 8 months ago

Description

When using a custom resolver, the pipeline runs normally, but it becomes impossible to use the kedro viz run command.

Context

I want to be able to use a custom resolver while still be able to visualize my pipelines.

Steps to Reproduce

Define a custom resolver, i.e. put those lines in settings.py (this resolver transforms an activation function given as a string like 'ReLU' into a class object):

import torch
custom_resolvers = {'act_resolver': lambda liste: [getattr(torch.nn, element) for element in liste]}
config_loader = OmegaConfigLoader(conf_source='.', custom_resolvers=custom_resolvers)

In parameters.yml, use your resolver:

grid:
  batch_size: [64]
  optimizer__lr: [0.0001, 0.001]
  module__act: '${act_resolver: [ReLU, Tanh]}'

Then, run kedro viz run

Expected Result

Should render the kedro visualization.

Actual Result

The Kedro visualization does not render, and this error is thrown:

ValueError: [TypeError("'builtin_function_or_method' object is not iterable"), TypeError('vars() argument must have __dict__ attribute')].
ERROR    Exception in ASGI application
Traceback:
C:\Users\XG04377\.conda\envs\monenv\lib\site-packages\uvicorn\protocols\http\httptools_impl.py:435 in run_asgi
C:\Users\XG04377\.conda\envs\monenv\lib\site-packages\uvicorn\middleware\proxy_headers.py:78 in __call__
C:\Users\XG04377\.conda\envs\monenv\lib\site-packages\uvicorn\middleware\message_logger.py:86 in __call__
 C:\Users\XG04377\.conda\envs\monenv\lib\site-packages\uvicorn\middleware\message_logger.py:82 in __call__
C:\Users\XG04377\.conda\envs\monenv\lib\site-packages\fastapi\applications.py:1054 in __call__
C:\Users\XG04377\.conda\envs\monenv\lib\site-packages\starlette\applications.py:116 in __call__
C:\Users\XG04377\.conda\envs\monenv\lib\site-packages\starlette\middleware\errors.py:186 in __call__
C:\Users\XG04377\.conda\envs\monenv\lib\site-packages\starlette\middleware\errors.py:164 in __call__
C:\Users\XG04377\.conda\envs\monenv\lib\site-packages\starlette\middleware\exceptions.py:62 in __call__
C:\Users\XG04377\.conda\envs\monenv\lib\site-packages\starlette\_exception_handler.py:55 in wrapped_app
C:\Users\XG04377\.conda\envs\monenv\lib\site-packages\starlette\_exception_handler.py:44 in wrapped_app
C:\Users\XG04377\.conda\envs\monenv\lib\site-packages\starlette\routing.py:746 in __call__
C:\Users\XG04377\.conda\envs\monenv\lib\site-packages\starlette\routing.py:288 in handle
C:\Users\XG04377\.conda\envs\monenv\lib\site-packages\starlette\routing.py:75 in app
C:\Users\XG04377\.conda\envs\monenv\lib\site-packages\starlette\_exception_handler.py:55 in wrapped_app
C:\Users\XG04377\.conda\envs\monenv\lib\site-packages\starlette\_exception_handler.py:44 in wrapped_app
C:\Users\XG04377\.conda\envs\monenv\lib\site-packages\starlette\routing.py:70 in app
C:\Users\XG04377\.conda\envs\monenv\lib\site-packages\fastapi\routing.py:315 in app
C:\Users\XG04377\.conda\envs\monenv\lib\site-packages\fastapi\routing.py:170 in serialize_response
C:\Users\XG04377\.conda\envs\monenv\lib\site-packages\fastapi\encoders.py:235 in jsonable_encoder
C:\Users\XG04377\.conda\envs\monenv\lib\site-packages\fastapi\encoders.py:287 in jsonable_encoder
C:\Users\XG04377\.conda\envs\monenv\lib\site-packages\fastapi\encoders.py:301 in jsonable_encoder
C:\Users\XG04377\.conda\envs\monenv\lib\site-packages\fastapi\encoders.py:287 in jsonable_encoder
And it goes on with jsonable_encoder all the way down.
Traceback (most recent call last):
  File "C:\Users\XG04377\.conda\envs\monenv\lib\site-packages\kedro_viz\launchers\cli.py", line 187, in run
    _wait_for(func=_check_viz_up, host=host, port=port)
  File "C:\Users\XG04377\.conda\envs\monenv\lib\site-packages\kedro_viz\launchers\utils.py", line 58, in _wait_for
    raise WaitForException(
kedro_viz.launchers.utils.WaitForException: func: <function _check_viz_up at 0x0000025B5F794AF0>, didn't return True within specified timeout: 60
kedro.framework.cli.utils.KedroCliError: func: <function _check_viz_up at 0x0000025B5F794AF0>, didn't return True within specified timeout: 60
Run with --verbose to see the full exception

Your Environment

conda environment kedro version 0.19.1 Python 3.10.13 Windows 10, Version 21H2 (version du système d'exploitation 19044.3803)

astrojuanlu commented 8 months ago

I can reproduce this on macOS.

  1. kedro new --name=test-custom-resolver-viz --starter=spaceflights-pandas everything works ✔️
  2. pip install torch everything still works ✔️
  3. Add custom resolver to src/test_custom_resolver_viz/settings.py as explained in https://github.com/kedro-org/kedro-viz/issues/1712#issue-2090023920 everything still works ✔️
  4. Add the module__act from https://github.com/kedro-org/kedro-viz/issues/1712#issue-2090023920 to conf/base/parameters_data_science.yml now kedro viz run fails 💥
``` │ 114 │ │ │ │ │ custom_encoder=custom_encoder, │ │ 115 │ │ │ │ │ sqlalchemy_safe=sqlalchemy_safe, │ │ 116 │ │ │ │ ) │ │ ❱ 117 │ │ │ │ encoded_value = jsonable_encoder( │ │ 118 │ │ │ │ │ value, │ │ 119 │ │ │ │ │ by_alias=by_alias, │ │ 120 │ │ │ │ │ exclude_unset=exclude_unset, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/encoders.py:160 in jsonable_encoder │ │ │ │ 157 │ │ │ data = vars(obj) │ │ 158 │ │ except Exception as e: │ │ 159 │ │ │ errors.append(e) │ │ ❱ 160 │ │ │ raise ValueError(errors) from e │ │ 161 │ return jsonable_encoder( │ │ 162 │ │ data, │ │ 163 │ │ include=include, │ ╰─────────────────────────────────────────────────────────────────╯ ValueError: [TypeError("'builtin_function_or_method' object is not iterable"), TypeError('vars() argument must have __dict__ attribute')] [01/19/24 10:50:22] ERROR Exception in ASGI application httptools_impl.py:440 ╭─────────────── Traceback (most recent call last) ───────────────╮ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/uvicorn/protocols/http/httptools_impl.py:435 in │ │ run_asgi │ │ │ │ 432 │ # ASGI exception wrapper │ │ 433 │ async def run_asgi(self, app: "ASGI3Application") -> │ │ 434 │ │ try: │ │ ❱ 435 │ │ │ result = await app( # type: ignore[func-retu │ │ 436 │ │ │ │ self.scope, self.receive, self.send │ │ 437 │ │ │ ) │ │ 438 │ │ except BaseException as exc: │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/uvicorn/middleware/proxy_headers.py:78 in __call__ │ │ │ │ 75 │ │ │ │ │ port = 0 │ │ 76 │ │ │ │ │ scope["client"] = (host, port) # type │ │ 77 │ │ │ │ ❱ 78 │ │ return await self.app(scope, receive, send) │ │ 79 │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/uvicorn/middleware/message_logger.py:86 in __call__ │ │ │ │ 83 │ │ except BaseException as exc: │ │ 84 │ │ │ log_text = "%s [%d] Raised exception" │ │ 85 │ │ │ self.logger.trace(log_text, prefix, task_count │ │ ❱ 86 │ │ │ raise exc from None │ │ 87 │ │ else: │ │ 88 │ │ │ log_text = "%s [%d] Completed" │ │ 89 │ │ │ self.logger.trace(log_text, prefix, task_count │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/uvicorn/middleware/message_logger.py:82 in __call__ │ │ │ │ 79 │ │ log_text = "%s [%d] Started scope=%s" │ │ 80 │ │ self.logger.trace(log_text, prefix, task_counter, │ │ 81 │ │ try: │ │ ❱ 82 │ │ │ await self.app(scope, inner_receive, inner_sen │ │ 83 │ │ except BaseException as exc: │ │ 84 │ │ │ log_text = "%s [%d] Raised exception" │ │ 85 │ │ │ self.logger.trace(log_text, prefix, task_count │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/applications.py:276 in __call__ │ │ │ │ 273 │ async def __call__(self, scope: Scope, receive: Recei │ │ 274 │ │ if self.root_path: │ │ 275 │ │ │ scope["root_path"] = self.root_path │ │ ❱ 276 │ │ await super().__call__(scope, receive, send) │ │ 277 │ │ │ 278 │ def add_api_route( │ │ 279 │ │ self, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/starlette/applications.py:122 in __call__ │ │ │ │ 119 │ │ scope["app"] = self │ │ 120 │ │ if self.middleware_stack is None: │ │ 121 │ │ │ self.middleware_stack = self.build_middleware │ │ ❱ 122 │ │ await self.middleware_stack(scope, receive, send) │ │ 123 │ │ │ 124 │ def on_event(self, event_type: str) -> typing.Callabl │ │ 125 │ │ return self.router.on_event(event_type) │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/starlette/middleware/errors.py:184 in __call__ │ │ │ │ 181 │ │ │ # We always continue to raise the exception. │ │ 182 │ │ │ # This allows servers to log the error, or al │ │ 183 │ │ │ # to optionally raise the error within the te │ │ ❱ 184 │ │ │ raise exc │ │ 185 │ │ │ 186 │ def format_line( │ │ 187 │ │ self, index: int, line: str, frame_lineno: int, f │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/starlette/middleware/errors.py:162 in __call__ │ │ │ │ 159 │ │ │ await send(message) │ │ 160 │ │ │ │ 161 │ │ try: │ │ ❱ 162 │ │ │ await self.app(scope, receive, _send) │ │ 163 │ │ except Exception as exc: │ │ 164 │ │ │ request = Request(scope) │ │ 165 │ │ │ if self.debug: │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/starlette/middleware/exceptions.py:79 in __call__ │ │ │ │ 76 │ │ │ │ handler = self._lookup_exception_handler( │ │ 77 │ │ │ │ │ 78 │ │ │ if handler is None: │ │ ❱ 79 │ │ │ │ raise exc │ │ 80 │ │ │ │ │ 81 │ │ │ if response_started: │ │ 82 │ │ │ │ msg = "Caught handled exception, but resp │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/starlette/middleware/exceptions.py:68 in __call__ │ │ │ │ 65 │ │ │ await send(message) │ │ 66 │ │ │ │ 67 │ │ try: │ │ ❱ 68 │ │ │ await self.app(scope, receive, sender) │ │ 69 │ │ except Exception as exc: │ │ 70 │ │ │ handler = None │ │ 71 │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/middleware/asyncexitstack.py:21 in __call__ │ │ │ │ 18 │ │ │ │ │ await self.app(scope, receive, send) │ │ 19 │ │ │ │ except Exception as e: │ │ 20 │ │ │ │ │ dependency_exception = e │ │ ❱ 21 │ │ │ │ │ raise e │ │ 22 │ │ │ if dependency_exception: │ │ 23 │ │ │ │ # This exception was possibly handled by t │ │ 24 │ │ │ │ # still bubble up so that the ServerErrorM │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/middleware/asyncexitstack.py:18 in __call__ │ │ │ │ 15 │ │ │ async with AsyncExitStack() as stack: │ │ 16 │ │ │ │ scope[self.context_name] = stack │ │ 17 │ │ │ │ try: │ │ ❱ 18 │ │ │ │ │ await self.app(scope, receive, send) │ │ 19 │ │ │ │ except Exception as e: │ │ 20 │ │ │ │ │ dependency_exception = e │ │ 21 │ │ │ │ │ raise e │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/starlette/routing.py:718 in __call__ │ │ │ │ 715 │ │ │ match, child_scope = route.matches(scope) │ │ 716 │ │ │ if match == Match.FULL: │ │ 717 │ │ │ │ scope.update(child_scope) │ │ ❱ 718 │ │ │ │ await route.handle(scope, receive, send) │ │ 719 │ │ │ │ return │ │ 720 │ │ │ elif match == Match.PARTIAL and partial is No │ │ 721 │ │ │ │ partial = route │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/starlette/routing.py:276 in handle │ │ │ │ 273 │ │ │ │ ) │ │ 274 │ │ │ await response(scope, receive, send) │ │ 275 │ │ else: │ │ ❱ 276 │ │ │ await self.app(scope, receive, send) │ │ 277 │ │ │ 278 │ def __eq__(self, other: typing.Any) -> bool: │ │ 279 │ │ return ( │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/starlette/routing.py:66 in app │ │ │ │ 63 │ async def app(scope: Scope, receive: Receive, send: S │ │ 64 │ │ request = Request(scope, receive=receive, send=se │ │ 65 │ │ if is_coroutine: │ │ ❱ 66 │ │ │ response = await func(request) │ │ 67 │ │ else: │ │ 68 │ │ │ response = await run_in_threadpool(func, requ │ │ 69 │ │ await response(scope, receive, send) │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/routing.py:255 in app │ │ │ │ 252 │ │ │ │ response_args["status_code"] = current_s │ │ 253 │ │ │ if sub_response.status_code: │ │ 254 │ │ │ │ response_args["status_code"] = sub_respo │ │ ❱ 255 │ │ │ content = await serialize_response( │ │ 256 │ │ │ │ field=response_field, │ │ 257 │ │ │ │ response_content=raw_response, │ │ 258 │ │ │ │ include=response_model_include, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/routing.py:142 in serialize_response │ │ │ │ 139 │ │ │ errors.extend(errors_) │ │ 140 │ │ if errors: │ │ 141 │ │ │ raise ValidationError(errors, field.type_) │ │ ❱ 142 │ │ return jsonable_encoder( │ │ 143 │ │ │ value, │ │ 144 │ │ │ include=include, │ │ 145 │ │ │ exclude=exclude, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/encoders.py:66 in jsonable_encoder │ │ │ │ 63 │ │ ) │ │ 64 │ │ if "__root__" in obj_dict: │ │ 65 │ │ │ obj_dict = obj_dict["__root__"] │ │ ❱ 66 │ │ return jsonable_encoder( │ │ 67 │ │ │ obj_dict, │ │ 68 │ │ │ exclude_none=exclude_none, │ │ 69 │ │ │ exclude_defaults=exclude_defaults, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/encoders.py:117 in jsonable_encoder │ │ │ │ 114 │ │ │ │ │ custom_encoder=custom_encoder, │ │ 115 │ │ │ │ │ sqlalchemy_safe=sqlalchemy_safe, │ │ 116 │ │ │ │ ) │ │ ❱ 117 │ │ │ │ encoded_value = jsonable_encoder( │ │ 118 │ │ │ │ │ value, │ │ 119 │ │ │ │ │ by_alias=by_alias, │ │ 120 │ │ │ │ │ exclude_unset=exclude_unset, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/encoders.py:131 in jsonable_encoder │ │ │ │ 128 │ │ encoded_list = [] │ │ 129 │ │ for item in obj: │ │ 130 │ │ │ encoded_list.append( │ │ ❱ 131 │ │ │ │ jsonable_encoder( │ │ 132 │ │ │ │ │ item, │ │ 133 │ │ │ │ │ include=include, │ │ 134 │ │ │ │ │ exclude=exclude, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/encoders.py:117 in jsonable_encoder │ │ │ │ 114 │ │ │ │ │ custom_encoder=custom_encoder, │ │ 115 │ │ │ │ │ sqlalchemy_safe=sqlalchemy_safe, │ │ 116 │ │ │ │ ) │ │ ❱ 117 │ │ │ │ encoded_value = jsonable_encoder( │ │ 118 │ │ │ │ │ value, │ │ 119 │ │ │ │ │ by_alias=by_alias, │ │ 120 │ │ │ │ │ exclude_unset=exclude_unset, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/encoders.py:117 in jsonable_encoder │ │ │ │ 114 │ │ │ │ │ custom_encoder=custom_encoder, │ │ 115 │ │ │ │ │ sqlalchemy_safe=sqlalchemy_safe, │ │ 116 │ │ │ │ ) │ │ ❱ 117 │ │ │ │ encoded_value = jsonable_encoder( │ │ 118 │ │ │ │ │ value, │ │ 119 │ │ │ │ │ by_alias=by_alias, │ │ 120 │ │ │ │ │ exclude_unset=exclude_unset, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/encoders.py:117 in jsonable_encoder │ │ │ │ 114 │ │ │ │ │ custom_encoder=custom_encoder, │ │ 115 │ │ │ │ │ sqlalchemy_safe=sqlalchemy_safe, │ │ 116 │ │ │ │ ) │ │ ❱ 117 │ │ │ │ encoded_value = jsonable_encoder( │ │ 118 │ │ │ │ │ value, │ │ 119 │ │ │ │ │ by_alias=by_alias, │ │ 120 │ │ │ │ │ exclude_unset=exclude_unset, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/encoders.py:131 in jsonable_encoder │ │ │ │ 128 │ │ encoded_list = [] │ │ 129 │ │ for item in obj: │ │ 130 │ │ │ encoded_list.append( │ │ ❱ 131 │ │ │ │ jsonable_encoder( │ │ 132 │ │ │ │ │ item, │ │ 133 │ │ │ │ │ include=include, │ │ 134 │ │ │ │ │ exclude=exclude, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/encoders.py:161 in jsonable_encoder │ │ │ │ 158 │ │ except Exception as e: │ │ 159 │ │ │ errors.append(e) │ │ 160 │ │ │ raise ValueError(errors) from e │ │ ❱ 161 │ return jsonable_encoder( │ │ 162 │ │ data, │ │ 163 │ │ include=include, │ │ 164 │ │ exclude=exclude, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/encoders.py:161 in jsonable_encoder │ │ │ │ 158 │ │ except Exception as e: │ │ 159 │ │ │ errors.append(e) │ │ 160 │ │ │ raise ValueError(errors) from e │ │ ❱ 161 │ return jsonable_encoder( │ │ 162 │ │ data, │ │ 163 │ │ include=include, │ │ 164 │ │ exclude=exclude, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/encoders.py:117 in jsonable_encoder │ │ │ │ 114 │ │ │ │ │ custom_encoder=custom_encoder, │ │ 115 │ │ │ │ │ sqlalchemy_safe=sqlalchemy_safe, │ │ 116 │ │ │ │ ) │ │ ❱ 117 │ │ │ │ encoded_value = jsonable_encoder( │ │ 118 │ │ │ │ │ value, │ │ 119 │ │ │ │ │ by_alias=by_alias, │ │ 120 │ │ │ │ │ exclude_unset=exclude_unset, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/encoders.py:117 in jsonable_encoder │ │ │ │ 114 │ │ │ │ │ custom_encoder=custom_encoder, │ │ 115 │ │ │ │ │ sqlalchemy_safe=sqlalchemy_safe, │ │ 116 │ │ │ │ ) │ │ ❱ 117 │ │ │ │ encoded_value = jsonable_encoder( │ │ 118 │ │ │ │ │ value, │ │ 119 │ │ │ │ │ by_alias=by_alias, │ │ 120 │ │ │ │ │ exclude_unset=exclude_unset, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/encoders.py:161 in jsonable_encoder │ │ │ │ 158 │ │ except Exception as e: │ │ 159 │ │ │ errors.append(e) │ │ 160 │ │ │ raise ValueError(errors) from e │ │ ❱ 161 │ return jsonable_encoder( │ │ 162 │ │ data, │ │ 163 │ │ include=include, │ │ 164 │ │ exclude=exclude, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/encoders.py:161 in jsonable_encoder │ │ │ │ 158 │ │ except Exception as e: │ │ 159 │ │ │ errors.append(e) │ │ 160 │ │ │ raise ValueError(errors) from e │ │ ❱ 161 │ return jsonable_encoder( │ │ 162 │ │ data, │ │ 163 │ │ include=include, │ │ 164 │ │ exclude=exclude, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/encoders.py:117 in jsonable_encoder │ │ │ │ 114 │ │ │ │ │ custom_encoder=custom_encoder, │ │ 115 │ │ │ │ │ sqlalchemy_safe=sqlalchemy_safe, │ │ 116 │ │ │ │ ) │ │ ❱ 117 │ │ │ │ encoded_value = jsonable_encoder( │ │ 118 │ │ │ │ │ value, │ │ 119 │ │ │ │ │ by_alias=by_alias, │ │ 120 │ │ │ │ │ exclude_unset=exclude_unset, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/encoders.py:160 in jsonable_encoder │ │ │ │ 157 │ │ │ data = vars(obj) │ │ 158 │ │ except Exception as e: │ │ 159 │ │ │ errors.append(e) │ │ ❱ 160 │ │ │ raise ValueError(errors) from e │ │ 161 │ return jsonable_encoder( │ │ 162 │ │ data, │ │ 163 │ │ include=include, │ ╰─────────────────────────────────────────────────────────────────╯ ValueError: [TypeError("'builtin_function_or_method' object is not iterable"), TypeError('vars() argument must have __dict__ attribute')] [01/19/24 10:50:23] ERROR Exception in ASGI application httptools_impl.py:440 ╭─────────────── Traceback (most recent call last) ───────────────╮ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/uvicorn/protocols/http/httptools_impl.py:435 in │ │ run_asgi │ │ │ │ 432 │ # ASGI exception wrapper │ │ 433 │ async def run_asgi(self, app: "ASGI3Application") -> │ │ 434 │ │ try: │ │ ❱ 435 │ │ │ result = await app( # type: ignore[func-retu │ │ 436 │ │ │ │ self.scope, self.receive, self.send │ │ 437 │ │ │ ) │ │ 438 │ │ except BaseException as exc: │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/uvicorn/middleware/proxy_headers.py:78 in __call__ │ │ │ │ 75 │ │ │ │ │ port = 0 │ │ 76 │ │ │ │ │ scope["client"] = (host, port) # type │ │ 77 │ │ │ │ ❱ 78 │ │ return await self.app(scope, receive, send) │ │ 79 │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/uvicorn/middleware/message_logger.py:86 in __call__ │ │ │ │ 83 │ │ except BaseException as exc: │ │ 84 │ │ │ log_text = "%s [%d] Raised exception" │ │ 85 │ │ │ self.logger.trace(log_text, prefix, task_count │ │ ❱ 86 │ │ │ raise exc from None │ │ 87 │ │ else: │ │ 88 │ │ │ log_text = "%s [%d] Completed" │ │ 89 │ │ │ self.logger.trace(log_text, prefix, task_count │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/uvicorn/middleware/message_logger.py:82 in __call__ │ │ │ │ 79 │ │ log_text = "%s [%d] Started scope=%s" │ │ 80 │ │ self.logger.trace(log_text, prefix, task_counter, │ │ 81 │ │ try: │ │ ❱ 82 │ │ │ await self.app(scope, inner_receive, inner_sen │ │ 83 │ │ except BaseException as exc: │ │ 84 │ │ │ log_text = "%s [%d] Raised exception" │ │ 85 │ │ │ self.logger.trace(log_text, prefix, task_count │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/applications.py:276 in __call__ │ │ │ │ 273 │ async def __call__(self, scope: Scope, receive: Recei │ │ 274 │ │ if self.root_path: │ │ 275 │ │ │ scope["root_path"] = self.root_path │ │ ❱ 276 │ │ await super().__call__(scope, receive, send) │ │ 277 │ │ │ 278 │ def add_api_route( │ │ 279 │ │ self, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/starlette/applications.py:122 in __call__ │ │ │ │ 119 │ │ scope["app"] = self │ │ 120 │ │ if self.middleware_stack is None: │ │ 121 │ │ │ self.middleware_stack = self.build_middleware │ │ ❱ 122 │ │ await self.middleware_stack(scope, receive, send) │ │ 123 │ │ │ 124 │ def on_event(self, event_type: str) -> typing.Callabl │ │ 125 │ │ return self.router.on_event(event_type) │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/starlette/middleware/errors.py:184 in __call__ │ │ │ │ 181 │ │ │ # We always continue to raise the exception. │ │ 182 │ │ │ # This allows servers to log the error, or al │ │ 183 │ │ │ # to optionally raise the error within the te │ │ ❱ 184 │ │ │ raise exc │ │ 185 │ │ │ 186 │ def format_line( │ │ 187 │ │ self, index: int, line: str, frame_lineno: int, f │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/starlette/middleware/errors.py:162 in __call__ │ │ │ │ 159 │ │ │ await send(message) │ │ 160 │ │ │ │ 161 │ │ try: │ │ ❱ 162 │ │ │ await self.app(scope, receive, _send) │ │ 163 │ │ except Exception as exc: │ │ 164 │ │ │ request = Request(scope) │ │ 165 │ │ │ if self.debug: │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/starlette/middleware/exceptions.py:79 in __call__ │ │ │ │ 76 │ │ │ │ handler = self._lookup_exception_handler( │ │ 77 │ │ │ │ │ 78 │ │ │ if handler is None: │ │ ❱ 79 │ │ │ │ raise exc │ │ 80 │ │ │ │ │ 81 │ │ │ if response_started: │ │ 82 │ │ │ │ msg = "Caught handled exception, but resp │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/starlette/middleware/exceptions.py:68 in __call__ │ │ │ │ 65 │ │ │ await send(message) │ │ 66 │ │ │ │ 67 │ │ try: │ │ ❱ 68 │ │ │ await self.app(scope, receive, sender) │ │ 69 │ │ except Exception as exc: │ │ 70 │ │ │ handler = None │ │ 71 │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/middleware/asyncexitstack.py:21 in __call__ │ │ │ │ 18 │ │ │ │ │ await self.app(scope, receive, send) │ │ 19 │ │ │ │ except Exception as e: │ │ 20 │ │ │ │ │ dependency_exception = e │ │ ❱ 21 │ │ │ │ │ raise e │ │ 22 │ │ │ if dependency_exception: │ │ 23 │ │ │ │ # This exception was possibly handled by t │ │ 24 │ │ │ │ # still bubble up so that the ServerErrorM │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/middleware/asyncexitstack.py:18 in __call__ │ │ │ │ 15 │ │ │ async with AsyncExitStack() as stack: │ │ 16 │ │ │ │ scope[self.context_name] = stack │ │ 17 │ │ │ │ try: │ │ ❱ 18 │ │ │ │ │ await self.app(scope, receive, send) │ │ 19 │ │ │ │ except Exception as e: │ │ 20 │ │ │ │ │ dependency_exception = e │ │ 21 │ │ │ │ │ raise e │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/starlette/routing.py:718 in __call__ │ │ │ │ 715 │ │ │ match, child_scope = route.matches(scope) │ │ 716 │ │ │ if match == Match.FULL: │ │ 717 │ │ │ │ scope.update(child_scope) │ │ ❱ 718 │ │ │ │ await route.handle(scope, receive, send) │ │ 719 │ │ │ │ return │ │ 720 │ │ │ elif match == Match.PARTIAL and partial is No │ │ 721 │ │ │ │ partial = route │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/starlette/routing.py:276 in handle │ │ │ │ 273 │ │ │ │ ) │ │ 274 │ │ │ await response(scope, receive, send) │ │ 275 │ │ else: │ │ ❱ 276 │ │ │ await self.app(scope, receive, send) │ │ 277 │ │ │ 278 │ def __eq__(self, other: typing.Any) -> bool: │ │ 279 │ │ return ( │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/starlette/routing.py:66 in app │ │ │ │ 63 │ async def app(scope: Scope, receive: Receive, send: S │ │ 64 │ │ request = Request(scope, receive=receive, send=se │ │ 65 │ │ if is_coroutine: │ │ ❱ 66 │ │ │ response = await func(request) │ │ 67 │ │ else: │ │ 68 │ │ │ response = await run_in_threadpool(func, requ │ │ 69 │ │ await response(scope, receive, send) │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/routing.py:255 in app │ │ │ │ 252 │ │ │ │ response_args["status_code"] = current_s │ │ 253 │ │ │ if sub_response.status_code: │ │ 254 │ │ │ │ response_args["status_code"] = sub_respo │ │ ❱ 255 │ │ │ content = await serialize_response( │ │ 256 │ │ │ │ field=response_field, │ │ 257 │ │ │ │ response_content=raw_response, │ │ 258 │ │ │ │ include=response_model_include, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/routing.py:142 in serialize_response │ │ │ │ 139 │ │ │ errors.extend(errors_) │ │ 140 │ │ if errors: │ │ 141 │ │ │ raise ValidationError(errors, field.type_) │ │ ❱ 142 │ │ return jsonable_encoder( │ │ 143 │ │ │ value, │ │ 144 │ │ │ include=include, │ │ 145 │ │ │ exclude=exclude, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/encoders.py:66 in jsonable_encoder │ │ │ │ 63 │ │ ) │ │ 64 │ │ if "__root__" in obj_dict: │ │ 65 │ │ │ obj_dict = obj_dict["__root__"] │ │ ❱ 66 │ │ return jsonable_encoder( │ │ 67 │ │ │ obj_dict, │ │ 68 │ │ │ exclude_none=exclude_none, │ │ 69 │ │ │ exclude_defaults=exclude_defaults, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/encoders.py:117 in jsonable_encoder │ │ │ │ 114 │ │ │ │ │ custom_encoder=custom_encoder, │ │ 115 │ │ │ │ │ sqlalchemy_safe=sqlalchemy_safe, │ │ 116 │ │ │ │ ) │ │ ❱ 117 │ │ │ │ encoded_value = jsonable_encoder( │ │ 118 │ │ │ │ │ value, │ │ 119 │ │ │ │ │ by_alias=by_alias, │ │ 120 │ │ │ │ │ exclude_unset=exclude_unset, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/encoders.py:131 in jsonable_encoder │ │ │ │ 128 │ │ encoded_list = [] │ │ 129 │ │ for item in obj: │ │ 130 │ │ │ encoded_list.append( │ │ ❱ 131 │ │ │ │ jsonable_encoder( │ │ 132 │ │ │ │ │ item, │ │ 133 │ │ │ │ │ include=include, │ │ 134 │ │ │ │ │ exclude=exclude, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/encoders.py:117 in jsonable_encoder │ │ │ │ 114 │ │ │ │ │ custom_encoder=custom_encoder, │ │ 115 │ │ │ │ │ sqlalchemy_safe=sqlalchemy_safe, │ │ 116 │ │ │ │ ) │ │ ❱ 117 │ │ │ │ encoded_value = jsonable_encoder( │ │ 118 │ │ │ │ │ value, │ │ 119 │ │ │ │ │ by_alias=by_alias, │ │ 120 │ │ │ │ │ exclude_unset=exclude_unset, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/encoders.py:117 in jsonable_encoder │ │ │ │ 114 │ │ │ │ │ custom_encoder=custom_encoder, │ │ 115 │ │ │ │ │ sqlalchemy_safe=sqlalchemy_safe, │ │ 116 │ │ │ │ ) │ │ ❱ 117 │ │ │ │ encoded_value = jsonable_encoder( │ │ 118 │ │ │ │ │ value, │ │ 119 │ │ │ │ │ by_alias=by_alias, │ │ 120 │ │ │ │ │ exclude_unset=exclude_unset, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/encoders.py:117 in jsonable_encoder │ │ │ │ 114 │ │ │ │ │ custom_encoder=custom_encoder, │ │ 115 │ │ │ │ │ sqlalchemy_safe=sqlalchemy_safe, │ │ 116 │ │ │ │ ) │ │ ❱ 117 │ │ │ │ encoded_value = jsonable_encoder( │ │ 118 │ │ │ │ │ value, │ │ 119 │ │ │ │ │ by_alias=by_alias, │ │ 120 │ │ │ │ │ exclude_unset=exclude_unset, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/encoders.py:131 in jsonable_encoder │ │ │ │ 128 │ │ encoded_list = [] │ │ 129 │ │ for item in obj: │ │ 130 │ │ │ encoded_list.append( │ │ ❱ 131 │ │ │ │ jsonable_encoder( │ │ 132 │ │ │ │ │ item, │ │ 133 │ │ │ │ │ include=include, │ │ 134 │ │ │ │ │ exclude=exclude, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/encoders.py:161 in jsonable_encoder │ │ │ │ 158 │ │ except Exception as e: │ │ 159 │ │ │ errors.append(e) │ │ 160 │ │ │ raise ValueError(errors) from e │ │ ❱ 161 │ return jsonable_encoder( │ │ 162 │ │ data, │ │ 163 │ │ include=include, │ │ 164 │ │ exclude=exclude, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/encoders.py:161 in jsonable_encoder │ │ │ │ 158 │ │ except Exception as e: │ │ 159 │ │ │ errors.append(e) │ │ 160 │ │ │ raise ValueError(errors) from e │ │ ❱ 161 │ return jsonable_encoder( │ │ 162 │ │ data, │ │ 163 │ │ include=include, │ │ 164 │ │ exclude=exclude, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/encoders.py:117 in jsonable_encoder │ │ │ │ 114 │ │ │ │ │ custom_encoder=custom_encoder, │ │ 115 │ │ │ │ │ sqlalchemy_safe=sqlalchemy_safe, │ │ 116 │ │ │ │ ) │ │ ❱ 117 │ │ │ │ encoded_value = jsonable_encoder( │ │ 118 │ │ │ │ │ value, │ │ 119 │ │ │ │ │ by_alias=by_alias, │ │ 120 │ │ │ │ │ exclude_unset=exclude_unset, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/encoders.py:117 in jsonable_encoder │ │ │ │ 114 │ │ │ │ │ custom_encoder=custom_encoder, │ │ 115 │ │ │ │ │ sqlalchemy_safe=sqlalchemy_safe, │ │ 116 │ │ │ │ ) │ │ ❱ 117 │ │ │ │ encoded_value = jsonable_encoder( │ │ 118 │ │ │ │ │ value, │ │ 119 │ │ │ │ │ by_alias=by_alias, │ │ 120 │ │ │ │ │ exclude_unset=exclude_unset, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/encoders.py:161 in jsonable_encoder │ │ │ │ 158 │ │ except Exception as e: │ │ 159 │ │ │ errors.append(e) │ │ 160 │ │ │ raise ValueError(errors) from e │ │ ❱ 161 │ return jsonable_encoder( │ │ 162 │ │ data, │ │ 163 │ │ include=include, │ │ 164 │ │ exclude=exclude, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/encoders.py:161 in jsonable_encoder │ │ │ │ 158 │ │ except Exception as e: │ │ 159 │ │ │ errors.append(e) │ │ 160 │ │ │ raise ValueError(errors) from e │ │ ❱ 161 │ return jsonable_encoder( │ │ 162 │ │ data, │ │ 163 │ │ include=include, │ │ 164 │ │ exclude=exclude, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/encoders.py:117 in jsonable_encoder │ │ │ │ 114 │ │ │ │ │ custom_encoder=custom_encoder, │ │ 115 │ │ │ │ │ sqlalchemy_safe=sqlalchemy_safe, │ │ 116 │ │ │ │ ) │ │ ❱ 117 │ │ │ │ encoded_value = jsonable_encoder( │ │ 118 │ │ │ │ │ value, │ │ 119 │ │ │ │ │ by_alias=by_alias, │ │ 120 │ │ │ │ │ exclude_unset=exclude_unset, │ │ │ │ /Users/juan_cano/.micromamba/envs/kedro310/lib/python3.10/site- │ │ packages/fastapi/encoders.py:160 in jsonable_encoder │ │ │ │ 157 │ │ │ data = vars(obj) │ │ 158 │ │ except Exception as e: │ │ 159 │ │ │ errors.append(e) │ │ ❱ 160 │ │ │ raise ValueError(errors) from e │ │ 161 │ return jsonable_encoder( │ │ 162 │ │ data, │ │ 163 │ │ include=include, │ ╰─────────────────────────────────────────────────────────────────╯ ValueError: [TypeError("'builtin_function_or_method' object is not iterable"), TypeError('vars() argument must have __dict__ attribute')] ```

I don't think this is specific to PyTorch though, maybe Kedro Viz is trying to serialise the getattr in the lambda as JSON and failing.

francisduval commented 8 months ago

Thanks! One option that works is to use the getattr function directly in nodes.py.