tinyssg 1.0.1__py3-none-any.whl → 1.2.0__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.
- tinyssg/__init__.py +96 -4
- {tinyssg-1.0.1.dist-info → tinyssg-1.2.0.dist-info}/METADATA +100 -16
- tinyssg-1.2.0.dist-info/RECORD +6 -0
- tinyssg-1.0.1.dist-info/RECORD +0 -6
- {tinyssg-1.0.1.dist-info → tinyssg-1.2.0.dist-info}/WHEEL +0 -0
- {tinyssg-1.0.1.dist-info → tinyssg-1.2.0.dist-info}/top_level.txt +0 -0
tinyssg/__init__.py
CHANGED
@@ -7,6 +7,7 @@ import re
|
|
7
7
|
import shutil
|
8
8
|
import subprocess
|
9
9
|
import sys
|
10
|
+
import textwrap
|
10
11
|
import time
|
11
12
|
import webbrowser
|
12
13
|
from http.server import HTTPServer, SimpleHTTPRequestHandler
|
@@ -40,6 +41,12 @@ class TinySSGPage:
|
|
40
41
|
"""
|
41
42
|
raise TinySSGException(f"The Page class corresponding to {self.__class__.__name__} does not appear to be implemented correctly.")
|
42
43
|
|
44
|
+
def indent(self, src: str, indent: int = 0) -> str:
|
45
|
+
"""
|
46
|
+
Indentation process
|
47
|
+
"""
|
48
|
+
return TinySSGUtility.set_indent(src, indent)
|
49
|
+
|
43
50
|
|
44
51
|
class TinySSGException(Exception):
|
45
52
|
"""
|
@@ -62,6 +69,95 @@ class TinySSGUtility:
|
|
62
69
|
result = re.sub(start_delimiter + re.escape(key) + end_delimiter, str(value), result)
|
63
70
|
return result
|
64
71
|
|
72
|
+
@classmethod
|
73
|
+
def set_indent(cls, src: str, indent: int = 0) -> str:
|
74
|
+
"""
|
75
|
+
Set the indent level of the text
|
76
|
+
"""
|
77
|
+
return textwrap.indent(textwrap.dedent(src).strip(), (' ' * indent)) + '\n'
|
78
|
+
|
79
|
+
@classmethod
|
80
|
+
def merge_dict(cls, base: dict, add: dict, overwrite: bool = True, extend: bool = True, reverse: bool = False) -> dict:
|
81
|
+
"""
|
82
|
+
Merge dictionaries
|
83
|
+
"""
|
84
|
+
if not isinstance(base, dict) or not isinstance(add, dict):
|
85
|
+
raise ValueError('Both base and add must be dictionary type.')
|
86
|
+
|
87
|
+
result = base.copy()
|
88
|
+
|
89
|
+
for key, value in add.items():
|
90
|
+
TinySSGUtility.merge_dict_value(result, key, value, overwrite, extend, reverse)
|
91
|
+
|
92
|
+
return result
|
93
|
+
|
94
|
+
@classmethod
|
95
|
+
def extend_list(cls, base: any, add: any) -> list:
|
96
|
+
"""
|
97
|
+
List Expansion
|
98
|
+
"""
|
99
|
+
if base is None:
|
100
|
+
return add if isinstance(add, list) else [add]
|
101
|
+
|
102
|
+
result = base.copy() if isinstance(base, list) else [base]
|
103
|
+
|
104
|
+
if isinstance(add, list):
|
105
|
+
result.extend(add)
|
106
|
+
else:
|
107
|
+
result.append(add)
|
108
|
+
|
109
|
+
return result
|
110
|
+
|
111
|
+
@classmethod
|
112
|
+
def merge_dict_value(cls, base: any, key: str, value: any, overwrite: bool = True, extend: bool = True, reverse: bool = False) -> None:
|
113
|
+
"""
|
114
|
+
Merge dictionary values (type-based merging process)
|
115
|
+
"""
|
116
|
+
if not isinstance(base, dict):
|
117
|
+
raise ValueError('Base must be dictionary type.')
|
118
|
+
|
119
|
+
if key not in base:
|
120
|
+
base[key] = value
|
121
|
+
elif isinstance(base[key], dict) and isinstance(value, dict):
|
122
|
+
base[key] = TinySSGUtility.merge_dict(base[key], value, overwrite, extend, reverse)
|
123
|
+
elif extend and (isinstance(base[key], list) or isinstance(value, list)):
|
124
|
+
if reverse:
|
125
|
+
base[key] = TinySSGUtility.extend_list(value, base[key])
|
126
|
+
else:
|
127
|
+
base[key] = TinySSGUtility.extend_list(base[key], value)
|
128
|
+
elif overwrite:
|
129
|
+
base[key] = value
|
130
|
+
|
131
|
+
@classmethod
|
132
|
+
def filter_json_serializable(cls, data):
|
133
|
+
"""
|
134
|
+
Filter only JSON-able objects
|
135
|
+
"""
|
136
|
+
if isinstance(data, (dict, list, str, int, float, bool, type(None))):
|
137
|
+
if isinstance(data, dict):
|
138
|
+
return {k: TinySSGUtility.filter_json_serializable(v) for k, v in data.items()}
|
139
|
+
elif isinstance(data, list):
|
140
|
+
return [TinySSGUtility.filter_json_serializable(v) for v in data]
|
141
|
+
else:
|
142
|
+
return data
|
143
|
+
return None
|
144
|
+
|
145
|
+
@classmethod
|
146
|
+
def get_serialize_json(cls, data: dict, jsonindent: any = 4) -> str:
|
147
|
+
"""
|
148
|
+
Only JSON-able objects from the dictionary are converted to JSON.
|
149
|
+
"""
|
150
|
+
if not isinstance(data, dict):
|
151
|
+
raise ValueError('The specified variable is not a dictionary type.')
|
152
|
+
return json.dumps(TinySSGUtility.filter_json_serializable(data), indent=jsonindent)
|
153
|
+
|
154
|
+
@classmethod
|
155
|
+
def exclude_double_underscore(cls, data: dict) -> dict:
|
156
|
+
"""
|
157
|
+
Exclude keys beginning with __
|
158
|
+
"""
|
159
|
+
return {k: v for k, v in data.items() if not k.startswith('__')}
|
160
|
+
|
65
161
|
@classmethod
|
66
162
|
def get_fullpath(cls, args: dict, pathkey: str = '') -> str:
|
67
163
|
"""
|
@@ -278,15 +374,11 @@ class TinySSGGenerator:
|
|
278
374
|
result = {}
|
279
375
|
|
280
376
|
for key, value in route.items():
|
281
|
-
|
282
377
|
if isinstance(value, dict):
|
283
378
|
current_path = f"{dict_path}/{key}"
|
284
379
|
result[key] = cls.traverse_route(value, current_path)
|
285
380
|
else:
|
286
381
|
page = value()
|
287
|
-
page.name = key
|
288
|
-
if not isinstance(page.name, str) or len(page.name) == 0:
|
289
|
-
raise TinySSGException('The name must be a non-empty string.')
|
290
382
|
result[key] = cls.create_content(page)
|
291
383
|
|
292
384
|
return result
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: tinyssg
|
3
|
-
Version: 1.0
|
3
|
+
Version: 1.2.0
|
4
4
|
Summary: A simple static site generator
|
5
5
|
Author: Uniras
|
6
6
|
Author-email: tkappeng@gmail.com
|
@@ -50,7 +50,7 @@ Configure the directory as follows.The directory name can be changed with an opt
|
|
50
50
|
|-- static Place static files that are not subject to SSG (css, images, etc.)
|
51
51
|
|-- dist This is the directory where SSG results will be output.The contents of this directory can be published as a web site by placing it on a web server.
|
52
52
|
|-- static The static directory is copied to this directory.
|
53
|
-
|
53
|
+
```
|
54
54
|
|
55
55
|
### Creating pages
|
56
56
|
|
@@ -66,19 +66,20 @@ class IndexPage(TinySSGPage):.
|
|
66
66
|
'content': 'Hello, World!'
|
67
67
|
}
|
68
68
|
|
69
|
-
def template(self):
|
70
|
-
return
|
71
|
-
<!DOCTYPE html>
|
72
|
-
<html>
|
73
|
-
<head>
|
74
|
-
|
75
|
-
|
76
|
-
</head>
|
77
|
-
<body>
|
78
|
-
|
79
|
-
|
80
|
-
</body>
|
81
|
-
</html>
|
69
|
+
def template(self):
|
70
|
+
return self.indent("""
|
71
|
+
<!DOCTYPE html>
|
72
|
+
<html>
|
73
|
+
<head>
|
74
|
+
<meta charset="utf-8" />
|
75
|
+
<title>{{ title }}</title>
|
76
|
+
</head>
|
77
|
+
<body>
|
78
|
+
<h1>{{ title }}</h1>
|
79
|
+
<p>{{ content }}</p>
|
80
|
+
</body>
|
81
|
+
</html>
|
82
|
+
""", 0)
|
82
83
|
```
|
83
84
|
|
84
85
|
Building it will generate an HTML file with the same name as the Python file.
|
@@ -99,7 +100,7 @@ If you use the markdown library here to describe the process of conversion, you
|
|
99
100
|
|
100
101
|
Each page must be defined individually, but since this is a simple Python class, if you want to apply it to multiple pages, you can create a class that defines the common parts and inherit it to easily apply it without copying any code.
|
101
102
|
|
102
|
-
### Start local server for development
|
103
|
+
### Start local server for development
|
103
104
|
|
104
105
|
```bash
|
105
106
|
python -m tinyssg dev
|
@@ -140,3 +141,86 @@ Options:
|
|
140
141
|
--noopen, -N Do not open browser when starting development server
|
141
142
|
--curdir CURDIR, -C CURDIR Specify current directory.
|
142
143
|
```
|
144
|
+
|
145
|
+
## FAQ
|
146
|
+
|
147
|
+
### **Q.** How can I use jinja2 as a template engine?
|
148
|
+
|
149
|
+
**A.** Override the `render` method to use jinja2 to render templates.
|
150
|
+
|
151
|
+
lib/jinja2_page.py
|
152
|
+
|
153
|
+
```python
|
154
|
+
from tinyssg import TinySSGPage
|
155
|
+
from jinja2 import Template
|
156
|
+
|
157
|
+
class Jinja2Page(TinySSGPage):
|
158
|
+
def render(self, src: str, data: dict) -> str:
|
159
|
+
template = Template(src)
|
160
|
+
return template.render(data)
|
161
|
+
```
|
162
|
+
|
163
|
+
pages/index.py
|
164
|
+
|
165
|
+
```python
|
166
|
+
from tinyssg import TinySSGPage
|
167
|
+
from lib.jinja2_page import Jinja2Page
|
168
|
+
|
169
|
+
class IndexPage(Jinja2Page):
|
170
|
+
def query(self):
|
171
|
+
return {
|
172
|
+
'title': 'Index', 'content': 'Hello, World!'
|
173
|
+
}
|
174
|
+
|
175
|
+
def template(self):
|
176
|
+
return self.indent("""
|
177
|
+
<!DOCTYPE html>
|
178
|
+
<html>
|
179
|
+
<head>
|
180
|
+
<meta charset="utf-8" />
|
181
|
+
<title>{{ title }}</title>
|
182
|
+
</head>
|
183
|
+
<body>
|
184
|
+
<h1>{{ title }}</h1>
|
185
|
+
<p>{{ content }}</p>
|
186
|
+
</body>
|
187
|
+
</html>
|
188
|
+
""", 0)
|
189
|
+
```
|
190
|
+
|
191
|
+
### **Q.** How do I use Markdown to write in my templates?
|
192
|
+
|
193
|
+
**A.** Override the `translate` method and use the Markdown library to convert it to HTML.
|
194
|
+
|
195
|
+
lib/markdown_page.py
|
196
|
+
|
197
|
+
```python
|
198
|
+
from tinyssg import TinySSGPage
|
199
|
+
import markdown
|
200
|
+
|
201
|
+
class MarkdownPage(TinySSGPage):
|
202
|
+
def translate(self, basestr: str) -> str:
|
203
|
+
return markdown.markdown(basestr)
|
204
|
+
```
|
205
|
+
|
206
|
+
pages/index.py
|
207
|
+
|
208
|
+
```python
|
209
|
+
from tinyssg import TinySSGPage
|
210
|
+
from lib.markdown_page import MarkdownPage
|
211
|
+
|
212
|
+
class IndexPage(MarkdownPage):
|
213
|
+
def query(self):
|
214
|
+
return {
|
215
|
+
'title': 'Index', 'content': 'Hello, World!'
|
216
|
+
}
|
217
|
+
|
218
|
+
def template(self):
|
219
|
+
return self.indent("""
|
220
|
+
# {{ title }}
|
221
|
+
|
222
|
+
{{ content }}
|
223
|
+
|
224
|
+
This is **Markdown** template.
|
225
|
+
""", 0)
|
226
|
+
```
|
@@ -0,0 +1,6 @@
|
|
1
|
+
tinyssg/__init__.py,sha256=5dn_SPH78JJStnQCxz_7YMflRHQRFusF-hJAdGWQ8X4,30677
|
2
|
+
tinyssg/__main__.py,sha256=E8PEZ-wWYae76H_iWbY1Xu9VYGb6sfsWMN-hzqTMoBc,83
|
3
|
+
tinyssg-1.2.0.dist-info/METADATA,sha256=3nFvOkOZmcjeYVja9crVQ6fhFPhxyw4CXllMyzRYHKU,8063
|
4
|
+
tinyssg-1.2.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
5
|
+
tinyssg-1.2.0.dist-info/top_level.txt,sha256=8u1XtPYCatbklb6u3NbJbFUbwA8nDKZ6cY3FScGwJDQ,8
|
6
|
+
tinyssg-1.2.0.dist-info/RECORD,,
|
tinyssg-1.0.1.dist-info/RECORD
DELETED
@@ -1,6 +0,0 @@
|
|
1
|
-
tinyssg/__init__.py,sha256=IX78iWBhjKJj1psk2wjqTh5HDUU3kTNd5licWpKY3cs,27408
|
2
|
-
tinyssg/__main__.py,sha256=E8PEZ-wWYae76H_iWbY1Xu9VYGb6sfsWMN-hzqTMoBc,83
|
3
|
-
tinyssg-1.0.1.dist-info/METADATA,sha256=XIfnaWBVwN2gqI5I0gHXc5dVqnkw0M9gsDYzRLIPUFY,6073
|
4
|
-
tinyssg-1.0.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
5
|
-
tinyssg-1.0.1.dist-info/top_level.txt,sha256=8u1XtPYCatbklb6u3NbJbFUbwA8nDKZ6cY3FScGwJDQ,8
|
6
|
-
tinyssg-1.0.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|