mirror of
https://github.com/slint-ui/slint.git
synced 2025-08-04 02:39:28 +00:00
Python: Replace import magic with an auto-loader
As discussed on Reddit, the magic import logic is not very tool friendly and a little too magic perhaps. Instead, this patch introduces an automatic loader (`slint.loader`), which can traverse `sys.path` and lazily load `.slint` files by attribute lookup. Closes #4856
This commit is contained in:
parent
93307707cd
commit
f86f4993fa
6 changed files with 48 additions and 52 deletions
|
@ -74,9 +74,8 @@ export component AppWindow inherits Window {
|
|||
|
||||
```python
|
||||
import slint
|
||||
import appwindow_slint
|
||||
|
||||
class App(appwindow_slint.AppWindow):
|
||||
class App(slint.loader.appwindow.AppWindow):
|
||||
@slint.callback
|
||||
def request_increase_value(self):
|
||||
self.counter = self.counter + 1
|
||||
|
@ -93,7 +92,7 @@ app.run()
|
|||
|
||||
The following example shows how to instantiate a Slint component in Python:
|
||||
|
||||
**`ui.slint`**
|
||||
**`app.slint`**
|
||||
|
||||
```slint
|
||||
export component MainWindow inherits Window {
|
||||
|
@ -111,21 +110,25 @@ export component MainWindow inherits Window {
|
|||
The exported component is exposed as a Python class. To access this class, you have two
|
||||
options:
|
||||
|
||||
1. Call `slint.load_file("ui.slint")`. The returned object is a [namespace](https://docs.python.org/3/library/types.html#types.SimpleNamespace),
|
||||
1. Call `slint.load_file("app.slint")`. The returned object is a [namespace](https://docs.python.org/3/library/types.html#types.SimpleNamespace),
|
||||
that provides the `MainWindow` class:
|
||||
```python
|
||||
import slint
|
||||
components = slint.load_file("ui.slint")
|
||||
components = slint.load_file("app.slint")
|
||||
main_window = components.MainWindow()
|
||||
```
|
||||
|
||||
2. Import the `.slint` file as module by treating it like a Python module where the `.slint` extension is replaced with `_slint`:
|
||||
2. Use Slint's auto-loader, which lazily loads `.slint` files from `sys.path`:
|
||||
```python
|
||||
import slint # needs to come first
|
||||
from ui_slint import MainWindow
|
||||
main_window = MainWindow()
|
||||
import slint
|
||||
# Look for for `app.slint` in `sys.path`:
|
||||
main_window = slint.loader.app.MainWindow()
|
||||
```
|
||||
|
||||
Any attribute lookup in `slint.loader` is searched for in `sys.path`. If a directory with the name exists, it is returned as a loader object, and subsequent
|
||||
attribute lookups follow the same logic. If the name matches a file with the `.slint` extension, it is automatically loaded with `load_file` and the
|
||||
[namespace](https://docs.python.org/3/library/types.html#types.SimpleNamespace) is returned.
|
||||
|
||||
### Accessing Properties
|
||||
|
||||
[Properties](../slint/src/language/syntax/properties) declared as `out` or `in-out` in `.slint` files are visible as properties on the component instance.
|
||||
|
|
|
@ -7,7 +7,7 @@ build-backend = "maturin"
|
|||
|
||||
[project]
|
||||
name = "slint"
|
||||
version = "1.6.0a5"
|
||||
version = "1.6.0a6"
|
||||
requires-python = ">= 3.10"
|
||||
authors = [
|
||||
{name = "Slint Team", email = "info@slint.dev"},
|
||||
|
|
|
@ -189,30 +189,31 @@ def load_file(path, quiet=False, style=None, include_paths=None, library_paths=N
|
|||
return module
|
||||
|
||||
|
||||
class SlintModuleLoader:
|
||||
def create_module(self, spec):
|
||||
class SlintAutoLoader:
|
||||
def __init__(self, base_dir=None):
|
||||
if base_dir:
|
||||
self.local_dirs = [base_dir]
|
||||
else:
|
||||
self.local_dirs = None
|
||||
|
||||
def __getattr__(self, name):
|
||||
for path in self.local_dirs or sys.path:
|
||||
dir_candidate = os.path.join(path, name)
|
||||
if os.path.isdir(dir_candidate):
|
||||
loader = SlintAutoLoader(dir_candidate)
|
||||
setattr(self, name, loader)
|
||||
return loader
|
||||
|
||||
file_candidate = dir_candidate + ".slint"
|
||||
if os.path.isfile(file_candidate):
|
||||
type_namespace = load_file(file_candidate)
|
||||
setattr(self, name, type_namespace)
|
||||
return type_namespace
|
||||
|
||||
return None
|
||||
|
||||
def exec_module(self, module):
|
||||
m = load_file(module.__name__)
|
||||
module.__dict__.update(m.__dict__)
|
||||
|
||||
|
||||
class SlintModuleFinder:
|
||||
def find_spec(self, name, path, target=None):
|
||||
if "." in name:
|
||||
return None
|
||||
|
||||
if not name.endswith("_slint"):
|
||||
return None
|
||||
|
||||
candidate_filename = name.removesuffix("_slint") + ".slint"
|
||||
|
||||
for path in sys.path:
|
||||
candidate = os.path.join(path, candidate_filename)
|
||||
if os.path.exists(candidate):
|
||||
return ModuleSpec(os.path.realpath(candidate), SlintModuleLoader())
|
||||
return None
|
||||
loader = SlintAutoLoader()
|
||||
|
||||
|
||||
def _callback_decorator(callable, info):
|
||||
|
@ -235,8 +236,6 @@ def callback(global_name=None, name=None):
|
|||
return lambda callback: _callback_decorator(callback, info)
|
||||
|
||||
|
||||
sys.meta_path.append(SlintModuleFinder())
|
||||
|
||||
Image = native.PyImage
|
||||
Color = native.PyColor
|
||||
Brush = native.PyBrush
|
||||
|
|
|
@ -3,25 +3,23 @@
|
|||
|
||||
import pytest
|
||||
from slint import slint as native
|
||||
from slint import loader
|
||||
import sys
|
||||
import os
|
||||
|
||||
|
||||
def test_magic_import():
|
||||
import test_load_file_slint as compiledmodule
|
||||
instance = compiledmodule.App()
|
||||
instance = loader.test_load_file.App()
|
||||
del instance
|
||||
|
||||
|
||||
def test_magic_import_path():
|
||||
oldsyspath = sys.path
|
||||
with pytest.raises(ModuleNotFoundError, match="No module named 'printerdemo_slint'"):
|
||||
import printerdemo_slint
|
||||
assert loader.printerdemo == None
|
||||
try:
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__),
|
||||
"..", "..", "..", "examples", "printerdemo", "ui"))
|
||||
import printerdemo_slint
|
||||
instance = printerdemo_slint.MainWindow()
|
||||
"..", "..", ".."))
|
||||
instance = loader.examples.printerdemo.ui.printerdemo.MainWindow()
|
||||
del instance
|
||||
finally:
|
||||
sys.path = oldsyspath
|
Loading…
Add table
Add a link
Reference in a new issue