autumn-core 1.0.0__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.
- autumn_core-1.0.0/PKG-INFO +157 -0
- autumn_core-1.0.0/README.md +140 -0
- autumn_core-1.0.0/pyproject.toml +48 -0
- autumn_core-1.0.0/src/Autumn/Naming/__init__.py +57 -0
- autumn_core-1.0.0/src/Autumn/Naming/attribute_selector.py +19 -0
- autumn_core-1.0.0/src/Autumn/Naming/class_.py +21 -0
- autumn_core-1.0.0/src/Autumn/Naming/identifier.py +21 -0
- autumn_core-1.0.0/src/Autumn/Naming/name.py +55 -0
- autumn_core-1.0.0/src/Autumn/Page/__init__.py +2 -0
- autumn_core-1.0.0/src/Autumn/Page/page.py +84 -0
- autumn_core-1.0.0/src/Autumn/Page/page_manager.py +119 -0
- autumn_core-1.0.0/src/Autumn/Style/Styles/__init__.py +11 -0
- autumn_core-1.0.0/src/Autumn/Style/Styles/style_holder.py +56 -0
- autumn_core-1.0.0/src/Autumn/Style/__init__.py +7 -0
- autumn_core-1.0.0/src/Autumn/Style/abstract_style.py +83 -0
- autumn_core-1.0.0/src/Autumn/Style/sheet.py +32 -0
- autumn_core-1.0.0/src/Autumn/Style/style_manager.py +46 -0
- autumn_core-1.0.0/src/Autumn/Tag/Enums/Meta/__init__.py +13 -0
- autumn_core-1.0.0/src/Autumn/Tag/Enums/Meta/as_attribute.py +46 -0
- autumn_core-1.0.0/src/Autumn/Tag/Enums/Meta/link_type.py +131 -0
- autumn_core-1.0.0/src/Autumn/Tag/Enums/Meta/script_type.py +17 -0
- autumn_core-1.0.0/src/Autumn/Tag/Enums/__init__.py +12 -0
- autumn_core-1.0.0/src/Autumn/Tag/Enums/language.py +295 -0
- autumn_core-1.0.0/src/Autumn/Tag/Enums/mime_type.py +147 -0
- autumn_core-1.0.0/src/Autumn/Tag/Meta/__init__.py +24 -0
- autumn_core-1.0.0/src/Autumn/Tag/Meta/_cdn.py +32 -0
- autumn_core-1.0.0/src/Autumn/Tag/Meta/_head.py +18 -0
- autumn_core-1.0.0/src/Autumn/Tag/Meta/_link.py +46 -0
- autumn_core-1.0.0/src/Autumn/Tag/Meta/_meta.py +28 -0
- autumn_core-1.0.0/src/Autumn/Tag/Meta/_script.py +26 -0
- autumn_core-1.0.0/src/Autumn/Tag/Meta/_script_unavailable.py +13 -0
- autumn_core-1.0.0/src/Autumn/Tag/Meta/_title.py +14 -0
- autumn_core-1.0.0/src/Autumn/Tag/__init__.py +5 -0
- autumn_core-1.0.0/src/Autumn/Tag/abstract_tag.py +313 -0
- autumn_core-1.0.0/src/Autumn/Tag/comment.py +27 -0
- autumn_core-1.0.0/src/Autumn/Tag/tag_manager.py +98 -0
- autumn_core-1.0.0/src/Autumn/__init__.py +30 -0
- autumn_core-1.0.0/src/Autumn/abstract_base.py +123 -0
- autumn_core-1.0.0/src/Autumn/base.py +338 -0
- autumn_core-1.0.0/src/Autumn/exceptions.py +23 -0
- autumn_core-1.0.0/src/Autumn/py.typed +0 -0
- autumn_core-1.0.0/src/Autumn/typing/__init__.py +0 -0
- autumn_core-1.0.0/src/Autumn/typing/types.py +8 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: autumn-core
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Autumn - Build CSS and HTML right in your backend
|
|
5
|
+
Author-email: Nerina0Coder <github.py.coder@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Classifier: Environment :: Web Environment
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
12
|
+
Requires-Python: ~=3.12
|
|
13
|
+
Requires-Dist: coloraide~=8.12.1
|
|
14
|
+
Requires-Dist: markupsafe~=3.0.3
|
|
15
|
+
Requires-Dist: werkzeug~=3.1.8
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
18
|
+
# Autumn Framework - Build front-end in the back-end
|
|
19
|
+
### What are we?
|
|
20
|
+
Autumn framework, a framework built to make front-end work as close as possible to back-end work.
|
|
21
|
+
Our goal is not to provide a server or back-end, but a tool that makes front-end look like back-end.
|
|
22
|
+
By making an ORM(Object Relational Mapping), we add an object-oriented way to write HTML documents and CSS styles
|
|
23
|
+
directly from Python.
|
|
24
|
+
### Why us?
|
|
25
|
+
You are not limited to us, there are Jinja2 and so many other libraries.
|
|
26
|
+
What makes us unique is making you to look at a Python class rather than a div tag.
|
|
27
|
+
|
|
28
|
+
Tests proven Autumn's speed is less than one millisecond,
|
|
29
|
+
and removing the prettifier actually makes it even faster(tho
|
|
30
|
+
not visibly faster in some cases).
|
|
31
|
+
### How?
|
|
32
|
+
The process is straight.
|
|
33
|
+
But there are important things to consider:
|
|
34
|
+
- Thread safety:
|
|
35
|
+
While we try our best to promise thread safety,
|
|
36
|
+
such as features like before_build, we cannot
|
|
37
|
+
lock every read/write. Your elements are up to you,
|
|
38
|
+
So, for reads/writes, it's best to access self._lock.
|
|
39
|
+
- Dynamicity:
|
|
40
|
+
When elements are dynamic, their dynamic
|
|
41
|
+
must be set to True. However, if any of the tags owned
|
|
42
|
+
are dynamic, the parent will be inferred as dynamic,
|
|
43
|
+
therefore no need to set dynamic to True.
|
|
44
|
+
|
|
45
|
+
here is an example with HTML, another with CSS.
|
|
46
|
+
#### HTML
|
|
47
|
+
Instead of writing:
|
|
48
|
+
```html
|
|
49
|
+
<div id="hello-world">Hello World!</div>
|
|
50
|
+
```
|
|
51
|
+
You write:
|
|
52
|
+
```python
|
|
53
|
+
from Autumn import new
|
|
54
|
+
|
|
55
|
+
Base = new()
|
|
56
|
+
|
|
57
|
+
tag = Base.tag
|
|
58
|
+
|
|
59
|
+
class MyDiv(tag.Tag):
|
|
60
|
+
def __init__(self):
|
|
61
|
+
self.name = "div"
|
|
62
|
+
self.closable = True
|
|
63
|
+
self.identifier = "hello-world"
|
|
64
|
+
self.tags = ["Hello World!"]
|
|
65
|
+
# How we recommend it.
|
|
66
|
+
# However, you can place spaces, tabs, and newlines, but be aware that they will be
|
|
67
|
+
# Replaced with -.
|
|
68
|
+
|
|
69
|
+
super().__init__()
|
|
70
|
+
```
|
|
71
|
+
While this seems like writing too much, it's core benefit will be seen when writing **dynamic** tags or when you don't know about HTML too much.
|
|
72
|
+
Let's see an example for a dynamic tag.
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
import time
|
|
76
|
+
from Autumn import new
|
|
77
|
+
|
|
78
|
+
Base = new()
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class MyText(Base.tag.Tag):
|
|
82
|
+
def __init__(self, classes: list[str]):
|
|
83
|
+
self.name = "p"
|
|
84
|
+
self.closable = True
|
|
85
|
+
self.classes = classes
|
|
86
|
+
self.dynamic = True
|
|
87
|
+
|
|
88
|
+
super().__init__()
|
|
89
|
+
|
|
90
|
+
def before_build(self, **_kwargs): # Recommended.
|
|
91
|
+
self.tags.append(time.time())
|
|
92
|
+
```
|
|
93
|
+
This will produce a well behaving Paragraph with dynamic elements.
|
|
94
|
+
#### CSS
|
|
95
|
+
Well, this is straight forward.
|
|
96
|
+
```css
|
|
97
|
+
div.any-div#my-div-used-for-footer {color: #000000;}
|
|
98
|
+
```
|
|
99
|
+
will simply turn into:
|
|
100
|
+
```python
|
|
101
|
+
from typing import Any
|
|
102
|
+
from Autumn import new
|
|
103
|
+
|
|
104
|
+
Base = new()
|
|
105
|
+
|
|
106
|
+
class MyStyle(Base.style.Style):
|
|
107
|
+
|
|
108
|
+
def __init__(self):
|
|
109
|
+
self.name = Base.name.Name("div", Base.name.Identifier("my-div-used-for-footer"),
|
|
110
|
+
[Base.name.Class("any-div")])
|
|
111
|
+
self.styles = [
|
|
112
|
+
"color: #000000;"
|
|
113
|
+
]
|
|
114
|
+
|
|
115
|
+
super().__init__()
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
This will also become valuable when writing **dynamic** CSS or when you simply don't know CSS.
|
|
119
|
+
Let's see an example for that too.
|
|
120
|
+
```python
|
|
121
|
+
from typing import Any
|
|
122
|
+
from Autumn import new
|
|
123
|
+
|
|
124
|
+
Base = new()
|
|
125
|
+
|
|
126
|
+
class MyStyle(Base.style.Style):
|
|
127
|
+
|
|
128
|
+
def __init__(self):
|
|
129
|
+
self.name = Base.name.Name("div", Base.name.Identifier("my-div-used-for-footer"),
|
|
130
|
+
[Base.name.Class("any-div")])
|
|
131
|
+
self.styles = [
|
|
132
|
+
"color: #000000;"
|
|
133
|
+
]
|
|
134
|
+
|
|
135
|
+
self.dynamic = True
|
|
136
|
+
|
|
137
|
+
super().__init__()
|
|
138
|
+
|
|
139
|
+
def before_build(self, **kwargs):
|
|
140
|
+
if "style" in kwargs:
|
|
141
|
+
self.styles.append(kwargs["style"])
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
#### A note on Thread safety
|
|
146
|
+
Thread safety is one of the most important parts to remember,
|
|
147
|
+
every public and private method is thread-safe if it inherits from AbstractBase.
|
|
148
|
+
Well, excluding __getattribute__, __setattr__, every public/private method is thread-safe,
|
|
149
|
+
unless it's a protected method. We do not lock protected methods, as they are (mostly, by convenience)
|
|
150
|
+
used by the public/private methods themselves.
|
|
151
|
+
|
|
152
|
+
### Our future goals
|
|
153
|
+
Our future goals (currently) can be listed as:
|
|
154
|
+
1. Out of the box experience.
|
|
155
|
+
2. A stable and mature ecosystem.
|
|
156
|
+
3. Complete support for HTML/CSS.
|
|
157
|
+
4. Support for JavaScript, etc...
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Autumn Framework - Build front-end in the back-end
|
|
2
|
+
### What are we?
|
|
3
|
+
Autumn framework, a framework built to make front-end work as close as possible to back-end work.
|
|
4
|
+
Our goal is not to provide a server or back-end, but a tool that makes front-end look like back-end.
|
|
5
|
+
By making an ORM(Object Relational Mapping), we add an object-oriented way to write HTML documents and CSS styles
|
|
6
|
+
directly from Python.
|
|
7
|
+
### Why us?
|
|
8
|
+
You are not limited to us, there are Jinja2 and so many other libraries.
|
|
9
|
+
What makes us unique is making you to look at a Python class rather than a div tag.
|
|
10
|
+
|
|
11
|
+
Tests proven Autumn's speed is less than one millisecond,
|
|
12
|
+
and removing the prettifier actually makes it even faster(tho
|
|
13
|
+
not visibly faster in some cases).
|
|
14
|
+
### How?
|
|
15
|
+
The process is straight.
|
|
16
|
+
But there are important things to consider:
|
|
17
|
+
- Thread safety:
|
|
18
|
+
While we try our best to promise thread safety,
|
|
19
|
+
such as features like before_build, we cannot
|
|
20
|
+
lock every read/write. Your elements are up to you,
|
|
21
|
+
So, for reads/writes, it's best to access self._lock.
|
|
22
|
+
- Dynamicity:
|
|
23
|
+
When elements are dynamic, their dynamic
|
|
24
|
+
must be set to True. However, if any of the tags owned
|
|
25
|
+
are dynamic, the parent will be inferred as dynamic,
|
|
26
|
+
therefore no need to set dynamic to True.
|
|
27
|
+
|
|
28
|
+
here is an example with HTML, another with CSS.
|
|
29
|
+
#### HTML
|
|
30
|
+
Instead of writing:
|
|
31
|
+
```html
|
|
32
|
+
<div id="hello-world">Hello World!</div>
|
|
33
|
+
```
|
|
34
|
+
You write:
|
|
35
|
+
```python
|
|
36
|
+
from Autumn import new
|
|
37
|
+
|
|
38
|
+
Base = new()
|
|
39
|
+
|
|
40
|
+
tag = Base.tag
|
|
41
|
+
|
|
42
|
+
class MyDiv(tag.Tag):
|
|
43
|
+
def __init__(self):
|
|
44
|
+
self.name = "div"
|
|
45
|
+
self.closable = True
|
|
46
|
+
self.identifier = "hello-world"
|
|
47
|
+
self.tags = ["Hello World!"]
|
|
48
|
+
# How we recommend it.
|
|
49
|
+
# However, you can place spaces, tabs, and newlines, but be aware that they will be
|
|
50
|
+
# Replaced with -.
|
|
51
|
+
|
|
52
|
+
super().__init__()
|
|
53
|
+
```
|
|
54
|
+
While this seems like writing too much, it's core benefit will be seen when writing **dynamic** tags or when you don't know about HTML too much.
|
|
55
|
+
Let's see an example for a dynamic tag.
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
import time
|
|
59
|
+
from Autumn import new
|
|
60
|
+
|
|
61
|
+
Base = new()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class MyText(Base.tag.Tag):
|
|
65
|
+
def __init__(self, classes: list[str]):
|
|
66
|
+
self.name = "p"
|
|
67
|
+
self.closable = True
|
|
68
|
+
self.classes = classes
|
|
69
|
+
self.dynamic = True
|
|
70
|
+
|
|
71
|
+
super().__init__()
|
|
72
|
+
|
|
73
|
+
def before_build(self, **_kwargs): # Recommended.
|
|
74
|
+
self.tags.append(time.time())
|
|
75
|
+
```
|
|
76
|
+
This will produce a well behaving Paragraph with dynamic elements.
|
|
77
|
+
#### CSS
|
|
78
|
+
Well, this is straight forward.
|
|
79
|
+
```css
|
|
80
|
+
div.any-div#my-div-used-for-footer {color: #000000;}
|
|
81
|
+
```
|
|
82
|
+
will simply turn into:
|
|
83
|
+
```python
|
|
84
|
+
from typing import Any
|
|
85
|
+
from Autumn import new
|
|
86
|
+
|
|
87
|
+
Base = new()
|
|
88
|
+
|
|
89
|
+
class MyStyle(Base.style.Style):
|
|
90
|
+
|
|
91
|
+
def __init__(self):
|
|
92
|
+
self.name = Base.name.Name("div", Base.name.Identifier("my-div-used-for-footer"),
|
|
93
|
+
[Base.name.Class("any-div")])
|
|
94
|
+
self.styles = [
|
|
95
|
+
"color: #000000;"
|
|
96
|
+
]
|
|
97
|
+
|
|
98
|
+
super().__init__()
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
This will also become valuable when writing **dynamic** CSS or when you simply don't know CSS.
|
|
102
|
+
Let's see an example for that too.
|
|
103
|
+
```python
|
|
104
|
+
from typing import Any
|
|
105
|
+
from Autumn import new
|
|
106
|
+
|
|
107
|
+
Base = new()
|
|
108
|
+
|
|
109
|
+
class MyStyle(Base.style.Style):
|
|
110
|
+
|
|
111
|
+
def __init__(self):
|
|
112
|
+
self.name = Base.name.Name("div", Base.name.Identifier("my-div-used-for-footer"),
|
|
113
|
+
[Base.name.Class("any-div")])
|
|
114
|
+
self.styles = [
|
|
115
|
+
"color: #000000;"
|
|
116
|
+
]
|
|
117
|
+
|
|
118
|
+
self.dynamic = True
|
|
119
|
+
|
|
120
|
+
super().__init__()
|
|
121
|
+
|
|
122
|
+
def before_build(self, **kwargs):
|
|
123
|
+
if "style" in kwargs:
|
|
124
|
+
self.styles.append(kwargs["style"])
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
#### A note on Thread safety
|
|
129
|
+
Thread safety is one of the most important parts to remember,
|
|
130
|
+
every public and private method is thread-safe if it inherits from AbstractBase.
|
|
131
|
+
Well, excluding __getattribute__, __setattr__, every public/private method is thread-safe,
|
|
132
|
+
unless it's a protected method. We do not lock protected methods, as they are (mostly, by convenience)
|
|
133
|
+
used by the public/private methods themselves.
|
|
134
|
+
|
|
135
|
+
### Our future goals
|
|
136
|
+
Our future goals (currently) can be listed as:
|
|
137
|
+
1. Out of the box experience.
|
|
138
|
+
2. A stable and mature ecosystem.
|
|
139
|
+
3. Complete support for HTML/CSS.
|
|
140
|
+
4. Support for JavaScript, etc...
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
|
|
3
|
+
requires = ["hatchling >= 1.26"]
|
|
4
|
+
build-backend = "hatchling.build"
|
|
5
|
+
|
|
6
|
+
[tool.hatch.build.targets.wheel]
|
|
7
|
+
|
|
8
|
+
packages = ["src/Autumn/"]
|
|
9
|
+
|
|
10
|
+
[tool.hatch.build.targets.sdist]
|
|
11
|
+
|
|
12
|
+
include = [
|
|
13
|
+
"src/Autumn/**/*.py",
|
|
14
|
+
"src/Autumn/**/.pyi",
|
|
15
|
+
"src/Autumn/py.typed",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[project]
|
|
19
|
+
|
|
20
|
+
name = "autumn-core"
|
|
21
|
+
version = "1.0.0"
|
|
22
|
+
description = "Autumn - Build CSS and HTML right in your backend"
|
|
23
|
+
requires-python = "~=3.12"
|
|
24
|
+
license = "MIT"
|
|
25
|
+
readme = "README.md"
|
|
26
|
+
authors = [
|
|
27
|
+
{name = "Nerina0Coder", email = "github.py.coder@gmail.com"},
|
|
28
|
+
]
|
|
29
|
+
dependencies = [
|
|
30
|
+
"markupsafe ~= 3.0.3",
|
|
31
|
+
"coloraide ~= 8.12.1",
|
|
32
|
+
"werkzeug ~= 3.1.8"
|
|
33
|
+
]
|
|
34
|
+
classifiers = [
|
|
35
|
+
"Programming Language :: Python :: 3",
|
|
36
|
+
"Operating System :: OS Independent",
|
|
37
|
+
"License :: OSI Approved :: MIT License",
|
|
38
|
+
"Topic :: Software Development :: Libraries :: Application Frameworks",
|
|
39
|
+
"Environment :: Web Environment"
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[dependency-groups]
|
|
43
|
+
|
|
44
|
+
docs = [
|
|
45
|
+
"sphinx>=7.0",
|
|
46
|
+
"myst-parser>=2.0",
|
|
47
|
+
"furo>=2.0",
|
|
48
|
+
]
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from .class_ import Class
|
|
2
|
+
from .identifier import Identifier
|
|
3
|
+
from .attribute_selector import AttributeSelector
|
|
4
|
+
from .name import Name
|
|
5
|
+
|
|
6
|
+
# --- Special Function ---
|
|
7
|
+
|
|
8
|
+
def MakeSelector(state = None,
|
|
9
|
+
identifier = None,
|
|
10
|
+
cls = None,
|
|
11
|
+
selector = None,):
|
|
12
|
+
out = []
|
|
13
|
+
|
|
14
|
+
if state is not None:
|
|
15
|
+
if not isinstance(state, list):
|
|
16
|
+
state = [state]
|
|
17
|
+
else:
|
|
18
|
+
state = []
|
|
19
|
+
|
|
20
|
+
if identifier is not None:
|
|
21
|
+
if not isinstance(identifier, list):
|
|
22
|
+
identifier = [identifier]
|
|
23
|
+
else:
|
|
24
|
+
identifier = [identifier]
|
|
25
|
+
|
|
26
|
+
if cls is not None:
|
|
27
|
+
if not isinstance(cls, list):
|
|
28
|
+
cls = [cls]
|
|
29
|
+
else:
|
|
30
|
+
cls = []
|
|
31
|
+
|
|
32
|
+
if selector is not None:
|
|
33
|
+
if not isinstance(selector, list):
|
|
34
|
+
selector = [selector]
|
|
35
|
+
else:
|
|
36
|
+
selector = []
|
|
37
|
+
|
|
38
|
+
mixed = [*state, *identifier, *cls, *selector]
|
|
39
|
+
|
|
40
|
+
for i in mixed:
|
|
41
|
+
adding = str(i) if not hasattr(i, "build") else i.build()
|
|
42
|
+
out.append(f"{adding if len(out) == 1 else f",{adding}"}")
|
|
43
|
+
|
|
44
|
+
return "".join(out)
|
|
45
|
+
|
|
46
|
+
# API
|
|
47
|
+
|
|
48
|
+
class Naming:
|
|
49
|
+
"""
|
|
50
|
+
Naming - Contains classes used for naming.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
MakeSelector = MakeSelector
|
|
54
|
+
Name = Name
|
|
55
|
+
Identifier = Identifier
|
|
56
|
+
Class = Class
|
|
57
|
+
AttributeSelector = AttributeSelector
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import threading
|
|
2
|
+
|
|
3
|
+
from markupsafe import escape
|
|
4
|
+
|
|
5
|
+
from Autumn.abstract_base import AbstractBase
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AttributeSelector(AbstractBase):
|
|
9
|
+
|
|
10
|
+
def __init__(self, attr, value):
|
|
11
|
+
self._attr = attr
|
|
12
|
+
self._value = value
|
|
13
|
+
|
|
14
|
+
self._lock = threading.Lock()
|
|
15
|
+
|
|
16
|
+
def build(self, **kwargs) -> str:
|
|
17
|
+
with self._lock:
|
|
18
|
+
self.before_build(**kwargs)
|
|
19
|
+
return f'[{escape(self._attr)}="{escape(self._attr)}"]'
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import threading
|
|
3
|
+
|
|
4
|
+
from markupsafe import escape
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclasses.dataclass
|
|
8
|
+
class Class:
|
|
9
|
+
"""
|
|
10
|
+
The class used to hold CSS/HTML class names.
|
|
11
|
+
"""
|
|
12
|
+
name: str
|
|
13
|
+
|
|
14
|
+
_lock = threading.Lock()
|
|
15
|
+
|
|
16
|
+
def __post_init__(self):
|
|
17
|
+
self.name = escape(self.name)
|
|
18
|
+
|
|
19
|
+
def __str__(self):
|
|
20
|
+
with self._lock:
|
|
21
|
+
return self.name.replace(" ", "-").replace("\n", "-").replace("\t", "-")
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import threading
|
|
3
|
+
|
|
4
|
+
from markupsafe import escape
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclasses.dataclass
|
|
8
|
+
class Identifier:
|
|
9
|
+
"""
|
|
10
|
+
The class used to hold CSS/HTML identifiers.
|
|
11
|
+
"""
|
|
12
|
+
name: str
|
|
13
|
+
|
|
14
|
+
_lock = threading.Lock()
|
|
15
|
+
|
|
16
|
+
def __post_init__(self):
|
|
17
|
+
self.name = escape(self.name)
|
|
18
|
+
|
|
19
|
+
def __str__(self):
|
|
20
|
+
with self._lock:
|
|
21
|
+
return self.name.replace(" ", "-").replace("\n", "-").replace("\t", "-")
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
import threading
|
|
3
|
+
|
|
4
|
+
from markupsafe import escape
|
|
5
|
+
|
|
6
|
+
from Autumn.abstract_base import AbstractBase
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Name(AbstractBase):
|
|
10
|
+
"""
|
|
11
|
+
Represents a CSS name.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def __init__(self,
|
|
15
|
+
name,
|
|
16
|
+
id_,
|
|
17
|
+
classes,
|
|
18
|
+
/, *,
|
|
19
|
+
attributes = None):
|
|
20
|
+
"""
|
|
21
|
+
:param attributes: CSS/JS only
|
|
22
|
+
"""
|
|
23
|
+
self.name = name
|
|
24
|
+
self.identifier = id_
|
|
25
|
+
self.classes = classes
|
|
26
|
+
|
|
27
|
+
self.attrs = attributes
|
|
28
|
+
|
|
29
|
+
self._lock = threading.Lock()
|
|
30
|
+
|
|
31
|
+
self._cache = []
|
|
32
|
+
|
|
33
|
+
def build(self, cache_if_possible = True, **kwargs):
|
|
34
|
+
|
|
35
|
+
with self._lock:
|
|
36
|
+
out = [self.name]
|
|
37
|
+
|
|
38
|
+
self.before_build(**kwargs)
|
|
39
|
+
|
|
40
|
+
if self.identifier:
|
|
41
|
+
out.append(f"#{self.identifier}")
|
|
42
|
+
|
|
43
|
+
if self.classes:
|
|
44
|
+
for cls in self.classes:
|
|
45
|
+
out.append(f".{cls}")
|
|
46
|
+
|
|
47
|
+
if self.attrs:
|
|
48
|
+
for k, v in self.attrs.items():
|
|
49
|
+
out.append(f'[{escape(k)}="{escape(v)}"]')
|
|
50
|
+
|
|
51
|
+
return ''.join(out)
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def from_tag(tag):
|
|
55
|
+
return Name(tag.name, tag.identifier, copy.deepcopy(tag.classes))
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import html
|
|
2
|
+
import threading
|
|
3
|
+
from typing import Literal, Any
|
|
4
|
+
|
|
5
|
+
from Autumn.Tag.Enums import Language
|
|
6
|
+
from Autumn.Tag.Meta import Title
|
|
7
|
+
from Autumn.Tag.abstract_tag import AbstractTag
|
|
8
|
+
from Autumn.Tag.Meta import Cdn
|
|
9
|
+
from Autumn.Tag.Meta import Head
|
|
10
|
+
from Autumn.abstract_base import AbstractBase
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Page(AbstractBase):
|
|
14
|
+
"""
|
|
15
|
+
A page class representing an HTML document.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, name: str, /, *, direction: Literal["ltr", "rtl", "auto"] = "auto", language: Language | str = "en"):
|
|
19
|
+
"""
|
|
20
|
+
Initializes a new page object.
|
|
21
|
+
:param name: The name of the page.
|
|
22
|
+
:param direction: The direction of the page.
|
|
23
|
+
:param language: The language of the page.
|
|
24
|
+
"""
|
|
25
|
+
self.name = name
|
|
26
|
+
self.direction = direction
|
|
27
|
+
self.language = language.value if isinstance(language, Language) else language
|
|
28
|
+
self._tags: list[AbstractTag] = []
|
|
29
|
+
self._requirements: list[Cdn] = []
|
|
30
|
+
|
|
31
|
+
self._cache: list[str] = []
|
|
32
|
+
|
|
33
|
+
self._lock = threading.Lock()
|
|
34
|
+
|
|
35
|
+
def require(self, cdn: list[Cdn] | Cdn, /) -> "Page":
|
|
36
|
+
"""
|
|
37
|
+
Requires CDN URL(s) for this specific page.
|
|
38
|
+
:param cdn: The CDN URL(s).
|
|
39
|
+
:return: This instance for chaining operations.
|
|
40
|
+
"""
|
|
41
|
+
self._requirements.extend(cdn if isinstance(cdn, list) else [cdn])
|
|
42
|
+
return self
|
|
43
|
+
|
|
44
|
+
def tag(self, tag: list[AbstractTag] | AbstractTag, /) -> "Page":
|
|
45
|
+
"""
|
|
46
|
+
Adds tag(s) to this page, which is often only a head and a body.
|
|
47
|
+
:param tag: The tag(s) to add.
|
|
48
|
+
:return: This instance for chaining operations.
|
|
49
|
+
"""
|
|
50
|
+
self._tags.extend(tag if isinstance(tag, list) else [tag])
|
|
51
|
+
return self
|
|
52
|
+
|
|
53
|
+
def build(self, **build_kwargs: Any) -> str:
|
|
54
|
+
dynamic = any(tag.dynamic for tag in self._tags)
|
|
55
|
+
|
|
56
|
+
if not dynamic and self._cache:
|
|
57
|
+
return self._cache[-1]
|
|
58
|
+
|
|
59
|
+
self.before_build(**build_kwargs)
|
|
60
|
+
|
|
61
|
+
head: Head | None = next(filter(lambda tag: isinstance(tag, Head), self._tags), None) # type: ignore
|
|
62
|
+
if head is None:
|
|
63
|
+
self._tags.insert(0, Head(Title(self.name), *self._requirements))
|
|
64
|
+
else:
|
|
65
|
+
head.tags.extend([Title(self.name), *self._requirements])
|
|
66
|
+
|
|
67
|
+
out = ("<!DOCTYPE html>" +
|
|
68
|
+
f'<html lang="{html.escape(self.language)}" dir="{html.escape(self.direction)}">' +
|
|
69
|
+
"".join(tag.build(dynamic, **build_kwargs) for tag in self._tags) +
|
|
70
|
+
"</html>")
|
|
71
|
+
|
|
72
|
+
if not dynamic and not self._cache:
|
|
73
|
+
self._cache.append(out)
|
|
74
|
+
return out
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def dynamic(self) -> bool:
|
|
78
|
+
return any(tag.dynamic for tag in self._tags)
|
|
79
|
+
@property
|
|
80
|
+
def tags(self) -> list[AbstractTag]:
|
|
81
|
+
return self._tags.copy()
|
|
82
|
+
@property
|
|
83
|
+
def requirements(self) -> list[Cdn]:
|
|
84
|
+
return self._requirements.copy()
|