ai-station/.venv/lib/python3.12/site-packages/textual/_import_app.py

127 lines
3.5 KiB
Python

from __future__ import annotations
import os
import runpy
import shlex
import sys
from pathlib import Path
from typing import TYPE_CHECKING, cast
if TYPE_CHECKING:
from textual.app import App
class AppFail(Exception):
pass
def shebang_python(candidate: Path) -> bool:
"""Does the given file look like it's run with Python?
Args:
candidate: The candidate file to check.
Returns:
``True`` if it looks to #! python, ``False`` if not.
"""
try:
with candidate.open("rb") as source:
first_line = source.readline()
except IOError:
return False
return first_line.startswith(b"#!") and b"python" in first_line
def import_app(import_name: str) -> App:
"""Import an app from a path or import name.
Args:
import_name: A name to import, such as `foo.bar`, or a path ending with .py.
Raises:
AppFail: If the app could not be found for any reason.
Returns:
A Textual application
"""
import importlib
import inspect
from textual.app import WINDOWS, App
import_name, *argv = shlex.split(import_name, posix=not WINDOWS)
drive, import_name = os.path.splitdrive(import_name)
lib, _colon, name = import_name.partition(":")
if drive:
lib = os.path.join(drive, os.sep, lib)
if lib.endswith(".py") or shebang_python(Path(lib)):
path = os.path.abspath(lib)
sys.path.append(str(Path(path).parent))
try:
global_vars = runpy.run_path(path, {})
except Exception as error:
raise AppFail(str(error))
sys.argv[:] = [path, *argv]
if name:
# User has given a name, use that
try:
app = global_vars[name]
except KeyError:
raise AppFail(f"App {name!r} not found in {lib!r}")
else:
# User has not given a name
if "app" in global_vars:
# App exists, lets use that
try:
app = global_vars["app"]
except KeyError:
raise AppFail(f"App {name!r} not found in {lib!r}")
else:
# Find an App class or instance that is *not* the base class
apps = [
value
for value in global_vars.values()
if (
isinstance(value, App)
or (inspect.isclass(value) and issubclass(value, App))
and value is not App
)
]
if not apps:
raise AppFail(
f'Unable to find app in {lib!r}, try specifying app with "foo.py:app"'
)
if len(apps) > 1:
raise AppFail(
f'Multiple apps found {lib!r}, try specifying app with "foo.py:app"'
)
app = apps[0]
app._BASE_PATH = path
else:
# Assuming the user wants to import the file
sys.path.append("")
try:
module = importlib.import_module(lib)
except ImportError as error:
raise AppFail(str(error))
find_app = name or "app"
try:
app = getattr(module, find_app or "app")
except AttributeError:
raise AppFail(f"Unable to find {find_app!r} in {module!r}")
sys.argv[:] = [import_name, *argv]
if inspect.isclass(app) and issubclass(app, App):
app = app()
return cast(App, app)