diff --git a/android/src/toga_android/factory.py b/android/src/toga_android/factory.py index 8b14f6ba3f..fae84635a8 100644 --- a/android/src/toga_android/factory.py +++ b/android/src/toga_android/factory.py @@ -1,86 +1,62 @@ -from toga import NotImplementedWarning +import importlib -from . import dialogs -from .app import App, MainWindow -from .command import Command -from .fonts import Font -from .hardware.camera import Camera -from .hardware.location import Location -from .icons import Icon -from .images import Image -from .paths import Paths -from .widgets.box import Box -from .widgets.button import Button -from .widgets.canvas import Canvas -from .widgets.dateinput import DateInput -from .widgets.detailedlist import DetailedList -from .widgets.divider import Divider -from .widgets.imageview import ImageView -from .widgets.label import Label -from .widgets.mapview import MapView -from .widgets.multilinetextinput import MultilineTextInput -from .widgets.numberinput import NumberInput -from .widgets.optioncontainer import OptionContainer -from .widgets.passwordinput import PasswordInput -from .widgets.progressbar import ProgressBar -from .widgets.scrollcontainer import ScrollContainer -from .widgets.selection import Selection -from .widgets.slider import Slider -from .widgets.switch import Switch -from .widgets.table import Table -from .widgets.textinput import TextInput -from .widgets.timeinput import TimeInput -from .widgets.webview import WebView -from .window import Window +from toga import NotImplementedWarning +toga_android_factory_imports = { + "App": "toga_android.app", + "MainWindow": "toga_android.app", + "Command": "toga_android.command", + "Camera": "toga_android.hardware.camera", + "Font": "toga_android.fonts", + "Icon": "toga_android.icons", + "Image": "toga_android.images", + "Paths": "toga_android.paths", + "Box": "toga_android.widgets.box", + "Button": "toga_android.widgets.button", + "Canvas": "toga_android.widgets.canvas", + "DateInput": "toga_android.widgets.dateinput", + "DetailedList": "toga_android.widgets.detailedlist", + "Divider": "toga_android.widgets.divider", + "dialogs": "toga_android", + "ImageView": "toga_android.widgets.imageview", + "Label": "toga_android.widgets.label", + "Location": "toga_android.hardware.location", + "MapView": "toga_android.widgets.mapview", + "MultilineTextInput": "toga_android.widgets.multilinetextinput", + "NumberInput": "toga_android.widgets.numberinput", + "OptionContainer": "toga_android.widgets.optioncontainer", + "PasswordInput": "toga_android.widgets.passwordinput", + "ProgressBar": "toga_android.widgets.progressbar", + "ScrollContainer": "toga_android.widgets.scrollcontainer", + "Selection": "toga_android.widgets.selection", + "Slider": "toga_android.widgets.slider", + "Switch": "toga_android.widgets.switch", + "Table": "toga_android.widgets.table", + "TextInput": "toga_android.widgets.textinput", + "TimeInput": "toga_android.widgets.timeinput", + "WebView": "toga_android.widgets.webview", + "Window": "toga_android.window", +} +__all__ = list(toga_android_factory_imports.keys()) + ["not_implemented"] -def not_implemented(feature): - NotImplementedWarning.warn("Android", feature) +def __getattr__(name): + try: + module_name = toga_android_factory_imports[name] -__all__ = [ - "App", - "Command", - "MainWindow", - "not_implemented", - # Resources - "dialogs", - "Font", - "Icon", - "Image", - "Paths", - # Hardware - "Camera", - "Location", - # Widgets - # ActivityIndicator - "Box", - "Button", - "Canvas", - "DateInput", - "DetailedList", - "Divider", - "ImageView", - "Label", - "MapView", - "MultilineTextInput", - "NumberInput", - "OptionContainer", - "PasswordInput", - "ProgressBar", - "ScrollContainer", - # "SplitContainer", - "Selection", - "Slider", - "Switch", - "Table", - "TextInput", - "TimeInput", - # "Tree", - "WebView", - "Window", -] + has_dot = module_name.find(".") != -1 + if not has_dot: + module_name = f"toga_android.{name}" + except KeyError: + raise NotImplementedError( + f"Toga's Android backend doesn't implement '{name}'" + ) from None + else: + module = importlib.import_module(module_name) + value = getattr(module, name) if has_dot else module + globals()[name] = value + return value -def __getattr__(name): # pragma: no cover - raise NotImplementedError(f"Toga's Android backend doesn't implement {name}") +def not_implemented(feature): + NotImplementedWarning.warn("Android", feature) diff --git a/android/src/toga_android/widgets/slider.py b/android/src/toga_android/widgets/slider.py index 12a263b152..d0c5988931 100644 --- a/android/src/toga_android/widgets/slider.py +++ b/android/src/toga_android/widgets/slider.py @@ -5,7 +5,7 @@ from android.widget import SeekBar from java import dynamic_proxy -import toga +from toga.widgets.slider import IntSliderImpl from .base import Widget @@ -31,7 +31,7 @@ def onStopTrackingTouch(self, native_seekbar): self.impl.interface.on_release() -class Slider(Widget, toga.widgets.slider.IntSliderImpl): +class Slider(Widget, IntSliderImpl): focusable = False TICK_DRAWABLE = None diff --git a/changes/2547.feature.rst b/changes/2547.feature.rst new file mode 100644 index 0000000000..b3b2b33cea --- /dev/null +++ b/changes/2547.feature.rst @@ -0,0 +1 @@ +Imports from the ``toga`` namespace and the backend factories have been modified to use lazy importing. diff --git a/cocoa/src/toga_cocoa/documents.py b/cocoa/src/toga_cocoa/documents.py index efa7b91092..62a57e2129 100644 --- a/cocoa/src/toga_cocoa/documents.py +++ b/cocoa/src/toga_cocoa/documents.py @@ -1,3 +1,4 @@ +# pragma: no cover import os from urllib.parse import quote diff --git a/cocoa/src/toga_cocoa/factory.py b/cocoa/src/toga_cocoa/factory.py index 981a384dc7..de2394bd53 100644 --- a/cocoa/src/toga_cocoa/factory.py +++ b/cocoa/src/toga_cocoa/factory.py @@ -1,90 +1,67 @@ -from toga import NotImplementedWarning +import importlib -from . import dialogs -from .app import App, DocumentApp, MainWindow -from .command import Command -from .documents import Document -from .fonts import Font -from .hardware.camera import Camera -from .hardware.location import Location -from .icons import Icon -from .images import Image -from .paths import Paths +from toga import NotImplementedWarning -# Widgets -from .widgets.activityindicator import ActivityIndicator -from .widgets.box import Box -from .widgets.button import Button -from .widgets.canvas import Canvas -from .widgets.detailedlist import DetailedList -from .widgets.divider import Divider -from .widgets.imageview import ImageView -from .widgets.label import Label -from .widgets.mapview import MapView -from .widgets.multilinetextinput import MultilineTextInput -from .widgets.numberinput import NumberInput -from .widgets.optioncontainer import OptionContainer -from .widgets.passwordinput import PasswordInput -from .widgets.progressbar import ProgressBar -from .widgets.scrollcontainer import ScrollContainer -from .widgets.selection import Selection -from .widgets.slider import Slider -from .widgets.splitcontainer import SplitContainer -from .widgets.switch import Switch -from .widgets.table import Table -from .widgets.textinput import TextInput -from .widgets.tree import Tree -from .widgets.webview import WebView -from .window import Window +toga_cocoa_factory_imports = { + "App": "toga_cocoa.app", + "DocumentApp": "toga_cocoa.app", + "DocumentMainWindow": "toga_cocoa.app", + "Command": "toga_cocoa.command", + "Document": "toga_cocoa.documents", + "dialogs": "toga_cocoa", + "Font": "toga_cocoa.fonts", + "Camera": "toga_cocoa.hardware.camera", + "Location": "toga_cocoa.hardware.location", + "Icon": "toga_cocoa.icons", + "Image": "toga_cocoa.images", + "Paths": "toga_cocoa.paths", + "ActivityIndicator": "toga_cocoa.widgets.activityindicator", + "Box": "toga_cocoa.widgets.box", + "Button": "toga_cocoa.widgets.button", + "Canvas": "toga_cocoa.widgets.canvas", + "DetailedList": "toga_cocoa.widgets.detailedlist", + "Divider": "toga_cocoa.widgets.divider", + "ImageView": "toga_cocoa.widgets.imageview", + "Label": "toga_cocoa.widgets.label", + "MapView": "toga_cocoa.widgets.mapview", + "MainWindow": "toga_cocoa.app", + "MultilineTextInput": "toga_cocoa.widgets.multilinetextinput", + "NumberInput": "toga_cocoa.widgets.numberinput", + "OptionContainer": "toga_cocoa.widgets.optioncontainer", + "PasswordInput": "toga_cocoa.widgets.passwordinput", + "ProgressBar": "toga_cocoa.widgets.progressbar", + "ScrollContainer": "toga_cocoa.widgets.scrollcontainer", + "Selection": "toga_cocoa.widgets.selection", + "Slider": "toga_cocoa.widgets.slider", + "SplitContainer": "toga_cocoa.widgets.splitcontainer", + "Switch": "toga_cocoa.widgets.switch", + "Table": "toga_cocoa.widgets.table", + "TextInput": "toga_cocoa.widgets.textinput", + "Tree": "toga_cocoa.widgets.tree", + "WebView": "toga_cocoa.widgets.webview", + "Window": "toga_cocoa.window", +} +__all__ = list(toga_cocoa_factory_imports.keys()) + ["not_implemented"] -def not_implemented(feature): - NotImplementedWarning.warn("Cocoa", feature) +def __getattr__(name): + try: + module_name = toga_cocoa_factory_imports[name] -__all__ = [ - "not_implemented", - "App", - "DocumentApp", - "MainWindow", - "Command", - "Document", - # Resources - "Font", - "Icon", - "Image", - "Paths", - "dialogs", - # Hardware - "Camera", - "Location", - # Widgets - "ActivityIndicator", - "Box", - "Button", - "Canvas", - "DetailedList", - "Divider", - "ImageView", - "Label", - "MapView", - "MultilineTextInput", - "NumberInput", - "OptionContainer", - "PasswordInput", - "ProgressBar", - "ScrollContainer", - "Selection", - "Slider", - "SplitContainer", - "Switch", - "Table", - "TextInput", - "Tree", - "WebView", - "Window", -] + has_dot = module_name.find(".") != -1 + if not has_dot: + module_name = f"toga_cocoa.{name}" + except KeyError: + raise NotImplementedError( + f"Toga's Cocoa backend doesn't implement '{name}'" + ) from None + else: + module = importlib.import_module(module_name) + value = getattr(module, name) if has_dot else module + globals()[name] = value + return value -def __getattr__(name): # pragma: no cover - raise NotImplementedError(f"Toga's Cocoa backend doesn't implement {name}") +def not_implemented(feature): + NotImplementedWarning.warn("Cocoa", feature) diff --git a/core/src/toga/__init__.py b/core/src/toga/__init__.py index a6fc276daf..5aeed8e6ca 100644 --- a/core/src/toga/__init__.py +++ b/core/src/toga/__init__.py @@ -1,47 +1,72 @@ +from __future__ import annotations + +import importlib +import importlib.util import warnings -from .app import App, DocumentApp, DocumentMainWindow, MainWindow +toga_core_imports = { + "App": "toga.app", + "DocumentApp": "toga.app", + "DocumentMainWindow": "toga.app", + "MainWindow": "toga.app", + "hsl": "toga.colors", + "hsla": "toga.colors", + "rgb": "toga.colors", + "rgba": "toga.colors", + "Command": "toga.command", + "Group": "toga.command", + "Document": "toga.documents", + "Font": "toga.fonts", + "Icon": "toga.icons", + "Image": "toga.images", + "Key": "toga.keys", + "LatLng": "toga.types", + "ActivityIndicator": "toga.widgets.activityindicator", + "Widget": "toga.widgets.base", + "Box": "toga.widgets.box", + "Button": "toga.widgets.button", + "Canvas": "toga.widgets.canvas", + "DateInput": "toga.widgets.dateinput", + "DatePicker": "toga.widgets.dateinput", + "DetailedList": "toga.widgets.detailedlist", + "Divider": "toga.widgets.divider", + "ImageView": "toga.widgets.imageview", + "Label": "toga.widgets.label", + "MapPin": "toga.widgets.mapview", + "MapView": "toga.widgets.mapview", + "MultilineTextInput": "toga.widgets.multilinetextinput", + "NumberInput": "toga.widgets.numberinput", + "OptionContainer": "toga.widgets.optioncontainer", + "OptionItem": "toga.widgets.optioncontainer", + "PasswordInput": "toga.widgets.passwordinput", + "ProgressBar": "toga.widgets.progressbar", + "ScrollContainer": "toga.widgets.scrollcontainer", + "Selection": "toga.widgets.selection", + "Slider": "toga.widgets.slider", + "SplitContainer": "toga.widgets.splitcontainer", + "Switch": "toga.widgets.switch", + "Table": "toga.widgets.table", + "TextInput": "toga.widgets.textinput", + "TimeInput": "toga.widgets.timeinput", + "TimePicker": "toga.widgets.timeinput", + "Tree": "toga.widgets.tree", + "WebView": "toga.widgets.webview", + "Window": "toga.window", +} -# Resources -from .colors import hsl, hsla, rgb, rgba -from .command import Command, Group -from .documents import Document -from .fonts import Font -from .icons import Icon -from .images import Image -from .keys import Key +__all__ = list(toga_core_imports.keys()) -# Types -from .types import LatLng -# Widgets -from .widgets.activityindicator import ActivityIndicator -from .widgets.base import Widget -from .widgets.box import Box -from .widgets.button import Button -from .widgets.canvas import Canvas -from .widgets.dateinput import DateInput, DatePicker -from .widgets.detailedlist import DetailedList -from .widgets.divider import Divider -from .widgets.imageview import ImageView -from .widgets.label import Label -from .widgets.mapview import MapPin, MapView -from .widgets.multilinetextinput import MultilineTextInput -from .widgets.numberinput import NumberInput -from .widgets.optioncontainer import OptionContainer, OptionItem -from .widgets.passwordinput import PasswordInput -from .widgets.progressbar import ProgressBar -from .widgets.scrollcontainer import ScrollContainer -from .widgets.selection import Selection -from .widgets.slider import Slider -from .widgets.splitcontainer import SplitContainer -from .widgets.switch import Switch -from .widgets.table import Table -from .widgets.textinput import TextInput -from .widgets.timeinput import TimeInput, TimePicker -from .widgets.tree import Tree -from .widgets.webview import WebView -from .window import Window +def __getattr__(name): + try: + module_name = toga_core_imports[name] + except KeyError: + raise AttributeError(f"module '{__name__}' has no attribute '{name}'") from None + else: + module = importlib.import_module(module_name) + value = getattr(module, name) + globals()[name] = value + return value class NotImplementedWarning(RuntimeWarning): @@ -54,66 +79,6 @@ def warn(self, platform, feature): warnings.warn(NotImplementedWarning(f"[{platform}] Not implemented: {feature}")) -__all__ = [ - "NotImplementedWarning", - # Applications - "App", - "DocumentApp", - "MainWindow", - "DocumentMainWindow", - # Commands - "Command", - "Group", - # Documents - "Document", - # Keys - "Key", - # Resources - "hsl", - "hsla", - "rgb", - "rgba", - "Font", - "Icon", - "Image", - # Types - "LatLng", - # Widgets - "ActivityIndicator", - "Box", - "Button", - "Canvas", - "DateInput", - "DetailedList", - "Divider", - "ImageView", - "Label", - "MapPin", - "MapView", - "MultilineTextInput", - "NumberInput", - "OptionContainer", - "OptionItem", - "PasswordInput", - "ProgressBar", - "ScrollContainer", - "Selection", - "Slider", - "SplitContainer", - "Switch", - "Table", - "TextInput", - "TimeInput", - "Tree", - "WebView", - "Widget", - "Window", - # Deprecated widget names - "DatePicker", - "TimePicker", -] - - def _package_version(file, name): try: # Read version from SCM metadata diff --git a/dummy/src/toga_dummy/widgets/slider.py b/dummy/src/toga_dummy/widgets/slider.py index 31c2a4137a..e3ea59a3e0 100644 --- a/dummy/src/toga_dummy/widgets/slider.py +++ b/dummy/src/toga_dummy/widgets/slider.py @@ -1,9 +1,9 @@ -import toga +from toga.widgets.slider import SliderImpl from .base import Widget -class Slider(Widget, toga.widgets.slider.SliderImpl): +class Slider(Widget, SliderImpl): def create(self): self._action("create Slider") diff --git a/gtk/src/toga_gtk/factory.py b/gtk/src/toga_gtk/factory.py index a0d48f0af1..efaf7b4855 100644 --- a/gtk/src/toga_gtk/factory.py +++ b/gtk/src/toga_gtk/factory.py @@ -1,83 +1,67 @@ +import importlib + from toga import NotImplementedWarning -from . import dialogs -from .app import App, DocumentApp, MainWindow -from .command import Command -from .documents import Document -from .fonts import Font -from .icons import Icon -from .images import Image -from .paths import Paths -from .widgets.activityindicator import ActivityIndicator -from .widgets.box import Box -from .widgets.button import Button -from .widgets.canvas import Canvas -from .widgets.detailedlist import DetailedList -from .widgets.divider import Divider -from .widgets.imageview import ImageView -from .widgets.label import Label -from .widgets.mapview import MapView -from .widgets.multilinetextinput import MultilineTextInput -from .widgets.numberinput import NumberInput -from .widgets.optioncontainer import OptionContainer -from .widgets.passwordinput import PasswordInput -from .widgets.progressbar import ProgressBar -from .widgets.scrollcontainer import ScrollContainer -from .widgets.selection import Selection -from .widgets.slider import Slider -from .widgets.splitcontainer import SplitContainer -from .widgets.switch import Switch -from .widgets.table import Table -from .widgets.textinput import TextInput -from .widgets.tree import Tree -from .widgets.webview import WebView -from .window import Window +toga_gtk_factory_imports = { + "App": "toga_gtk.app", + "DocumentApp": "toga_gtk.app", + "DocumentMainWindow": "toga_gtk.app", + "Command": "toga_gtk.command", + "Document": "toga_gtk.documents", + "dialogs": "toga_gtk", + "Font": "toga_gtk.fonts", + "Camera": "toga_gtk.hardware.camera", + "Location": "toga_gtk.hardware.location", + "Icon": "toga_gtk.icons", + "Image": "toga_gtk.images", + "Paths": "toga_gtk.paths", + "ActivityIndicator": "toga_gtk.widgets.activityindicator", + "Box": "toga_gtk.widgets.box", + "Button": "toga_gtk.widgets.button", + "Canvas": "toga_gtk.widgets.canvas", + "DetailedList": "toga_gtk.widgets.detailedlist", + "Divider": "toga_gtk.widgets.divider", + "ImageView": "toga_gtk.widgets.imageview", + "Label": "toga_gtk.widgets.label", + "MapView": "toga_gtk.widgets.mapview", + "MainWindow": "toga_gtk.app", + "MultilineTextInput": "toga_gtk.widgets.multilinetextinput", + "NumberInput": "toga_gtk.widgets.numberinput", + "OptionContainer": "toga_gtk.widgets.optioncontainer", + "PasswordInput": "toga_gtk.widgets.passwordinput", + "ProgressBar": "toga_gtk.widgets.progressbar", + "ScrollContainer": "toga_gtk.widgets.scrollcontainer", + "Selection": "toga_gtk.widgets.selection", + "Slider": "toga_gtk.widgets.slider", + "SplitContainer": "toga_gtk.widgets.splitcontainer", + "Switch": "toga_gtk.widgets.switch", + "Table": "toga_gtk.widgets.table", + "TextInput": "toga_gtk.widgets.textinput", + "Tree": "toga_gtk.widgets.tree", + "WebView": "toga_gtk.widgets.webview", + "Window": "toga_gtk.window", +} +__all__ = list(toga_gtk_factory_imports.keys()) + ["not_implemented"] -def not_implemented(feature): - NotImplementedWarning.warn("GTK", feature) +def __getattr__(name): + try: + module_name = toga_gtk_factory_imports[name] -__all__ = [ - "not_implemented", - "App", - "DocumentApp", - "MainWindow", - "Command", - "Document", - # Resources - "Font", - "Icon", - "Image", - "Paths", - "dialogs", - # Widgets - "ActivityIndicator", - "Box", - "Button", - "Canvas", - "DetailedList", - "Divider", - "ImageView", - "Label", - "MapView", - "MultilineTextInput", - "NumberInput", - "OptionContainer", - "PasswordInput", - "ProgressBar", - "ScrollContainer", - "Selection", - "Slider", - "SplitContainer", - "Switch", - "Table", - "TextInput", - "Tree", - "WebView", - "Window", -] + has_dot = module_name.find(".") != -1 + if not has_dot: + module_name = f"toga_gtk.{name}" + except KeyError: + raise NotImplementedError( + f"Toga's GTK backend doesn't implement '{name}'" + ) from None + else: + module = importlib.import_module(module_name) + value = getattr(module, name) if has_dot else module + globals()[name] = value + return value -def __getattr__(name): # pragma: no cover - raise NotImplementedError(f"Toga's GTK backend doesn't implement {name}") +def not_implemented(feature): + NotImplementedWarning.warn("GTK", feature) diff --git a/gtk/src/toga_gtk/widgets/slider.py b/gtk/src/toga_gtk/widgets/slider.py index 301cbfa356..f5d4766372 100644 --- a/gtk/src/toga_gtk/widgets/slider.py +++ b/gtk/src/toga_gtk/widgets/slider.py @@ -1,6 +1,6 @@ from travertino.size import at_least -import toga +from toga.widgets.slider import SliderImpl from ..libs import Gtk from .base import Widget @@ -16,7 +16,7 @@ # to line up at the same values. -class Slider(Widget, toga.widgets.slider.SliderImpl): +class Slider(Widget, SliderImpl): def create(self): self.adj = Gtk.Adjustment() self.native = Gtk.Scale.new(Gtk.Orientation.HORIZONTAL, self.adj) diff --git a/iOS/src/toga_iOS/factory.py b/iOS/src/toga_iOS/factory.py index 2c7088d206..98dcde3f70 100644 --- a/iOS/src/toga_iOS/factory.py +++ b/iOS/src/toga_iOS/factory.py @@ -1,88 +1,61 @@ -from toga import NotImplementedWarning - -from . import dialogs -from .app import App, MainWindow -from .colors import native_color -from .command import Command -from .fonts import Font -from .hardware.camera import Camera -from .hardware.location import Location -from .icons import Icon -from .images import Image -from .paths import Paths - -# Widgets -from .widgets.box import Box -from .widgets.button import Button -from .widgets.canvas import Canvas -from .widgets.detailedlist import DetailedList -from .widgets.imageview import ImageView -from .widgets.label import Label -from .widgets.mapview import MapView -from .widgets.multilinetextinput import MultilineTextInput -from .widgets.numberinput import NumberInput -from .widgets.optioncontainer import OptionContainer -from .widgets.passwordinput import PasswordInput -from .widgets.progressbar import ProgressBar -from .widgets.scrollcontainer import ScrollContainer -from .widgets.selection import Selection -from .widgets.slider import Slider +import importlib -# from .widgets.splitcontainer import SplitContainer -from .widgets.switch import Switch - -# from .widgets.table import Table -from .widgets.textinput import TextInput +from toga import NotImplementedWarning -# from .widgets.tree import Tree -from .widgets.webview import WebView -from .window import Window +toga_ios_factory_imports = { + "App": "toga_iOS.app", + "MainWindow": "toga_iOS.app", + "Command": "toga_iOS.command", + "Camera": "toga_iOS.hardware.camera", + "native_color": "toga_iOS.colors", + "Font": "toga_iOS.fonts", + "Icon": "toga_iOS.icons", + "Image": "toga_iOS.images", + "Paths": "toga_iOS.paths", + "dialogs": "toga_iOS", + "Box": "toga_iOS.widgets.box", + "Button": "toga_iOS.widgets.button", + "Canvas": "toga_iOS.widgets.canvas", + "DetailedList": "toga_iOS.widgets.detailedlist", + "ImageView": "toga_iOS.widgets.imageview", + "Label": "toga_iOS.widgets.label", + "Location": "toga_iOS.hardware.location", + "MapView": "toga_iOS.widgets.mapview", + "MultilineTextInput": "toga_iOS.widgets.multilinetextinput", + "NumberInput": "toga_iOS.widgets.numberinput", + "OptionContainer": "toga_iOS.widgets.optioncontainer", + "PasswordInput": "toga_iOS.widgets.passwordinput", + "ProgressBar": "toga_iOS.widgets.progressbar", + "ScrollContainer": "toga_iOS.widgets.scrollcontainer", + "Selection": "toga_iOS.widgets.selection", + "Slider": "toga_iOS.widgets.slider", + "Switch": "toga_iOS.widgets.switch", + "TextInput": "toga_iOS.widgets.textinput", + "WebView": "toga_iOS.widgets.webview", + "Window": "toga_iOS.window", +} + + +__all__ = list(toga_ios_factory_imports.keys()) + ["not_implemented"] + + +def __getattr__(name): + try: + module_name = toga_ios_factory_imports[name] + + has_dot = module_name.find(".") != -1 + if not has_dot: + module_name = f"toga_iOS.{name}" + except KeyError: + raise NotImplementedError( + f"Toga's iOS backend doesn't implement '{name}'" + ) from None + else: + module = importlib.import_module(module_name) + value = getattr(module, name) if has_dot else module + globals()[name] = value + return value def not_implemented(feature): NotImplementedWarning.warn("iOS", feature) - - -__all__ = [ - "not_implemented", - "App", - "MainWindow", - "Command", - # Resources - "native_color", # colors - "Font", - "Icon", - "Image", - "Paths", - "dialogs", - # Hardware - "Camera", - "Location", - # Widgets - "Box", - "Button", - "Canvas", - "DetailedList", - "ImageView", - "Label", - "MapView", - "MultilineTextInput", - "NumberInput", - "OptionContainer", - "PasswordInput", - "ProgressBar", - "ScrollContainer", - "Selection", - "Slider", - # 'SplitContainer', - "Switch", - # 'Table', - "TextInput", - # 'Tree', - "WebView", - "Window", -] - - -def __getattr__(name): # pragma: no cover - raise NotImplementedError(f"Toga's iOS backend doesn't implement {name}") diff --git a/testbed/tests/test_factory.py b/testbed/tests/test_factory.py new file mode 100644 index 0000000000..faeba66074 --- /dev/null +++ b/testbed/tests/test_factory.py @@ -0,0 +1,8 @@ +import pytest + +import toga + + +def test_missing_widget(): + with pytest.raises(NotImplementedError): + toga.platform.get_platform_factory().MissingWidget diff --git a/textual/src/toga_textual/factory.py b/textual/src/toga_textual/factory.py index 06217c2350..4d0318c26b 100644 --- a/textual/src/toga_textual/factory.py +++ b/textual/src/toga_textual/factory.py @@ -1,91 +1,67 @@ -from toga import NotImplementedWarning +import importlib -from . import dialogs -from .app import App, DocumentApp, MainWindow +from toga import NotImplementedWarning -# from .command import Command -# from .documents import Document -# from .fonts import Font -from .icons import Icon +toga_textual_factory_imports = { + "App": "toga_textual.app", + "DocumentApp": "toga_textual.app", + "MainWindow": "toga_textual.app", + "dialogs": "toga_textual", + # "Command": "toga_textual.command", + # "Document": "toga_textual.documents", + # "Font": "toga_textual.fonts", + "Icon": "toga_textual.icons", + # "Image": "toga_textual.images", + "Paths": "toga_textual.paths", + # "ActivityIndicator": "toga_textual.widgets.activityindicator", + # "widget": "toga_textual.widgets.base", + "Box": "toga_textual.widgets.box", + "Button": "toga_textual.widgets.button", + # "Canvas": "toga_textual.widgets.canvas", + # "DateInput": "toga_textual.widgets.dateinput", + # "DetailedList": "toga_textual.widgets.detailedlist", + # "Divider": "toga_textual.widgets.divider", + # "ImageView": "toga_textual.widgets.imageview", + "Label": "toga_textual.widgets.label", + # "MultilineTextInput": "toga_textual.widgets.multilinetextinput", + # "NumberInput": "toga_textual.widgets.numberinput", + # "OptionContainer": "toga_textual.widgets.optioncontainer", + # "PasswordInput": "toga_textual.widgets.passwordinput", + # "ProgressBar": "toga_textual.widgets.progressbar", + # "ScrollContainer": "toga_textual.widgets.scrollcontainer", + # "Selection": "toga_textual.widgets.selection", + # "Slider": "toga_textual.widgets.slider", + # "SplitContainer": "toga_textual.widgets.splitcontainer", + # "Switch": "toga_textual.widgets.switch", + # "Table": "toga_textual.widgets.table", + "TextInput": "toga_textual.widgets.textinput", + # "TimeInput": "toga_textual.widgets.timeinput", + # "Tree": "toga_textual.widgets.tree", + # "WebView": "toga_textual.widgets.webview", + "Window": "toga_textual.window", +} -# from .images import Image -from .paths import Paths -# from .widgets.activityindicator import ActivityIndicator -# from .widgets.base import Widget -from .widgets.box import Box -from .widgets.button import Button +__all__ = list(toga_textual_factory_imports.keys()) + ["not_implemented"] -# from .widgets.canvas import Canvas -# from .widgets.dateinput import DateInput -# from .widgets.detailedlist import DetailedList -# from .widgets.divider import Divider -# from .widgets.imageview import ImageView -from .widgets.label import Label -# from .widgets.multilinetextinput import MultilineTextInput -# from .widgets.numberinput import NumberInput -# from .widgets.optioncontainer import OptionContainer -# from .widgets.passwordinput import PasswordInput -# from .widgets.progressbar import ProgressBar -# from .widgets.scrollcontainer import ScrollContainer -# from .widgets.selection import Selection -# from .widgets.slider import Slider -# from .widgets.splitcontainer import SplitContainer -# from .widgets.switch import Switch -# from .widgets.table import Table -from .widgets.textinput import TextInput +def __getattr__(name): + try: + module_name = toga_textual_factory_imports[name] -# from .widgets.timeinput import TimeInput -# from .widgets.tree import Tree -# from .widgets.webview import WebView -from .window import Window + has_dot = module_name.find(".") != -1 + if not has_dot: + module_name = f"toga_textual.{name}" + except KeyError: + raise NotImplementedError( + f"Toga's Textual backend doesn't implement '{name}'" + ) from None + else: + module = importlib.import_module(module_name) + value = getattr(module, name) if has_dot else module + globals()[name] = value + return value def not_implemented(feature): NotImplementedWarning.warn("Textual", feature) # pragma: nocover - - -__all__ = [ - "not_implemented", - "App", - "DocumentApp", - "MainWindow", - # "Command", - # "Document", - # "Font", - "Icon", - # "Image", - "Paths", - "dialogs", - # # Widgets - # "ActivityIndicator", - "Box", - "Button", - # "Canvas", - # "DateInput", - # "DetailedList", - # "Divider", - # "ImageView", - "Label", - # "MultilineTextInput", - # "NumberInput", - # "OptionContainer", - # "PasswordInput", - # "ProgressBar", - # "ScrollContainer", - # "Selection", - # "Slider", - # "SplitContainer", - # "Switch", - # "Table", - "TextInput", - # "TimeInput", - # "Tree", - # "WebView", - "Window", -] - - -def __getattr__(name): # pragma: no cover - raise NotImplementedError(f"Toga's Textual backend doesn't implement {name}") diff --git a/web/src/toga_web/factory.py b/web/src/toga_web/factory.py index bef184f1d9..9f9d8c2bd3 100644 --- a/web/src/toga_web/factory.py +++ b/web/src/toga_web/factory.py @@ -1,88 +1,63 @@ -from toga import NotImplementedWarning - -from . import dialogs -from .app import App, MainWindow # DocumentApp -from .command import Command - -# from .documents import Document -# from .fonts import Font -from .icons import Icon - -# from .images import Image -from .paths import Paths -from .widgets.activityindicator import ActivityIndicator -from .widgets.box import Box -from .widgets.button import Button -from .widgets.divider import Divider - -# from .widgets.canvas import Canvas -# from .widgets.detailedlist import DetailedList -# from .widgets.imageview import ImageView -from .widgets.label import Label - -# from .widgets.multilinetextinput import MultilineTextInput -# from .widgets.numberinput import NumberInput -# from .widgets.optioncontainer import OptionContainer -from .widgets.passwordinput import PasswordInput -from .widgets.progressbar import ProgressBar +import importlib -# from .widgets.scrollcontainer import ScrollContainer -# from .widgets.selection import Selection -# from .widgets.slider import Slider -# from .widgets.splitcontainer import SplitContainer -from .widgets.switch import Switch - -# from .widgets.table import Table -from .widgets.textinput import TextInput +from toga import NotImplementedWarning -# from .widgets.tree import Tree -# from .widgets.webview import WebView -# from .window import Window +toga_web_factory_imports = { + "App": "toga_web.app", + "MainWindow": "toga_web.app", + "Command": "toga_web.command", + "dialogs": "toga_web", + # "DocumentApp": "toga_web.app", + # "Document": "toga_web.documents", + # "Font": "toga_web.fonts", + "Icon": "toga_web.icons", + "Paths": "toga_web.paths", + # "Image": "toga_web.images", + "ActivityIndicator": "toga_web.widgets.activityindicator", + "Box": "toga_web.widgets.box", + "Button": "toga_web.widgets.button", + "Divider": "toga_web.widgets.divider", + # "Canvas": "toga_web.widgets.canvas", + # "DetailedList": "toga_web.widgets.detailedlist", + # "ImageView": "toga_web.widgets.imageview", + "Label": "toga_web.widgets.label", + # "MultilineTextInput": "toga_web.widgets.multilinetextinput", + # "NumberInput": "toga_web.widgets.numberinput", + # "OptionContainer": "toga_web.widgets.optioncontainer", + "PasswordInput": "toga_web.widgets.passwordinput", + "ProgressBar": "toga_web.widgets.progressbar", + # "ScrollContainer": "toga_web.widgets.scrollcontainer", + # "Selection": "toga_web.widgets.selection", + # "Slider": "toga_web.widgets.slider", + # "SplitContainer": "toga_web.widgets.splitcontainer", + "Switch": "toga_web.widgets.switch", + # "Table": "toga_web.widgets.table", + "TextInput": "toga_web.widgets.textinput", + # "Tree": "toga_web.widgets.tree", + # "WebView": "toga_web.widgets.webview", + # "Window": "toga_web.window", +} + +__all__ = list(toga_web_factory_imports.keys()) + ["not_implemented"] + + +def __getattr__(name): + try: + module_name = toga_web_factory_imports[name] + + has_dot = module_name.find(".") != -1 + if not has_dot: + module_name = f"toga_iOS.{name}" + except KeyError: + raise NotImplementedError( + f"Toga's Web backend doesn't implement '{name}'" + ) from None + else: + module = importlib.import_module(module_name) + value = getattr(module, name) if has_dot else module + globals()[name] = value + return value def not_implemented(feature): - NotImplementedWarning.warn("Web", feature) # pragma: nocover - - -__all__ = [ - "not_implemented", - "App", - "MainWindow", - # 'DocumentApp', - "Command", - # 'Document', - # # Resources - # 'Font', - "Icon", - # 'Image', - "Paths", - "dialogs", - # # Widgets - "Box", - "Button", - # 'Canvas', - "Divider", - # 'DetailedList', - # 'ImageView', - "Label", - # 'MultilineTextInput', - # 'NumberInput', - # 'OptionContainer', - "PasswordInput", - "ProgressBar", - "ActivityIndicator", - # 'ScrollContainer', - # 'Selection', - # 'Slider', - # 'SplitContainer', - "Switch", - # 'Table', - "TextInput", - # 'Tree', - # 'WebView', - # 'Window', -] - - -def __getattr__(name): # pragma: no cover - raise NotImplementedError(f"Toga's Web backend doesn't implement {name}") + NotImplementedWarning.warn("Web", feature) diff --git a/winforms/src/toga_winforms/factory.py b/winforms/src/toga_winforms/factory.py index 80f097ea10..150bee78a9 100644 --- a/winforms/src/toga_winforms/factory.py +++ b/winforms/src/toga_winforms/factory.py @@ -1,80 +1,63 @@ -from toga import NotImplementedWarning +import importlib -from . import dialogs -from .app import App, MainWindow -from .command import Command -from .fonts import Font -from .icons import Icon -from .images import Image -from .paths import Paths -from .widgets.box import Box -from .widgets.button import Button -from .widgets.canvas import Canvas -from .widgets.dateinput import DateInput -from .widgets.detailedlist import DetailedList -from .widgets.divider import Divider -from .widgets.imageview import ImageView -from .widgets.label import Label -from .widgets.mapview import MapView -from .widgets.multilinetextinput import MultilineTextInput -from .widgets.numberinput import NumberInput -from .widgets.optioncontainer import OptionContainer -from .widgets.passwordinput import PasswordInput -from .widgets.progressbar import ProgressBar -from .widgets.scrollcontainer import ScrollContainer -from .widgets.selection import Selection -from .widgets.slider import Slider -from .widgets.splitcontainer import SplitContainer -from .widgets.switch import Switch -from .widgets.table import Table -from .widgets.textinput import TextInput -from .widgets.timeinput import TimeInput -from .widgets.webview import WebView -from .window import Window +from toga import NotImplementedWarning def not_implemented(feature): - NotImplementedWarning.warn("Winforms", feature) # pragma: nocover + NotImplementedWarning.warn("winforms", feature) + + +winforms_factory_imports = { + "App": "toga_winforms.app", + "MainWindow": "toga_winforms.app", + "Command": "toga_winforms.command", + "dialogs": "toga_winforms", + "Font": "toga_winforms.fonts", + "Icon": "toga_winforms.icons", + "Image": "toga_winforms.images", + "Paths": "toga_winforms.paths", + "Box": "toga_winforms.widgets.box", + "Button": "toga_winforms.widgets.button", + "Canvas": "toga_winforms.widgets.canvas", + "DetailedList": "toga_winforms.widgets.detailedlist", + "Divider": "toga_winforms.widgets.divider", + "DateInput": "toga_winforms.widgets.dateinput", + "ImageView": "toga_winforms.widgets.imageview", + "Label": "toga_winforms.widgets.label", + "MapView": "toga_winforms.widgets.mapview", + "MultilineTextInput": "toga_winforms.widgets.multilinetextinput", + "NumberInput": "toga_winforms.widgets.numberinput", + "OptionContainer": "toga_winforms.widgets.optioncontainer", + "PasswordInput": "toga_winforms.widgets.passwordinput", + "ProgressBar": "toga_winforms.widgets.progressbar", + "ScrollContainer": "toga_winforms.widgets.scrollcontainer", + "Selection": "toga_winforms.widgets.selection", + "Slider": "toga_winforms.widgets.slider", + "SplitContainer": "toga_winforms.widgets.splitcontainer", + "Switch": "toga_winforms.widgets.switch", + "Table": "toga_winforms.widgets.table", + "TextInput": "toga_winforms.widgets.textinput", + "TimeInput": "toga_winforms.widgets.timeinput", + "WebView": "toga_winforms.widgets.webview", + "Window": "toga_winforms.window", +} +__all__ = list(winforms_factory_imports.keys()) + ["not_implemented"] -__all__ = [ - "not_implemented", - "App", - "MainWindow", - "Command", - # Resources - "Font", - "Icon", - "Image", - "Paths", - "dialogs", - # Widgets - "Box", - "Button", - "Canvas", - "DateInput", - "DetailedList", - "Divider", - "ImageView", - "Label", - "MapView", - "MultilineTextInput", - "NumberInput", - "OptionContainer", - "PasswordInput", - "ProgressBar", - "ScrollContainer", - "Selection", - "Slider", - "SplitContainer", - "Switch", - "Table", - "TextInput", - "TimeInput", - "WebView", - "Window", -] +def __getattr__(name): + try: + module_name = winforms_factory_imports[name] -def __getattr__(name): # pragma: no cover - raise NotImplementedError(f"Toga's Winforms backend doesn't implement {name}") + has_dot = module_name.find(".") != -1 + if not has_dot: + module_name = f"toga_winforms.{name}" + except KeyError: + raise NotImplementedError( + f"Toga's Winforms backend doesn't implement '{name}'" + ) from None + else: + module = importlib.import_module(module_name) + value = getattr(module, name) if has_dot else module + globals()[name] = value + return value