hypie 0.1.1.dev4__tar.gz → 0.1.1.dev5__tar.gz
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.
- hypie-0.1.1.dev5/PKG-INFO +104 -0
- hypie-0.1.1.dev5/README.md +90 -0
- {hypie-0.1.1.dev4 → hypie-0.1.1.dev5}/pyproject.toml +2 -1
- hypie-0.1.1.dev5/src/hypie/__init__.py +6 -0
- hypie-0.1.1.dev5/src/hypie/cli/build_components.py +142 -0
- {hypie-0.1.1.dev4 → hypie-0.1.1.dev5}/src/hypie/commands.py +21 -27
- {hypie-0.1.1.dev4 → hypie-0.1.1.dev5}/src/hypie/events.py +77 -77
- hypie-0.1.1.dev5/src/hypie/experimental/client_component.py +120 -0
- hypie-0.1.1.dev5/src/hypie/experimental/server_component.py +56 -0
- hypie-0.1.1.dev5/src/hypie/experimental/style.py +62 -0
- {hypie-0.1.1.dev4 → hypie-0.1.1.dev5}/src/hypie/features.py +37 -0
- {hypie-0.1.1.dev4 → hypie-0.1.1.dev5}/src/hypie/hs_ast/commands.py +35 -2
- {hypie-0.1.1.dev4 → hypie-0.1.1.dev5}/src/hypie/hs_ast/expressions.py +67 -35
- {hypie-0.1.1.dev4 → hypie-0.1.1.dev5}/src/hypie/hs_ast/features.py +73 -3
- hypie-0.1.1.dev5/src/hypie/hs_ast/helpers.py +120 -0
- hypie-0.1.1.dev4/PKG-INFO +0 -915
- hypie-0.1.1.dev4/README.md +0 -901
- hypie-0.1.1.dev4/src/hypie/__init__.py +0 -34
- hypie-0.1.1.dev4/src/hypie/cli/build_components.py +0 -148
- hypie-0.1.1.dev4/src/hypie/experimental/client_components.py +0 -78
- hypie-0.1.1.dev4/src/hypie/experimental/components.py +0 -135
- hypie-0.1.1.dev4/src/hypie/experimental/hyperscript.py +0 -35
- hypie-0.1.1.dev4/src/hypie/experimental/templates.py +0 -50
- hypie-0.1.1.dev4/src/hypie/hs_ast/helpers.py +0 -85
- {hypie-0.1.1.dev4 → hypie-0.1.1.dev5}/LICENSE +0 -0
- {hypie-0.1.1.dev4 → hypie-0.1.1.dev5}/src/hypie/dom_position.py +0 -0
- {hypie-0.1.1.dev4 → hypie-0.1.1.dev5}/src/hypie/hs_ast/constants.py +0 -0
- {hypie-0.1.1.dev4 → hypie-0.1.1.dev5}/src/hypie/hs_ast/contexts.py +0 -0
- {hypie-0.1.1.dev4 → hypie-0.1.1.dev5}/src/hypie/literals.py +0 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hypie
|
|
3
|
+
Version: 0.1.1.dev5
|
|
4
|
+
Summary: Add your description here
|
|
5
|
+
Author: Yasser Ahmed
|
|
6
|
+
Author-email: Yasser Ahmed <yasserfawzi1029@gmail.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: htpy>=26.5.1
|
|
10
|
+
Requires-Dist: watchfiles>=1.2.0
|
|
11
|
+
Requires-Python: >=3.12
|
|
12
|
+
Project-URL: Repository, https://github.com/Infrared1029/hypie
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
<p align="center">
|
|
16
|
+
<img src="https://raw.githubusercontent.com/Infrared1029/hypie/main/assets/hypie_logo.jpg" alt="Logo" width="400">
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
## Intro
|
|
20
|
+
Hypie, pronounced "high-pie", is a python library for building web frontends, built on top of [_hyperscript](https://github.com/bigskysoftware/_hyperscript), a scripting language for the web made by the [htmx](https://github.com/bigskysoftware/htmx) folks, and [htpy](https://github.com/pelme/htpy), a library for generating HTML in python.
|
|
21
|
+
|
|
22
|
+
The goal of hypie is to be able to code powerful web frontends, server-side rendered or client-side rendered (or a mix of both), without having to leave python, or switch your favorite backend, using relatively little abstractions and no reactivity.
|
|
23
|
+
|
|
24
|
+
New README in the process, for now enjoy this counter example:
|
|
25
|
+
```python
|
|
26
|
+
import htpy
|
|
27
|
+
import hypie as hp
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class CounterWidget(hp.ServerComponent):
|
|
31
|
+
initial_count: int = 0
|
|
32
|
+
|
|
33
|
+
def template(self):
|
|
34
|
+
count_var = hp.var(":count")
|
|
35
|
+
|
|
36
|
+
return htpy.div[
|
|
37
|
+
htpy.button(
|
|
38
|
+
_=hp.hs(
|
|
39
|
+
hp.set_(count_var, to=self.initial_count),
|
|
40
|
+
hp.On("click")[
|
|
41
|
+
hp.set_(count_var, to=count_var + 1),
|
|
42
|
+
hp.set_(hp.me.textContent, to=f"Count is: {count_var}"),
|
|
43
|
+
],
|
|
44
|
+
)
|
|
45
|
+
)[f"Count is: {self.initial_count}"]
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@hp.style(CounterWidget)
|
|
50
|
+
def counter_style():
|
|
51
|
+
return {
|
|
52
|
+
"button": {
|
|
53
|
+
"background-color": "#4A90D9",
|
|
54
|
+
"color": "#ffffff",
|
|
55
|
+
"border": "none",
|
|
56
|
+
"border-radius": "6px",
|
|
57
|
+
"padding": "10px 20px",
|
|
58
|
+
"font-size": "16px",
|
|
59
|
+
"font-weight": "600",
|
|
60
|
+
"cursor": "pointer",
|
|
61
|
+
"transition": "background-color 0.2s ease",
|
|
62
|
+
},
|
|
63
|
+
"button:hover": {"background-color": "#3A7BC8"},
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
```sh
|
|
68
|
+
hypie build -i ./app/CounterWidget.py -o ./app/static
|
|
69
|
+
```
|
|
70
|
+
(or your favourite backend)
|
|
71
|
+
```python
|
|
72
|
+
import pathlib
|
|
73
|
+
|
|
74
|
+
import htpy
|
|
75
|
+
from fastapi import FastAPI
|
|
76
|
+
from fastapi.staticfiles import StaticFiles
|
|
77
|
+
from fastapi.responses import HTMLResponse
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
from .CounterWidget import CounterWidget
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
app = FastAPI()
|
|
84
|
+
|
|
85
|
+
app.mount(
|
|
86
|
+
"/static", StaticFiles(directory=pathlib.Path(__file__).parent.resolve() / "static")
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@app.get("/")
|
|
91
|
+
def counter_example(count: int = 0):
|
|
92
|
+
return HTMLResponse(
|
|
93
|
+
htpy.html[
|
|
94
|
+
htpy.head[
|
|
95
|
+
htpy.link(),
|
|
96
|
+
htpy.link(rel="stylesheet", href="/static/_hypie_styles.css"),
|
|
97
|
+
htpy.script(src="https://cdn.jsdelivr.net/npm/hyperscript.org@0.9.93"),
|
|
98
|
+
htpy.title["Counter Example"],
|
|
99
|
+
],
|
|
100
|
+
htpy.body[CounterWidget(count)],
|
|
101
|
+
]
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
```
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/Infrared1029/hypie/main/assets/hypie_logo.jpg" alt="Logo" width="400">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
## Intro
|
|
6
|
+
Hypie, pronounced "high-pie", is a python library for building web frontends, built on top of [_hyperscript](https://github.com/bigskysoftware/_hyperscript), a scripting language for the web made by the [htmx](https://github.com/bigskysoftware/htmx) folks, and [htpy](https://github.com/pelme/htpy), a library for generating HTML in python.
|
|
7
|
+
|
|
8
|
+
The goal of hypie is to be able to code powerful web frontends, server-side rendered or client-side rendered (or a mix of both), without having to leave python, or switch your favorite backend, using relatively little abstractions and no reactivity.
|
|
9
|
+
|
|
10
|
+
New README in the process, for now enjoy this counter example:
|
|
11
|
+
```python
|
|
12
|
+
import htpy
|
|
13
|
+
import hypie as hp
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class CounterWidget(hp.ServerComponent):
|
|
17
|
+
initial_count: int = 0
|
|
18
|
+
|
|
19
|
+
def template(self):
|
|
20
|
+
count_var = hp.var(":count")
|
|
21
|
+
|
|
22
|
+
return htpy.div[
|
|
23
|
+
htpy.button(
|
|
24
|
+
_=hp.hs(
|
|
25
|
+
hp.set_(count_var, to=self.initial_count),
|
|
26
|
+
hp.On("click")[
|
|
27
|
+
hp.set_(count_var, to=count_var + 1),
|
|
28
|
+
hp.set_(hp.me.textContent, to=f"Count is: {count_var}"),
|
|
29
|
+
],
|
|
30
|
+
)
|
|
31
|
+
)[f"Count is: {self.initial_count}"]
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@hp.style(CounterWidget)
|
|
36
|
+
def counter_style():
|
|
37
|
+
return {
|
|
38
|
+
"button": {
|
|
39
|
+
"background-color": "#4A90D9",
|
|
40
|
+
"color": "#ffffff",
|
|
41
|
+
"border": "none",
|
|
42
|
+
"border-radius": "6px",
|
|
43
|
+
"padding": "10px 20px",
|
|
44
|
+
"font-size": "16px",
|
|
45
|
+
"font-weight": "600",
|
|
46
|
+
"cursor": "pointer",
|
|
47
|
+
"transition": "background-color 0.2s ease",
|
|
48
|
+
},
|
|
49
|
+
"button:hover": {"background-color": "#3A7BC8"},
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
```sh
|
|
54
|
+
hypie build -i ./app/CounterWidget.py -o ./app/static
|
|
55
|
+
```
|
|
56
|
+
(or your favourite backend)
|
|
57
|
+
```python
|
|
58
|
+
import pathlib
|
|
59
|
+
|
|
60
|
+
import htpy
|
|
61
|
+
from fastapi import FastAPI
|
|
62
|
+
from fastapi.staticfiles import StaticFiles
|
|
63
|
+
from fastapi.responses import HTMLResponse
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
from .CounterWidget import CounterWidget
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
app = FastAPI()
|
|
70
|
+
|
|
71
|
+
app.mount(
|
|
72
|
+
"/static", StaticFiles(directory=pathlib.Path(__file__).parent.resolve() / "static")
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@app.get("/")
|
|
77
|
+
def counter_example(count: int = 0):
|
|
78
|
+
return HTMLResponse(
|
|
79
|
+
htpy.html[
|
|
80
|
+
htpy.head[
|
|
81
|
+
htpy.link(),
|
|
82
|
+
htpy.link(rel="stylesheet", href="/static/_hypie_styles.css"),
|
|
83
|
+
htpy.script(src="https://cdn.jsdelivr.net/npm/hyperscript.org@0.9.93"),
|
|
84
|
+
htpy.title["Counter Example"],
|
|
85
|
+
],
|
|
86
|
+
htpy.body[CounterWidget(count)],
|
|
87
|
+
]
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "hypie"
|
|
3
|
-
version = "0.1.1.
|
|
3
|
+
version = "0.1.1.dev5"
|
|
4
4
|
description = "Add your description here"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
@@ -30,6 +30,7 @@ dev = [
|
|
|
30
30
|
]
|
|
31
31
|
examples = [
|
|
32
32
|
"fastapi[standard]>=0.139.0",
|
|
33
|
+
"requests>=2.34.2",
|
|
33
34
|
]
|
|
34
35
|
|
|
35
36
|
[[tool.uv.index]]
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
import pathlib
|
|
3
|
+
import sys
|
|
4
|
+
import inspect
|
|
5
|
+
import traceback
|
|
6
|
+
|
|
7
|
+
import argparse
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from hypie.hs_ast.expressions import Expr
|
|
11
|
+
from hypie.experimental.style import generate_scoped_css
|
|
12
|
+
|
|
13
|
+
import watchfiles
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def find_components_register_artifacts(in_path, out_path, out_files_prefix=""):
|
|
17
|
+
IGNORE_LIST = [".venv", ".pyc", ".pyo"]
|
|
18
|
+
MODULES = set()
|
|
19
|
+
|
|
20
|
+
BEHAVIORS = set()
|
|
21
|
+
STYLES = set()
|
|
22
|
+
try:
|
|
23
|
+
path = pathlib.Path(in_path).resolve()
|
|
24
|
+
if not path.exists():
|
|
25
|
+
raise Exception("Input path does not exist")
|
|
26
|
+
# print(path, path.parent.resolve())
|
|
27
|
+
outdir = pathlib.Path(out_path).resolve()
|
|
28
|
+
if outdir.is_file():
|
|
29
|
+
raise Exception("--output must be a dir")
|
|
30
|
+
outdir.mkdir(exist_ok=True, parents=True)
|
|
31
|
+
# print(outdir)
|
|
32
|
+
sys.path.insert(0, str(path.parent.resolve()))
|
|
33
|
+
path_parts_len = len(path.parts)
|
|
34
|
+
if path.is_file():
|
|
35
|
+
python_files = [path]
|
|
36
|
+
else:
|
|
37
|
+
python_files = pathlib.Path(path).glob("**/*.py")
|
|
38
|
+
for file_path in python_files:
|
|
39
|
+
if any(p in file_path.parts for p in IGNORE_LIST):
|
|
40
|
+
continue
|
|
41
|
+
module_name_parts = list(file_path.parts[path_parts_len - 1 : -1]) + [
|
|
42
|
+
file_path.stem
|
|
43
|
+
]
|
|
44
|
+
module_name = ".".join(module_name_parts)
|
|
45
|
+
mod = importlib.import_module(module_name)
|
|
46
|
+
MODULES.add(module_name)
|
|
47
|
+
for _, obj in inspect.getmembers(mod):
|
|
48
|
+
if isinstance(obj, Expr):
|
|
49
|
+
continue
|
|
50
|
+
elif getattr(obj, "_hs_behavior", False) == True:
|
|
51
|
+
BEHAVIORS.add(obj)
|
|
52
|
+
elif getattr(obj, "_hypie_style", False) == True:
|
|
53
|
+
STYLES.add(obj)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# create css files
|
|
57
|
+
styles = []
|
|
58
|
+
hs_files = []
|
|
59
|
+
html = []
|
|
60
|
+
print(
|
|
61
|
+
f"Done Proccessing: {len(STYLES)} Styles, {len(BEHAVIORS)} Behaviors."
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
for s in STYLES:
|
|
65
|
+
style = s._style
|
|
66
|
+
if style:
|
|
67
|
+
styles.append(generate_scoped_css(", ".join(s._components), style))
|
|
68
|
+
|
|
69
|
+
for b in BEHAVIORS:
|
|
70
|
+
script = b._behavior
|
|
71
|
+
if script:
|
|
72
|
+
hs_files.append(script)
|
|
73
|
+
|
|
74
|
+
style_contents = "\n".join(styles)
|
|
75
|
+
if style_contents:
|
|
76
|
+
with open(outdir / f"{out_files_prefix}_hypie_styles.css", "w") as f:
|
|
77
|
+
f.write(style_contents)
|
|
78
|
+
|
|
79
|
+
html_contents = "\n".join(html)
|
|
80
|
+
if html_contents:
|
|
81
|
+
with open(outdir / f"{out_files_prefix}_hypie_templates.html", "w") as f:
|
|
82
|
+
f.write(html_contents)
|
|
83
|
+
|
|
84
|
+
hs_contents = "\n".join(h.render() for h in hs_files)
|
|
85
|
+
if hs_contents:
|
|
86
|
+
with open(outdir / f"{out_files_prefix}_hypie_hyperscript._hs", "w") as f:
|
|
87
|
+
f.write(hs_contents)
|
|
88
|
+
finally:
|
|
89
|
+
# clean up
|
|
90
|
+
sys.path.pop(0)
|
|
91
|
+
for mod_name in MODULES:
|
|
92
|
+
del sys.modules[mod_name]
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def main():
|
|
96
|
+
cli_parser = argparse.ArgumentParser(prog="hypie")
|
|
97
|
+
|
|
98
|
+
subparsers = cli_parser.add_subparsers(dest="command", required=True)
|
|
99
|
+
build_parser = subparsers.add_parser("build", help="Build components")
|
|
100
|
+
build_parser.add_argument(
|
|
101
|
+
"--input", "-i", type=Path, required=True, help="Path to components dir"
|
|
102
|
+
)
|
|
103
|
+
build_parser.add_argument(
|
|
104
|
+
"--output", "-o", type=Path, required=True, help="Output path for file"
|
|
105
|
+
)
|
|
106
|
+
build_parser.add_argument(
|
|
107
|
+
"--prefix",
|
|
108
|
+
"-p",
|
|
109
|
+
type=str,
|
|
110
|
+
required=False,
|
|
111
|
+
default="",
|
|
112
|
+
help="output files prefix",
|
|
113
|
+
)
|
|
114
|
+
build_parser.add_argument(
|
|
115
|
+
"--watch",
|
|
116
|
+
"-w",
|
|
117
|
+
action="store_true",
|
|
118
|
+
required=False,
|
|
119
|
+
default=False,
|
|
120
|
+
help="watch dir and re-run build",
|
|
121
|
+
)
|
|
122
|
+
args = cli_parser.parse_args()
|
|
123
|
+
|
|
124
|
+
if args.command == "build":
|
|
125
|
+
find_components_register_artifacts(
|
|
126
|
+
out_files_prefix=args.prefix, in_path=args.input, out_path=args.output
|
|
127
|
+
)
|
|
128
|
+
if args.watch:
|
|
129
|
+
print("[hypie]: waiting for changes...")
|
|
130
|
+
for _ in watchfiles.watch(args.input):
|
|
131
|
+
print("[hypie]: detected changes, re-running build...")
|
|
132
|
+
try:
|
|
133
|
+
find_components_register_artifacts(
|
|
134
|
+
out_files_prefix=args.prefix,
|
|
135
|
+
in_path=args.input,
|
|
136
|
+
out_path=args.output,
|
|
137
|
+
)
|
|
138
|
+
except KeyboardInterrupt:
|
|
139
|
+
exit()
|
|
140
|
+
except Exception as e:
|
|
141
|
+
traceback.print_exc()
|
|
142
|
+
print("[hypie]: waiting for changes...")
|
|
@@ -3,8 +3,7 @@ from typing import overload
|
|
|
3
3
|
from hypie.hs_ast.commands import *
|
|
4
4
|
from hypie.hs_ast.expressions import *
|
|
5
5
|
from hypie.events import Event
|
|
6
|
-
from hypie.experimental.
|
|
7
|
-
# from hypie.experimental.client_components import ClientComponent
|
|
6
|
+
from hypie.experimental.client_component import ClientComponent
|
|
8
7
|
|
|
9
8
|
|
|
10
9
|
@overload
|
|
@@ -128,26 +127,6 @@ def set_(set_expr: VariableLiteral, /, to: Expr):
|
|
|
128
127
|
|
|
129
128
|
|
|
130
129
|
def put(expr: Expr, /, placement: PUT_PLACEMENTS, target: Expr):
|
|
131
|
-
if (
|
|
132
|
-
hasattr(expr, "_hs_dsl_client_component")
|
|
133
|
-
and expr._hs_dsl_client_component == True
|
|
134
|
-
):
|
|
135
|
-
# expr = TemplateLiteral(expr.__html__())
|
|
136
|
-
tag = f"<{expr.component_name}/>"
|
|
137
|
-
# f:python_to_hs(getattr(self, f)) for f in self.field_names
|
|
138
|
-
commands = [make(tag)]
|
|
139
|
-
# for f in expr.field_names
|
|
140
|
-
return [
|
|
141
|
-
make(tag),
|
|
142
|
-
[
|
|
143
|
-
set_(
|
|
144
|
-
VariableLiteral(f"result's @{f}"),
|
|
145
|
-
to=VariableLiteral(f"`'${{{getattr(expr, f)}}}'`"),
|
|
146
|
-
)
|
|
147
|
-
for f in expr.field_names
|
|
148
|
-
],
|
|
149
|
-
Put(expr=VariableLiteral("result"), placement=placement, target=target),
|
|
150
|
-
]
|
|
151
130
|
return Put(expr=expr, placement=placement, target=target)
|
|
152
131
|
|
|
153
132
|
|
|
@@ -211,10 +190,25 @@ def make(object_name: str, /, *from_: Expr | list[Expr], called: str = None):
|
|
|
211
190
|
return Make(object_name=object_name, from_=from_, called=called)
|
|
212
191
|
|
|
213
192
|
|
|
214
|
-
def render(template:
|
|
215
|
-
|
|
193
|
+
def render(template: ClientComponent):
|
|
194
|
+
def extract_expr(v):
|
|
195
|
+
if isinstance(v, TemplatedVariableLiteral):
|
|
196
|
+
return v.symbol
|
|
197
|
+
if isinstance(template, (ClientComponent)):
|
|
216
198
|
return Render(
|
|
217
|
-
template_id=template.
|
|
218
|
-
with_={f: getattr(template, f) for f in template.field_names},
|
|
199
|
+
template_id=template._template_id,
|
|
200
|
+
with_={f: extract_expr(getattr(template, f)) for f in template.field_names},
|
|
219
201
|
)
|
|
220
|
-
raise Exception("render only accepts
|
|
202
|
+
raise Exception("render only accepts Client Fragments")
|
|
203
|
+
|
|
204
|
+
def append(expr: Expr, / , to):
|
|
205
|
+
return Append(expr=expr, to=to)
|
|
206
|
+
|
|
207
|
+
def js(*vars):
|
|
208
|
+
_vars = []
|
|
209
|
+
for v in vars:
|
|
210
|
+
if isinstance(v, VariableLiteral):
|
|
211
|
+
_vars.append(v.symbol)
|
|
212
|
+
else:
|
|
213
|
+
_vars.append(v)
|
|
214
|
+
return Js(vars=_vars)
|
|
@@ -1,77 +1,77 @@
|
|
|
1
|
-
from hypie.literals import var, Expr, TimeLiteral
|
|
2
|
-
from typing import Literal, dataclass_transform
|
|
3
|
-
from dataclasses import fields, dataclass, is_dataclass
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
@dataclass
|
|
7
|
-
class EventSpec:
|
|
8
|
-
filter: Expr = (None,)
|
|
9
|
-
count: int = (None,)
|
|
10
|
-
from_: Expr | Literal["elsewhere"] = (None,)
|
|
11
|
-
in_: Expr = (None,)
|
|
12
|
-
debounce: TimeLiteral = (None,)
|
|
13
|
-
throttle: TimeLiteral = (None,)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
@dataclass_transform()
|
|
17
|
-
class Event:
|
|
18
|
-
event_spec: EventSpec = None
|
|
19
|
-
event_name: str = None
|
|
20
|
-
event_args: list = None
|
|
21
|
-
|
|
22
|
-
def __init_subclass__(cls):
|
|
23
|
-
if not is_dataclass(cls):
|
|
24
|
-
dataclass_cls = dataclass(cls)
|
|
25
|
-
else:
|
|
26
|
-
dataclass_cls = cls
|
|
27
|
-
dataclass_cls.event_name = cls.__name__
|
|
28
|
-
args = []
|
|
29
|
-
for f in fields(dataclass_cls):
|
|
30
|
-
setattr(cls, f.name, var(f"{cls.event_name}__{f.name}"))
|
|
31
|
-
args.append(f"{cls.event_name}__{f.name}")
|
|
32
|
-
cls.event_args = args
|
|
33
|
-
|
|
34
|
-
@property
|
|
35
|
-
def _bounded_args(self):
|
|
36
|
-
# if not TEMPLATE_CONTEXT.get():
|
|
37
|
-
return {
|
|
38
|
-
f"{self.event_name}__{f.name}": getattr(self, f.name) for f in fields(self)
|
|
39
|
-
}
|
|
40
|
-
# template
|
|
41
|
-
# out = {}
|
|
42
|
-
# for f in fields(self):
|
|
43
|
-
# v = getattr(self, f.name)
|
|
44
|
-
# if isinstance(v, VariableLiteral):
|
|
45
|
-
# out[f"{self.event_name}__{f.name}"] = VariableLiteral(f"${{{v.render()}}}")
|
|
46
|
-
# else:
|
|
47
|
-
# out[f"{self.event_name}__{f.name}"] = v
|
|
48
|
-
|
|
49
|
-
# return out
|
|
50
|
-
|
|
51
|
-
@classmethod
|
|
52
|
-
def with_spec(
|
|
53
|
-
cls,
|
|
54
|
-
filter=None,
|
|
55
|
-
count: int = None,
|
|
56
|
-
from_: Expr | Literal["elsewhere"] = None,
|
|
57
|
-
in_: Expr = None,
|
|
58
|
-
debounce: TimeLiteral = None,
|
|
59
|
-
throttle: TimeLiteral = None,
|
|
60
|
-
):
|
|
61
|
-
spec = EventSpec(
|
|
62
|
-
filter=filter,
|
|
63
|
-
count=count,
|
|
64
|
-
from_=from_,
|
|
65
|
-
in_=in_,
|
|
66
|
-
debounce=debounce,
|
|
67
|
-
throttle=throttle,
|
|
68
|
-
)
|
|
69
|
-
return type(cls)(
|
|
70
|
-
cls.__name__,
|
|
71
|
-
(cls,),
|
|
72
|
-
{
|
|
73
|
-
"event_spec": spec,
|
|
74
|
-
"__qualname__": cls.__qualname__,
|
|
75
|
-
"__module__": cls.__module__,
|
|
76
|
-
},
|
|
77
|
-
)
|
|
1
|
+
from hypie.literals import var, Expr, TimeLiteral, python_to_hs
|
|
2
|
+
from typing import Literal, dataclass_transform
|
|
3
|
+
from dataclasses import fields, dataclass, is_dataclass
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass
|
|
7
|
+
class EventSpec:
|
|
8
|
+
filter: Expr = (None,)
|
|
9
|
+
count: int = (None,)
|
|
10
|
+
from_: Expr | Literal["elsewhere"] = (None,)
|
|
11
|
+
in_: Expr = (None,)
|
|
12
|
+
debounce: TimeLiteral = (None,)
|
|
13
|
+
throttle: TimeLiteral = (None,)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass_transform()
|
|
17
|
+
class Event:
|
|
18
|
+
event_spec: EventSpec = None
|
|
19
|
+
event_name: str = None
|
|
20
|
+
event_args: list = None
|
|
21
|
+
|
|
22
|
+
def __init_subclass__(cls):
|
|
23
|
+
if not is_dataclass(cls):
|
|
24
|
+
dataclass_cls = dataclass(cls)
|
|
25
|
+
else:
|
|
26
|
+
dataclass_cls = cls
|
|
27
|
+
dataclass_cls.event_name = cls.__name__
|
|
28
|
+
args = []
|
|
29
|
+
for f in fields(dataclass_cls):
|
|
30
|
+
setattr(cls, f.name, var(f"{cls.event_name}__{f.name}"))
|
|
31
|
+
args.append(f"{cls.event_name}__{f.name}")
|
|
32
|
+
cls.event_args = args
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def _bounded_args(self):
|
|
36
|
+
# if not TEMPLATE_CONTEXT.get():
|
|
37
|
+
return {
|
|
38
|
+
f"{self.event_name}__{f.name}": getattr(self, f.name) for f in fields(self)
|
|
39
|
+
}
|
|
40
|
+
# template
|
|
41
|
+
# out = {}
|
|
42
|
+
# for f in fields(self):
|
|
43
|
+
# v = getattr(self, f.name)
|
|
44
|
+
# if isinstance(v, VariableLiteral):
|
|
45
|
+
# out[f"{self.event_name}__{f.name}"] = VariableLiteral(f"${{{v.render()}}}")
|
|
46
|
+
# else:
|
|
47
|
+
# out[f"{self.event_name}__{f.name}"] = v
|
|
48
|
+
|
|
49
|
+
# return out
|
|
50
|
+
|
|
51
|
+
@classmethod
|
|
52
|
+
def with_spec(
|
|
53
|
+
cls,
|
|
54
|
+
filter=None,
|
|
55
|
+
count: int = None,
|
|
56
|
+
from_: Expr | Literal["elsewhere"] = None,
|
|
57
|
+
in_: Expr = None,
|
|
58
|
+
debounce: TimeLiteral = None,
|
|
59
|
+
throttle: TimeLiteral = None,
|
|
60
|
+
):
|
|
61
|
+
spec = EventSpec(
|
|
62
|
+
filter=filter,
|
|
63
|
+
count=count,
|
|
64
|
+
from_=from_,
|
|
65
|
+
in_=in_,
|
|
66
|
+
debounce=debounce,
|
|
67
|
+
throttle=throttle,
|
|
68
|
+
)
|
|
69
|
+
return type(cls)(
|
|
70
|
+
cls.__name__,
|
|
71
|
+
(cls,),
|
|
72
|
+
{
|
|
73
|
+
"event_spec": spec,
|
|
74
|
+
"__qualname__": cls.__qualname__,
|
|
75
|
+
"__module__": cls.__module__,
|
|
76
|
+
},
|
|
77
|
+
)
|