Textualize / textual

The lean application framework for Python. Build sophisticated user interfaces with a simple Python API. Run your apps in the terminal and a web browser.
https://textual.textualize.io/
MIT License
25.61k stars 788 forks source link

executable does not find textual.widget._tab_pane after building with pyinstaller #4912

Open nnako opened 2 months ago

nnako commented 2 months ago

On a Windows system I am trying to create an executable application based on an example using the file tabbed_content.py. The example can be found within docs/examples/widgets/.

To create the executable, I execute the following command line:

pyinstaller tabbed_content.py --onefile

I get the following version information:

161 INFO: PyInstaller: 6.10.0, contrib hooks: 2024.8
161 INFO: Python: 3.12.4
189 INFO: Platform: Windows-10-10.0.19045-SP0

No errors and no warnings visible.

Now, when I try to execute the newly created executable using

dist\tabbed_content.exe

, I get the following error output:

Traceback (most recent call last):
  File "tabbed_content.py", line 2, in <module>
  File "textual\widgets\__init__.py", line 106, in __getattr__
  File "importlib\__init__.py", line 90, in import_module
ModuleNotFoundError: No module named 'textual.widgets._tab_pane'
[PYI-1220:ERROR] Failed to execute script 'gui' due to unhandled exception!

in line #2 of the source code, I see the following statement:

from textual.widgets import Footer, Label, Markdown, TabbedContent, TabPane

When creating an executable using other examples like rich_log.py using the same process as described above, it runs just fine. So, I guess there is some conflict between pyinstaller and the tabbing feature. When executing any of the examples using the interactive python interpreter, they all run fine in my terminal, e.g.:

python tabbed_content.py

Any suggestions how to fix this issue?

github-actions[bot] commented 2 months ago

We found the following entries in the FAQ which you may find helpful:

Feel free to close this issue if you found an answer in the FAQ. Otherwise, please give us a little time to review.

This is an automated reply, generated by FAQtory

TomJGooding commented 2 months ago

I'm not familiar with pyinstaller, but from a quick scan of their docs have you tried --hidden-import=textual.widgets._tab_pane?

nnako commented 2 months ago

Hi @TomJGooding , now it does build the application correctly. I can start and run it properly. Thanks.

But, should this be the "normal" case that single imports must have to be added like that? What would be the conceptual difference between this and another example source code? Why would the other not fail? I would expect pyinstaller to be able to just build the application without additional settings to cope with special cases. If I knew the code base better, I would try to offer a PR to "fix" this necessity.

TomJGooding commented 2 months ago

Glad to hear that this worked!

I have no idea how pyinstaller resolves imports, but my quick scan of the docs suggests this problem isn't unique to Textual.

There may be a good reason for the "indirection" in _tab_pane, but I'm afraid that needs an answer from the maintainers.