widgetti / solara

A Pure Python, React-style Framework for Scaling Your Jupyter and Web Apps
https://solara.dev
MIT License
1.89k stars 138 forks source link

combination of `__future__` `annotations` import and `dataclass` gives `AttributeError` #208

Closed Jhsmit closed 1 year ago

Jhsmit commented 1 year ago

The following example:

from __future__ import annotations
from typing import Optional

from dataclasses import dataclass, field
import solara

@dataclass(frozen=True)
class FilterItem:
    name: str
    min: Optional[float] = field(default=None)
    max: Optional[float] = field(default=None)

@solara.component
def Page():
    solara.Markdown("## Foobar")

Gives the following mysterious traceback:

Traceback (most recent call last):
  File "\..\.venv-py39\lib\runpy.py", line 197, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "\..\.venv-py39\lib\runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "\..\.venv-py39\Scripts\solara.exe\__main__.py", line 7, in <module>
  File "\..\.venv-py39\lib\site-packages\solara\__main__.py", line 688, in main
    cli()
  File "\..\.venv-py39\lib\site-packages\click\core.py", line 1130, in __call__
    return self.main(*args, **kwargs)
  File "\..\.venv-py39\lib\site-packages\rich_click\rich_group.py", line 21, in main
    rv = super().main(*args, standalone_mode=False, **kwargs)
  File "\..\.venv-py39\lib\site-packages\click\core.py", line 1055, in main
    rv = self.invoke(ctx)
  File "\..\.venv-py39\lib\site-packages\click\core.py", line 1657, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "\..\.venv-py39\lib\site-packages\click\core.py", line 1404, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "\..\.venv-py39\lib\site-packages\click\core.py", line 760, in invoke
    return __callback(*args, **kwargs)
  File "\..\.venv-py39\lib\site-packages\solara\__main__.py", line 410, in run
    start_server()
  File "\..\.venv-py39\lib\site-packages\solara\__main__.py", line 382, in start_server
    server.run()
  File "\..\.venv-py39\lib\site-packages\uvicorn\server.py", line 59, in run
    return asyncio.run(self.serve(sockets=sockets))
  File "\..\.venv-py39\lib\asyncio\runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "\..\.venv-py39\lib\asyncio\base_events.py", line 647, in run_until_complete
    return future.result()
  File "\..\.venv-py39\lib\site-packages\uvicorn\server.py", line 66, in serve
    config.load()
  File "\..\.venv-py39\lib\site-packages\uvicorn\config.py", line 471, in load
    self.loaded_app = import_from_string(self.app)
  File "\..\.venv-py39\lib\site-packages\uvicorn\importer.py", line 21, in import_from_string
    module = importlib.import_module(module_str)
  File "\..\.venv-py39\lib\importlib\__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 850, in exec_module
  File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
  File "\..\.venv-py39\lib\site-packages\solara\server\starlette.py", line 48, in <module>
    from . import app as appmod
  File "\..\.venv-py39\lib\site-packages\solara\server\app.py", line 538, in <module>
    apps["__default__"] = AppScript(os.environ.get("SOLARA_APP", "solara.website.pages:Page"))
  File "\..\.venv-py39\lib\site-packages\solara\server\app.py", line 79, in __init__
    app = self._execute()
  File "\..\.venv-py39\lib\site-packages\solara\server\app.py", line 115, in _execute
    routes = [solara.autorouting._generate_route_path(self.path, first=True)]
  File "\..\.venv-py39\lib\site-packages\solara\autorouting.py", line 496, in _generate_route_path
    module = source_to_module(subpath)
  File "\..\.venv-py39\lib\site-packages\solara\autorouting.py", line 36, in source_to_module
    exec(ast, mod.__dict__)
  File "C:\Users\jhsmi\pp\awesome-solara\awesome_solara\mwe_dataclass.py", line 10, in <module>
    class FilterItem:
  File "\..\.venv-py39\lib\dataclasses.py", line 1013, in wrap
    return _process_class(cls, init, repr, eq, order, unsafe_hash, frozen)
  File "\..\.venv-py39\lib\dataclasses.py", line 863, in _process_class
    cls_fields = [_get_field(cls, name, type)
  File "\..\.venv-py39\lib\dataclasses.py", line 863, in <listcomp>
    cls_fields = [_get_field(cls, name, type)
  File "\..\.venv-py39\lib\dataclasses.py", line 714, in _get_field
    and _is_type(f.type, cls, typing, typing.ClassVar,
  File "\..\.venv-py39\lib\dataclasses.py", line 660, in _is_type
    ns = sys.modules.get(cls.__module__).__dict__
AttributeError: 'NoneType' object has no attribute '__dict__'

It the combination of both the dataclass and the annotations import that gives the error. Workarounds are either not importing from the future or moving the dataclass to a separate file.

This is on windows python 3.9 solara 1.18

maartenbreddels commented 1 year ago

Thank you 🙏 Should be out soon