pythonx-compose 0.1.0a1__py3-none-any.whl → 0.1.0a3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pythonx/compose/__init__.pyi +1 -1
- pythonx/compose/_reexport.py +145 -191
- pythonx/compose/foundation/__init__.pyi +1 -1
- pythonx/compose/foundation/layout/__init__.pyi +1 -1
- pythonx/compose/foundation/text/input/__init__.pyi +8 -2
- pythonx/compose/layout/__init__.pyi +349 -142
- pythonx/compose/material/__init__.pyi +1 -1
- pythonx/compose/material/icons/__init__.pyi +1 -1
- pythonx/compose/material3/__init__.py +306 -0
- pythonx/compose/material3/__init__.pyi +4159 -431
- pythonx/compose/runtime/__init__.py +175 -23
- pythonx/compose/runtime/__init__.pyi +32 -5
- pythonx/compose/ui/__init__.pyi +90 -30
- pythonx/compose/ui/graphics/__init__.pyi +843 -94
- pythonx/compose/ui/graphics/vector/__init__.pyi +97 -1
- pythonx/compose/ui/modifier.py +44 -28
- pythonx/compose/ui/text/__init__.pyi +317 -30
- pythonx/compose/ui/unit/__init__.pyi +153 -42
- {pythonx_compose-0.1.0a1.dist-info → pythonx_compose-0.1.0a3.dist-info}/METADATA +42 -30
- pythonx_compose-0.1.0a3.dist-info/RECORD +40 -0
- {pythonx_compose-0.1.0a1.dist-info → pythonx_compose-0.1.0a3.dist-info}/WHEEL +1 -1
- pythonx_compose-0.1.0a3.dist-info/licenses/LICENSE +202 -0
- pythonx_compose-0.1.0a1.dist-info/RECORD +0 -40
- pythonx_compose-0.1.0a1.dist-info/licenses/LICENSE +0 -21
- {pythonx_compose-0.1.0a1.dist-info → pythonx_compose-0.1.0a3.dist-info}/top_level.txt +0 -0
pythonx/compose/__init__.pyi
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
# GENERATED by scripts/gen_stubs.py from python-multiplatform's Kotlin-named stubs (artifact
|
|
1
|
+
# GENERATED by scripts/gen_stubs.py from python-multiplatform's Kotlin-named stubs (artifact ks, workflow run 42402779166, commit 9507e6dd, Compose 1.11.1-ext.2). Do not edit.
|
pythonx/compose/_reexport.py
CHANGED
|
@@ -1,33 +1,29 @@
|
|
|
1
1
|
"""The one rule that makes a `pythonx.compose.*` module a Pythonic view of a Kotlin package.
|
|
2
2
|
|
|
3
|
-
The binder (`python-multiplatform`) serves every bound Kotlin package under its **Kotlin** name
|
|
4
|
-
`androidx.compose.foundation.layout`
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
The binder (`python-multiplatform`) serves every bound Kotlin package under its **Kotlin** name
|
|
4
|
+
(`androidx.compose.foundation.layout`), and since its #131 it also serves the Pythonic spellings
|
|
5
|
+
itself: snake_case names and keyword arguments, by this module's rule (`python_name`), with the
|
|
6
|
+
Kotlin spelling still working and `inspect.signature` reporting the snake_case keywords. It never
|
|
7
|
+
renames a namespace: `pythonx` is this package.
|
|
8
8
|
|
|
9
|
-
This module
|
|
9
|
+
This module groups those Kotlin packages into `pythonx.compose.*`. A mapped package's `__init__.py`
|
|
10
|
+
is one line::
|
|
10
11
|
|
|
11
12
|
__getattr__, __dir__ = reexport(__name__)
|
|
12
13
|
|
|
13
14
|
and every name in it then resolves against the Kotlin package `pythonx-map.toml` maps it to:
|
|
14
15
|
|
|
15
|
-
- **Names.** A name that starts upper-case
|
|
16
|
-
spelling (`Modifier`, `Text`). Any other name is reached by its snake_case spelling
|
|
16
|
+
- **Names.** A name that starts upper-case (a type, an object, a `@Composable`) keeps its Kotlin
|
|
17
|
+
spelling (`Modifier`, `Text`). Any other name is listed and reached by its snake_case spelling
|
|
17
18
|
(`fillMaxWidth` -> `fill_max_width`, `toURLString` -> `to_url_string`), and an explicit overload
|
|
18
|
-
keeps its suffix (`padding__Dp`). The
|
|
19
|
-
|
|
20
|
-
- **
|
|
21
|
-
declaration's own parameter names (every overload's), never derived.
|
|
22
|
-
- **Signatures.** A re-exported function answers `inspect.signature` with the snake_case names;
|
|
23
|
-
defaults stay `python_multiplatform.KOTLIN_DEFAULT`.
|
|
19
|
+
keeps its suffix (`padding__Dp`). The rule is applied forward to the module's real Kotlin names,
|
|
20
|
+
so a name the reverse rule could not invert still resolves.
|
|
21
|
+
- **Functions** are the binder's own callables; their keywords and signatures are the binder's.
|
|
24
22
|
- **Value classes.** The manifest's `raw-primitive-allowed` list is handed to the binder once per
|
|
25
23
|
installed binding layer, so `padding(16)` reaches a `Dp` parameter.
|
|
26
|
-
- **Methods on a proxy
|
|
27
|
-
`Modifier.padding(16).fill_max_width()`
|
|
28
|
-
|
|
29
|
-
read from `python_multiplatform.describe_member`. The Kotlin spelling still works. The binder
|
|
30
|
-
caches the alias in its own registry; its proxy classes keep Kotlin names in `dir()`.
|
|
24
|
+
- **Methods on a proxy** are the binder's: since python-multiplatform #131 it serves
|
|
25
|
+
`Modifier.padding(16).fill_max_width()` and `m.padding(padding_values=...)` itself, by this
|
|
26
|
+
package's rule, with the Kotlin spelling still working. Nothing here takes part.
|
|
31
27
|
|
|
32
28
|
- **Grouped constants.** Inside a Kotlin object served as a namespace, a type nested in it that
|
|
33
29
|
is not one of its members groups the constants declared as exactly that type, so the notebook's
|
|
@@ -50,13 +46,14 @@ from __future__ import annotations
|
|
|
50
46
|
|
|
51
47
|
import importlib
|
|
52
48
|
import importlib.util
|
|
53
|
-
import inspect
|
|
54
49
|
import re
|
|
55
50
|
import sys
|
|
56
51
|
import types
|
|
57
52
|
import weakref
|
|
53
|
+
from collections.abc import Callable, Iterable, Mapping
|
|
58
54
|
from functools import lru_cache
|
|
59
55
|
from pathlib import Path
|
|
56
|
+
from typing import Protocol, TypedDict, cast
|
|
60
57
|
|
|
61
58
|
ROOT_MODULE = "python_multiplatform"
|
|
62
59
|
BINDING_MODULE = "python_multiplatform.binding"
|
|
@@ -66,6 +63,30 @@ _MANIFEST = Path(__file__).resolve().parent / "pythonx-map.toml"
|
|
|
66
63
|
_LOWER_UPPER = re.compile(r"([a-z0-9])([A-Z])")
|
|
67
64
|
_ACRONYM_WORD = re.compile(r"([A-Z]+)([A-Z][a-z])")
|
|
68
65
|
|
|
66
|
+
# `pythonx-map.toml`'s shape. Its tables are keyed by Kotlin and module names, so the hyphenated
|
|
67
|
+
# keys are spelled in the functional form.
|
|
68
|
+
_ValueClasses = TypedDict("_ValueClasses", {"raw-primitive-allowed": list[str]}, total=False)
|
|
69
|
+
Manifest = TypedDict(
|
|
70
|
+
"Manifest",
|
|
71
|
+
{
|
|
72
|
+
"modules": dict[str, str],
|
|
73
|
+
"value-classes": _ValueClasses,
|
|
74
|
+
"aliases": dict[str, dict[str, "list[str] | dict[str, str]"]],
|
|
75
|
+
},
|
|
76
|
+
total=False,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class BindingLayer(Protocol):
|
|
81
|
+
"""What this module asks of `python_multiplatform.binding`."""
|
|
82
|
+
|
|
83
|
+
def allow_raw_primitive(self, kotlin_type: str, /) -> None: ...
|
|
84
|
+
def register_empty(self, kotlin_type: object, empty_factory: object, /) -> None: ...
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
Describe = Callable[[types.ModuleType, str], object]
|
|
88
|
+
"""`python_multiplatform.describe(module, name)`."""
|
|
89
|
+
|
|
69
90
|
|
|
70
91
|
def snake_case(kotlin_name: str) -> str:
|
|
71
92
|
"""`fillMaxWidth` -> `fill_max_width`, `toURLString` -> `to_url_string`, `zIndex` -> `z_index`."""
|
|
@@ -85,12 +106,12 @@ def python_name(kotlin_name: str) -> str:
|
|
|
85
106
|
|
|
86
107
|
|
|
87
108
|
@lru_cache(maxsize=1)
|
|
88
|
-
def manifest() ->
|
|
109
|
+
def manifest() -> Manifest:
|
|
89
110
|
"""`pythonx-map.toml`, parsed once."""
|
|
90
111
|
import tomllib
|
|
91
112
|
|
|
92
113
|
with _MANIFEST.open("rb") as handle:
|
|
93
|
-
return tomllib.load(handle)
|
|
114
|
+
return cast(Manifest, tomllib.load(handle))
|
|
94
115
|
|
|
95
116
|
|
|
96
117
|
def kotlin_package_of(module_name: str) -> str:
|
|
@@ -102,7 +123,7 @@ def kotlin_package_of(module_name: str) -> str:
|
|
|
102
123
|
) from None
|
|
103
124
|
|
|
104
125
|
|
|
105
|
-
def binding_layer():
|
|
126
|
+
def binding_layer() -> BindingLayer:
|
|
106
127
|
"""The binder's installed binding module, or a `RuntimeError` that says what did not happen."""
|
|
107
128
|
binding = sys.modules.get(BINDING_MODULE)
|
|
108
129
|
if binding is None:
|
|
@@ -110,95 +131,47 @@ def binding_layer():
|
|
|
110
131
|
"the python-multiplatform binding layer is not installed: the Kotlin host must run "
|
|
111
132
|
"PythonxAdapter.install() before pythonx.compose can resolve a Kotlin declaration"
|
|
112
133
|
)
|
|
113
|
-
return binding
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
_PRIMED = weakref.WeakSet()
|
|
117
|
-
|
|
134
|
+
return cast(BindingLayer, binding)
|
|
118
135
|
|
|
119
|
-
_MEMBER_KEYWORDS: "weakref.WeakKeyDictionary" = weakref.WeakKeyDictionary()
|
|
120
136
|
|
|
137
|
+
_PRIMED: weakref.WeakSet[BindingLayer] = weakref.WeakSet()
|
|
138
|
+
_MODIFIER_TYPE = "androidx.compose.ui.Modifier"
|
|
121
139
|
|
|
122
|
-
def _method_keywords(kotlin_type_name: str, kotlin_member_name: str) -> dict:
|
|
123
|
-
"""`{snake_case(p): p}` for every parameter `p` of every declaration of a proxy's member.
|
|
124
140
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
(
|
|
128
|
-
starts with an empty table. A keyword whose snake_case spelling is its own is left out, and a
|
|
129
|
-
binder without `describe_member`, or a member it does not describe, has no map.
|
|
130
|
-
"""
|
|
131
|
-
describe_member = getattr(sys.modules.get(ROOT_MODULE), "describe_member", None)
|
|
132
|
-
if describe_member is None:
|
|
133
|
-
return {}
|
|
134
|
-
try:
|
|
135
|
-
known = _MEMBER_KEYWORDS.setdefault(describe_member, {})
|
|
136
|
-
except TypeError: # something that cannot be weakly referenced: nothing to key a cache by
|
|
137
|
-
known = {}
|
|
138
|
-
key = (kotlin_type_name, kotlin_member_name)
|
|
139
|
-
if key not in known:
|
|
140
|
-
keywords: dict = {}
|
|
141
|
-
try:
|
|
142
|
-
declarations = describe_member(kotlin_type_name, kotlin_member_name)
|
|
143
|
-
except (TypeError, AttributeError):
|
|
144
|
-
declarations = ()
|
|
145
|
-
for declaration in declarations:
|
|
146
|
-
for parameter in declaration.get("parameters", ()):
|
|
147
|
-
kotlin_parameter = parameter.get("name")
|
|
148
|
-
if kotlin_parameter and snake_case(kotlin_parameter) != kotlin_parameter:
|
|
149
|
-
keywords.setdefault(snake_case(kotlin_parameter), kotlin_parameter)
|
|
150
|
-
known[key] = keywords
|
|
151
|
-
return known[key]
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
def member_name(kotlin_type_name: str, requested: str, kotlin_member_names):
|
|
155
|
-
"""The binder's member-resolver hook: the Kotlin member a Pythonic name means on a proxy, and
|
|
156
|
-
the keywords it takes.
|
|
157
|
-
|
|
158
|
-
The binder asks for a name its proxy has no Kotlin member of
|
|
159
|
-
(`python_multiplatform.binding.add_member_resolver`): the rule is the module rule applied
|
|
160
|
-
forward to the proxy's real member names, so `fill_max_width` finds `fillMaxWidth`. It asks
|
|
161
|
-
again for a Kotlin member name when a call passes keywords. The answer is then
|
|
162
|
-
`(kotlin_name, {python_keyword: kotlinParameter})`, the parameters of every overload of that
|
|
163
|
-
member, so `m.padding(padding_values=...)` reaches `paddingValues`. Without a keyword to map the
|
|
164
|
-
answer is the name alone, which is all a binder without `describe_member` can be given.
|
|
165
|
-
"""
|
|
166
|
-
if requested in kotlin_member_names:
|
|
167
|
-
target = requested
|
|
168
|
-
else:
|
|
169
|
-
matches = [name for name in kotlin_member_names if python_name(name) == requested]
|
|
170
|
-
target = matches[0] if len(matches) == 1 else None
|
|
171
|
-
if target is None:
|
|
172
|
-
return None
|
|
173
|
-
keywords = _method_keywords(kotlin_type_name, target)
|
|
174
|
-
return (target, keywords) if keywords else target
|
|
141
|
+
def _modifier_companion() -> object:
|
|
142
|
+
"""`Modifier.Companion` read live from `androidx.compose.ui.Modifier` (python-multiplatform #324)."""
|
|
143
|
+
return cast(object, importlib.import_module("androidx.compose.ui").Modifier.Companion)
|
|
175
144
|
|
|
176
145
|
|
|
177
|
-
def _prime(binding) -> None:
|
|
178
|
-
"""Hand this binding layer the manifest's value-class allowlist and the
|
|
146
|
+
def _prime(binding: BindingLayer) -> None:
|
|
147
|
+
"""Hand this binding layer the manifest's value-class allowlist and the default empty `Modifier`, once.
|
|
179
148
|
|
|
180
|
-
|
|
181
|
-
|
|
149
|
+
Method names and keywords on a proxy are the binder's own since python-multiplatform #131
|
|
150
|
+
(snake_case by this package's rule); nothing is registered for them here. Since
|
|
151
|
+
python-multiplatform #324, `Modifier.Companion` is bound as a value on the proxy class and
|
|
152
|
+
`register_empty` accepts a callable (`SPEC.md` S6.2, #138); if the caller has not already
|
|
153
|
+
registered an explicit empty factory for `androidx.compose.ui.Modifier`, `_modifier_companion`
|
|
154
|
+
is registered as the default.
|
|
182
155
|
"""
|
|
183
156
|
if binding in _PRIMED:
|
|
184
157
|
return
|
|
185
158
|
for kotlin_type in manifest().get("value-classes", {}).get("raw-primitive-allowed", ()):
|
|
186
159
|
binding.allow_raw_primitive(kotlin_type)
|
|
187
|
-
|
|
188
|
-
if
|
|
189
|
-
|
|
160
|
+
factories = cast("Mapping[str, object]", getattr(binding, "_EMPTY_FACTORIES", {}))
|
|
161
|
+
if _MODIFIER_TYPE not in factories:
|
|
162
|
+
binding.register_empty(_MODIFIER_TYPE, _modifier_companion)
|
|
190
163
|
_PRIMED.add(binding)
|
|
191
164
|
|
|
192
165
|
|
|
193
|
-
def kotlin_module(module_name: str):
|
|
166
|
+
def kotlin_module(module_name: str) -> types.ModuleType:
|
|
194
167
|
"""The binder's module for the Kotlin package `module_name` maps to."""
|
|
195
168
|
_prime(binding_layer())
|
|
196
169
|
return importlib.import_module(kotlin_package_of(module_name))
|
|
197
170
|
|
|
198
171
|
|
|
199
|
-
def _name_table(kotlin) -> dict:
|
|
172
|
+
def _name_table(kotlin: types.ModuleType) -> dict[str, str]:
|
|
200
173
|
"""Pythonic name -> Kotlin name for everything the Kotlin module reports, with collisions refused."""
|
|
201
|
-
table = {}
|
|
174
|
+
table: dict[str, str] = {}
|
|
202
175
|
for kotlin_name in dir(kotlin):
|
|
203
176
|
if kotlin_name.startswith("_"):
|
|
204
177
|
continue
|
|
@@ -213,7 +186,7 @@ def _name_table(kotlin) -> dict:
|
|
|
213
186
|
return table
|
|
214
187
|
|
|
215
188
|
|
|
216
|
-
def _declared_type(kotlin, kotlin_name: str):
|
|
189
|
+
def _declared_type(kotlin: types.ModuleType, kotlin_name: str) -> tuple[object, str | None] | None:
|
|
217
190
|
"""`(kind, declared return type)` of the bound name [kotlin_name] in [kotlin], without reading it.
|
|
218
191
|
|
|
219
192
|
`python_multiplatform.describe(module, name)` answers from the binder's table and never runs a
|
|
@@ -221,7 +194,7 @@ def _declared_type(kotlin, kotlin_name: str):
|
|
|
221
194
|
that is not a single bound declaration (an overload set, a child package), answers `None`.
|
|
222
195
|
"""
|
|
223
196
|
root = sys.modules.get(ROOT_MODULE)
|
|
224
|
-
describe = getattr(root, "describe", None)
|
|
197
|
+
describe = cast("Describe | None", getattr(root, "describe", None))
|
|
225
198
|
if describe is None:
|
|
226
199
|
return None
|
|
227
200
|
try:
|
|
@@ -230,78 +203,23 @@ def _declared_type(kotlin, kotlin_name: str):
|
|
|
230
203
|
return None
|
|
231
204
|
if not isinstance(described, dict):
|
|
232
205
|
return None
|
|
233
|
-
|
|
206
|
+
# The binder's answer is a plain dict of strings; its values are only compared and sliced here.
|
|
207
|
+
answer = cast(dict[str, object], described)
|
|
208
|
+
returns = answer.get("returns")
|
|
209
|
+
return answer.get("kind"), returns if isinstance(returns, str) else None
|
|
234
210
|
|
|
235
211
|
|
|
236
|
-
def
|
|
237
|
-
root = sys.modules.get(ROOT_MODULE)
|
|
238
|
-
if root is None or not hasattr(root, "describe"):
|
|
239
|
-
return ()
|
|
240
|
-
try:
|
|
241
|
-
return root.describe(fn)
|
|
242
|
-
except (TypeError, AttributeError):
|
|
243
|
-
return ()
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
class PythonicFunction:
|
|
247
|
-
"""A binder callable seen through the naming rule: snake_case keywords and signature.
|
|
248
|
-
|
|
249
|
-
Calls go straight to the binder's callable with the keywords translated; overload selection,
|
|
250
|
-
defaults and value classes are the binder's. A keyword that matches no parameter is passed on
|
|
251
|
-
unchanged, so the binder's own refusal -- which lists the candidates -- is what the caller sees.
|
|
252
|
-
"""
|
|
253
|
-
|
|
254
|
-
__slots__ = ("_kotlin", "_keywords", "__name__", "__qualname__")
|
|
255
|
-
|
|
256
|
-
def __init__(self, kotlin_fn, name):
|
|
257
|
-
self._kotlin = kotlin_fn
|
|
258
|
-
self.__name__ = name
|
|
259
|
-
self.__qualname__ = name
|
|
260
|
-
keywords = {}
|
|
261
|
-
for row in _describe(kotlin_fn):
|
|
262
|
-
for parameter in row.get("parameters", ()):
|
|
263
|
-
kotlin_parameter = parameter["name"]
|
|
264
|
-
keywords.setdefault(snake_case(kotlin_parameter), kotlin_parameter)
|
|
265
|
-
self._keywords = keywords
|
|
266
|
-
|
|
267
|
-
@property
|
|
268
|
-
def __kotlin__(self):
|
|
269
|
-
"""The binder's callable this wraps."""
|
|
270
|
-
return self._kotlin
|
|
271
|
-
|
|
272
|
-
@property
|
|
273
|
-
def __kotlin_rows__(self):
|
|
274
|
-
return self._kotlin.__kotlin_rows__
|
|
275
|
-
|
|
276
|
-
@property
|
|
277
|
-
def __signature__(self):
|
|
278
|
-
kotlin_signature = inspect.signature(self._kotlin)
|
|
279
|
-
return kotlin_signature.replace(parameters=[
|
|
280
|
-
parameter if parameter.kind in (parameter.VAR_POSITIONAL, parameter.VAR_KEYWORD)
|
|
281
|
-
else parameter.replace(name=snake_case(parameter.name))
|
|
282
|
-
for parameter in kotlin_signature.parameters.values()
|
|
283
|
-
])
|
|
284
|
-
|
|
285
|
-
def __call__(self, *args, **kwargs):
|
|
286
|
-
if kwargs:
|
|
287
|
-
kwargs = {self._keywords.get(key, key): value for key, value in kwargs.items()}
|
|
288
|
-
return self._kotlin(*args, **kwargs)
|
|
289
|
-
|
|
290
|
-
def __repr__(self):
|
|
291
|
-
return f"<pythonx function {self.__name__} -> {self._kotlin!r}>"
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
def _pythonic(value, name):
|
|
212
|
+
def _pythonic(value: object) -> object:
|
|
295
213
|
if isinstance(value, types.ModuleType):
|
|
296
214
|
# A Kotlin object served as a sub-package. Since python-multiplatform #35 the binder lists it
|
|
297
215
|
# and exposes it as an attribute of its parent, so the name table reaches it directly.
|
|
298
|
-
return
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
return
|
|
216
|
+
return _kotlin_object(value)
|
|
217
|
+
# A function is the binder's own callable: since python-multiplatform #131 it takes snake_case
|
|
218
|
+
# keywords and reports them in `inspect.signature` itself.
|
|
219
|
+
return value
|
|
302
220
|
|
|
303
221
|
|
|
304
|
-
def _resolve(kotlin, name: str):
|
|
222
|
+
def _resolve(kotlin: types.ModuleType, name: str) -> tuple[object, bool] | None:
|
|
305
223
|
"""What the Pythonic [name] means in the binder's module [kotlin]: `(value, cacheable)` or `None`.
|
|
306
224
|
|
|
307
225
|
A name the module lists is looked up by the rule. An upper-case name it does not list may be a
|
|
@@ -311,23 +229,23 @@ def _resolve(kotlin, name: str):
|
|
|
311
229
|
"""
|
|
312
230
|
kotlin_name = _name_table(kotlin).get(name)
|
|
313
231
|
if kotlin_name is not None:
|
|
314
|
-
value = getattr(kotlin, kotlin_name)
|
|
232
|
+
value = cast(object, getattr(kotlin, kotlin_name))
|
|
315
233
|
# The binder declined to cache it -- a live property, read again on every access -- so this
|
|
316
234
|
# layer does not freeze it either.
|
|
317
|
-
return _pythonic(value
|
|
235
|
+
return _pythonic(value), kotlin_name in cast(Mapping[str, object], vars(kotlin))
|
|
318
236
|
if name[:1].isupper():
|
|
319
237
|
try:
|
|
320
238
|
nested = importlib.import_module(kotlin.__name__ + "." + name)
|
|
321
239
|
except ImportError:
|
|
322
240
|
return None
|
|
323
|
-
return
|
|
241
|
+
return _kotlin_object(nested), True
|
|
324
242
|
return None
|
|
325
243
|
|
|
326
244
|
|
|
327
245
|
_STATIC_GETTER = "STATIC_GETTER"
|
|
328
246
|
|
|
329
247
|
|
|
330
|
-
def _constant_groups(kotlin) -> dict:
|
|
248
|
+
def _constant_groups(kotlin: types.ModuleType) -> dict[str, dict[str, str]]:
|
|
331
249
|
"""Group name -> {Pythonic constant name: Kotlin name}, for the Kotlin object [kotlin].
|
|
332
250
|
|
|
333
251
|
A group is a type nested in the object, `P.G`, that some constant of `P` is declared as; it holds
|
|
@@ -337,12 +255,13 @@ def _constant_groups(kotlin) -> dict:
|
|
|
337
255
|
"""
|
|
338
256
|
prefix = kotlin.__name__ + "."
|
|
339
257
|
members = _name_table(kotlin)
|
|
340
|
-
groups: dict = {}
|
|
258
|
+
groups: dict[str, dict[str, str]] = {}
|
|
341
259
|
for pythonic, kotlin_name in members.items():
|
|
342
260
|
declared = _declared_type(kotlin, kotlin_name)
|
|
343
261
|
if declared is None or declared[0] != _STATIC_GETTER or not declared[1]:
|
|
344
262
|
continue
|
|
345
|
-
|
|
263
|
+
returns = declared[1]
|
|
264
|
+
group = returns[len(prefix):] if returns.startswith(prefix) else ""
|
|
346
265
|
if not group[:1].isupper() or "." in group or group in members:
|
|
347
266
|
continue
|
|
348
267
|
groups.setdefault(group, {})[pythonic] = kotlin_name
|
|
@@ -360,17 +279,24 @@ class KotlinObject:
|
|
|
360
279
|
|
|
361
280
|
__slots__ = ("_kotlin", "_cache", "_groups")
|
|
362
281
|
|
|
363
|
-
|
|
282
|
+
_kotlin: types.ModuleType
|
|
283
|
+
_cache: dict[str, object]
|
|
284
|
+
_groups: dict[str, dict[str, str]] | None
|
|
285
|
+
|
|
286
|
+
def __init__(self, kotlin_module: types.ModuleType) -> None:
|
|
364
287
|
object.__setattr__(self, "_kotlin", kotlin_module)
|
|
365
|
-
object.__setattr__(self, "_cache",
|
|
288
|
+
object.__setattr__(self, "_cache", dict[str, object]())
|
|
366
289
|
object.__setattr__(self, "_groups", None)
|
|
367
290
|
|
|
368
|
-
def _constant_groups(self) -> dict:
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
291
|
+
def _constant_groups(self) -> dict[str, dict[str, str]]:
|
|
292
|
+
known = self._groups
|
|
293
|
+
if known is not None:
|
|
294
|
+
return known
|
|
295
|
+
groups = _constant_groups(self._kotlin)
|
|
296
|
+
object.__setattr__(self, "_groups", groups)
|
|
297
|
+
return groups
|
|
372
298
|
|
|
373
|
-
def __getattr__(self, name):
|
|
299
|
+
def __getattr__(self, name: str) -> object:
|
|
374
300
|
if name.startswith("__") and name.endswith("__"):
|
|
375
301
|
raise AttributeError(name)
|
|
376
302
|
cached = self._cache.get(name)
|
|
@@ -384,22 +310,43 @@ class KotlinObject:
|
|
|
384
310
|
f"{self._kotlin.__name__} has nothing spelled {name!r} under pythonx's naming rule"
|
|
385
311
|
)
|
|
386
312
|
# The grouping is metadata and is kept; the constants inside are read on each access.
|
|
387
|
-
|
|
313
|
+
group = ConstantGroup(self, f"{self._kotlin.__name__}.{name}", members)
|
|
314
|
+
self._cache[name] = group
|
|
315
|
+
return group
|
|
388
316
|
value, cacheable = resolved
|
|
389
317
|
if cacheable:
|
|
390
318
|
self._cache[name] = value
|
|
391
319
|
return value
|
|
392
320
|
|
|
393
|
-
def __setattr__(self, name, value):
|
|
321
|
+
def __setattr__(self, name: str, value: object) -> None:
|
|
394
322
|
raise AttributeError(f"{self._kotlin.__name__} is a Kotlin object; its members are read-only here")
|
|
395
323
|
|
|
396
|
-
def __dir__(self):
|
|
324
|
+
def __dir__(self) -> list[str]:
|
|
397
325
|
return sorted(set(_name_table(self._kotlin)) | set(self._constant_groups()))
|
|
398
326
|
|
|
399
|
-
def __repr__(self):
|
|
327
|
+
def __repr__(self) -> str:
|
|
400
328
|
return f"<pythonx view of Kotlin object {self._kotlin.__name__}>"
|
|
401
329
|
|
|
402
330
|
|
|
331
|
+
class CallableKotlinObject(KotlinObject):
|
|
332
|
+
"""A Kotlin object whose name is also a function in its package (`Color`), callable as that function.
|
|
333
|
+
|
|
334
|
+
The binder makes such a module callable (python-multiplatform #78): `Color(0xFFFFFFFF)` calls
|
|
335
|
+
the `Color` function, while `Color.Red` is still a constant of the object. Calling goes to the
|
|
336
|
+
binder's module itself, which resolves the function on every call.
|
|
337
|
+
"""
|
|
338
|
+
|
|
339
|
+
__slots__ = ()
|
|
340
|
+
|
|
341
|
+
def __call__(self, *args: object, **kwargs: object) -> object:
|
|
342
|
+
return cast("Callable[..., object]", self._kotlin)(*args, **kwargs)
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def _kotlin_object(kotlin_module: types.ModuleType) -> KotlinObject:
|
|
346
|
+
"""The namespace for a Kotlin object module: callable only when the binder made the module callable."""
|
|
347
|
+
return CallableKotlinObject(kotlin_module) if callable(kotlin_module) else KotlinObject(kotlin_module)
|
|
348
|
+
|
|
349
|
+
|
|
403
350
|
class ConstantGroup:
|
|
404
351
|
"""The constants of a Kotlin object declared as one type nested in it: `Alignment.Horizontal`.
|
|
405
352
|
|
|
@@ -410,12 +357,16 @@ class ConstantGroup:
|
|
|
410
357
|
|
|
411
358
|
__slots__ = ("_owner", "_kotlin_type", "_members")
|
|
412
359
|
|
|
413
|
-
|
|
360
|
+
_owner: KotlinObject
|
|
361
|
+
_kotlin_type: str
|
|
362
|
+
_members: dict[str, str]
|
|
363
|
+
|
|
364
|
+
def __init__(self, owner: KotlinObject, kotlin_type: str, members: Mapping[str, str]) -> None:
|
|
414
365
|
object.__setattr__(self, "_owner", owner)
|
|
415
366
|
object.__setattr__(self, "_kotlin_type", kotlin_type)
|
|
416
367
|
object.__setattr__(self, "_members", dict(members))
|
|
417
368
|
|
|
418
|
-
def __getattr__(self, name):
|
|
369
|
+
def __getattr__(self, name: str) -> object:
|
|
419
370
|
if name.startswith("__") and name.endswith("__"):
|
|
420
371
|
raise AttributeError(name)
|
|
421
372
|
if name not in self._members:
|
|
@@ -424,33 +375,36 @@ class ConstantGroup:
|
|
|
424
375
|
f"{self._owner._kotlin.__name__} declared as {self._kotlin_type}: "
|
|
425
376
|
+ ", ".join(sorted(self._members))
|
|
426
377
|
)
|
|
427
|
-
|
|
378
|
+
# The owner's own resolver, by name: a constant is never an attribute of `KotlinObject` itself.
|
|
379
|
+
return self._owner.__getattr__(name)
|
|
428
380
|
|
|
429
|
-
def __setattr__(self, name, value):
|
|
381
|
+
def __setattr__(self, name: str, value: object) -> None:
|
|
430
382
|
raise AttributeError(f"{self._kotlin_type} is a group of Kotlin constants; they are read-only here")
|
|
431
383
|
|
|
432
|
-
def __dir__(self):
|
|
384
|
+
def __dir__(self) -> list[str]:
|
|
433
385
|
return sorted(self._members)
|
|
434
386
|
|
|
435
|
-
def __repr__(self):
|
|
387
|
+
def __repr__(self) -> str:
|
|
436
388
|
return f"<pythonx group of {self._owner._kotlin.__name__} constants declared {self._kotlin_type}>"
|
|
437
389
|
|
|
438
390
|
|
|
439
|
-
def _aliases(module_name: str) -> dict:
|
|
391
|
+
def _aliases(module_name: str) -> dict[str, tuple[str, str]]:
|
|
440
392
|
"""Name -> `(source module, attribute path in it)`, per the manifest's `[aliases]`.
|
|
441
393
|
|
|
442
394
|
An entry under a source module is a list of names (lent under the same name, path = the name) or
|
|
443
395
|
a table `{ lent = "Path.To.Attr" }` (a different name, through an attribute path).
|
|
444
396
|
"""
|
|
445
|
-
borrowed = {}
|
|
397
|
+
borrowed: dict[str, tuple[str, str]] = {}
|
|
446
398
|
for source, lent in manifest().get("aliases", {}).get(module_name, {}).items():
|
|
447
|
-
pairs
|
|
399
|
+
pairs: Iterable[tuple[str, str]] = (
|
|
400
|
+
((name, name) for name in lent) if isinstance(lent, list) else lent.items()
|
|
401
|
+
)
|
|
448
402
|
for name, path in pairs:
|
|
449
403
|
borrowed[name] = (source, path)
|
|
450
404
|
return borrowed
|
|
451
405
|
|
|
452
406
|
|
|
453
|
-
def _borrow(module_name: str, name: str, source: str, path: str):
|
|
407
|
+
def _borrow(module_name: str, name: str, source: str, path: str) -> object:
|
|
454
408
|
"""The value of the alias [name]: [path] read in [source].
|
|
455
409
|
|
|
456
410
|
A bare name is resolved once and kept in the owner's namespace. A path with attribute steps is
|
|
@@ -458,22 +412,22 @@ def _borrow(module_name: str, name: str, source: str, path: str):
|
|
|
458
412
|
serves `Icons.Default` afresh on every read and this layer does not freeze it either.
|
|
459
413
|
"""
|
|
460
414
|
head, *rest = path.split(".")
|
|
461
|
-
value = getattr(importlib.import_module(source), head)
|
|
415
|
+
value = cast(object, getattr(importlib.import_module(source), head))
|
|
462
416
|
for step in rest:
|
|
463
|
-
value = getattr(value, step)
|
|
417
|
+
value = cast(object, getattr(value, step))
|
|
464
418
|
if not rest:
|
|
465
419
|
setattr(sys.modules[module_name], name, value)
|
|
466
420
|
return value
|
|
467
421
|
|
|
468
422
|
|
|
469
|
-
def reexport(module_name: str):
|
|
423
|
+
def reexport(module_name: str) -> tuple[Callable[[str], object], Callable[[], list[str]]]:
|
|
470
424
|
"""The module-level `__getattr__` and `__dir__` for the mapped package `module_name`.
|
|
471
425
|
|
|
472
426
|
Nothing is resolved here, only when a name is first read, so importing a `pythonx.compose`
|
|
473
427
|
module needs neither the manifest row's Kotlin package nor an installed binder.
|
|
474
428
|
"""
|
|
475
429
|
|
|
476
|
-
def __getattr__(name):
|
|
430
|
+
def __getattr__(name: str) -> object:
|
|
477
431
|
if name.startswith("__") and name.endswith("__"):
|
|
478
432
|
raise AttributeError(name)
|
|
479
433
|
if importlib.util.find_spec(module_name + "." + name) is not None:
|
|
@@ -501,8 +455,8 @@ def reexport(module_name: str):
|
|
|
501
455
|
setattr(sys.modules[module_name], name, value)
|
|
502
456
|
return value
|
|
503
457
|
|
|
504
|
-
def __dir__():
|
|
505
|
-
own = set(vars(sys.modules[module_name])) | set(_aliases(module_name))
|
|
458
|
+
def __dir__() -> list[str]:
|
|
459
|
+
own = set(cast(Mapping[str, object], vars(sys.modules[module_name]))) | set(_aliases(module_name))
|
|
506
460
|
try:
|
|
507
461
|
own.update(_name_table(kotlin_module(module_name)))
|
|
508
462
|
except (RuntimeError, ImportError):
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# GENERATED by scripts/gen_stubs.py from python-multiplatform's Kotlin-named stubs (artifact
|
|
1
|
+
# GENERATED by scripts/gen_stubs.py from python-multiplatform's Kotlin-named stubs (artifact ks, workflow run 42402779166, commit 9507e6dd, Compose 1.11.1-ext.2). Do not edit.
|
|
2
2
|
import typing as _t
|
|
3
3
|
|
|
4
4
|
class BorderStroke:
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
# GENERATED by scripts/gen_stubs.py from python-multiplatform's Kotlin-named stubs (artifact
|
|
1
|
+
# GENERATED by scripts/gen_stubs.py from python-multiplatform's Kotlin-named stubs (artifact ks, workflow run 42402779166, commit 9507e6dd, Compose 1.11.1-ext.2). Do not edit.
|
|
2
2
|
from pythonx.compose.layout import *
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# GENERATED by scripts/gen_stubs.py from python-multiplatform's Kotlin-named stubs (artifact
|
|
1
|
+
# GENERATED by scripts/gen_stubs.py from python-multiplatform's Kotlin-named stubs (artifact ks, workflow run 42402779166, commit 9507e6dd, Compose 1.11.1-ext.2). Do not edit.
|
|
2
2
|
import typing as _t
|
|
3
3
|
|
|
4
4
|
class _TextFieldState_clearText(_t.Protocol):
|
|
@@ -25,18 +25,24 @@ class OutputTransformation:
|
|
|
25
25
|
class TextFieldBuffer:
|
|
26
26
|
"""Kotlin: androidx.compose.foundation.text.input.TextFieldBuffer"""
|
|
27
27
|
|
|
28
|
+
class TextFieldDecorator:
|
|
29
|
+
"""Kotlin: androidx.compose.foundation.text.input.TextFieldDecorator"""
|
|
30
|
+
|
|
28
31
|
class TextFieldLineLimits:
|
|
29
32
|
"""Kotlin: androidx.compose.foundation.text.input.TextFieldLineLimits"""
|
|
30
33
|
|
|
31
34
|
class TextFieldState:
|
|
32
35
|
"""Kotlin: androidx.compose.foundation.text.input.TextFieldState"""
|
|
33
|
-
def __init__(self,
|
|
36
|
+
def __init__(self, initial_text: str = ..., initial_selection: _t.Any = ...) -> None:
|
|
34
37
|
"""Kotlin constructor: androidx.compose.foundation.text.input.TextFieldState (androidx.compose.foundation.text.input.TextFieldState(initialText: kotlin.String, initialSelection: androidx.compose.ui.text.TextRange): androidx.compose.foundation.text.input.TextFieldState)"""
|
|
35
38
|
...
|
|
36
39
|
clear_text: _t.ClassVar[_TextFieldState_clearText]
|
|
37
40
|
set_text_and_place_cursor_at_end: _t.ClassVar[_TextFieldState_setTextAndPlaceCursorAtEnd]
|
|
38
41
|
set_text_and_select_all: _t.ClassVar[_TextFieldState_setTextAndSelectAll]
|
|
39
42
|
to_text_field_buffer: _t.ClassVar[_TextFieldState_toTextFieldBuffer]
|
|
43
|
+
def edit(self, block: _t.Callable[[TextFieldBuffer], None]) -> None:
|
|
44
|
+
"""Kotlin member: androidx.compose.foundation.text.input.TextFieldState.edit"""
|
|
45
|
+
...
|
|
40
46
|
@property
|
|
41
47
|
def composition(self) -> _t.Any | None:
|
|
42
48
|
"""Kotlin property: androidx.compose.foundation.text.input.TextFieldState.composition (androidx.compose.foundation.text.input.TextFieldState.composition: androidx.compose.ui.text.TextRange?)"""
|