tinyssg 1.1.0__py3-none-any.whl → 1.2.1__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 -0
- {tinyssg-1.1.0.dist-info → tinyssg-1.2.1.dist-info}/METADATA +101 -18
- tinyssg-1.2.1.dist-info/RECORD +6 -0
- tinyssg-1.1.0.dist-info/RECORD +0 -6
- {tinyssg-1.1.0.dist-info → tinyssg-1.2.1.dist-info}/WHEEL +0 -0
- {tinyssg-1.1.0.dist-info → tinyssg-1.2.1.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
|
"""
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: tinyssg
|
3
|
-
Version: 1.1
|
3
|
+
Version: 1.2.1
|
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
|
|
@@ -60,25 +60,26 @@ Create a Python file in the `pages` directory and create a class that extends th
|
|
60
60
|
from tinyssg import TinySSGPage
|
61
61
|
|
62
62
|
class IndexPage(TinySSGPage):.
|
63
|
-
def query(self)
|
63
|
+
def query(self) -> any:
|
64
64
|
return {
|
65
65
|
'title': 'Index', 'content': 'Hello, World!
|
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) -> str:
|
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.
|
@@ -105,7 +106,7 @@ Each page must be defined individually, but since this is a simple Python class,
|
|
105
106
|
python -m tinyssg dev
|
106
107
|
```
|
107
108
|
|
108
|
-
The local server for development will be started.You can see the generated HTML by accessing
|
109
|
+
The local server for development will be started.You can see the generated HTML by accessing `http://localhost:8000`.
|
109
110
|
|
110
111
|
If you change files in the `pages`, `libs`, or `static` directories, the server will automatically restart to reflect the changes.
|
111
112
|
|
@@ -120,7 +121,6 @@ HTML files will be generated in the `dist` directory.
|
|
120
121
|
### options (excerpt)
|
121
122
|
|
122
123
|
```text
|
123
|
-
|
124
124
|
usage: python -m tinyssg [--page PAGE] [--static STATIC] [--lib LIB] [--input INPUT] [--output OUTPUT] [--port PORT] [--wait WAIT] [--nolog] [--noreloadnoreload] [--noopen] [--curdir CURDIR] [mode]
|
125
125
|
|
126
126
|
MODE:
|
@@ -140,3 +140,86 @@ Options:
|
|
140
140
|
--noopen, -N Do not open browser when starting development server
|
141
141
|
--curdir CURDIR, -C CURDIR Specify current directory.
|
142
142
|
```
|
143
|
+
|
144
|
+
## FAQ
|
145
|
+
|
146
|
+
### **Q.** How can I use jinja2 as a template engine?
|
147
|
+
|
148
|
+
**A.** Override the `render` method to use jinja2 to render templates.
|
149
|
+
|
150
|
+
lib/jinja2_page.py
|
151
|
+
|
152
|
+
```python
|
153
|
+
from tinyssg import TinySSGPage
|
154
|
+
from jinja2 import Template
|
155
|
+
|
156
|
+
class Jinja2Page(TinySSGPage):
|
157
|
+
def render(self, src: str, data: dict) -> str:
|
158
|
+
template = Template(src)
|
159
|
+
return template.render(data)
|
160
|
+
```
|
161
|
+
|
162
|
+
pages/index.py
|
163
|
+
|
164
|
+
```python
|
165
|
+
from tinyssg import TinySSGPage
|
166
|
+
from lib.jinja2_page import Jinja2Page
|
167
|
+
|
168
|
+
class IndexPage(Jinja2Page):
|
169
|
+
def query(self) -> any:
|
170
|
+
return {
|
171
|
+
'title': 'Index', 'content': 'Hello, World!'
|
172
|
+
}
|
173
|
+
|
174
|
+
def template(self) -> str:
|
175
|
+
return self.indent("""
|
176
|
+
<!DOCTYPE html>
|
177
|
+
<html>
|
178
|
+
<head>
|
179
|
+
<meta charset="utf-8" />
|
180
|
+
<title>{{ title }}</title>
|
181
|
+
</head>
|
182
|
+
<body>
|
183
|
+
<h1>{{ title }}</h1>
|
184
|
+
<p>{{ content }}</p>
|
185
|
+
</body>
|
186
|
+
</html>
|
187
|
+
""", 0)
|
188
|
+
```
|
189
|
+
|
190
|
+
### **Q.** How do I use Markdown to write in my templates?
|
191
|
+
|
192
|
+
**A.** Override the `translate` method and use the Markdown library to convert it to HTML.
|
193
|
+
|
194
|
+
lib/markdown_page.py
|
195
|
+
|
196
|
+
```python
|
197
|
+
from tinyssg import TinySSGPage
|
198
|
+
import markdown
|
199
|
+
|
200
|
+
class MarkdownPage(TinySSGPage):
|
201
|
+
def translate(self, basestr: str) -> str:
|
202
|
+
return markdown.markdown(basestr)
|
203
|
+
```
|
204
|
+
|
205
|
+
pages/index.py
|
206
|
+
|
207
|
+
```python
|
208
|
+
from tinyssg import TinySSGPage
|
209
|
+
from lib.markdown_page import MarkdownPage
|
210
|
+
|
211
|
+
class IndexPage(MarkdownPage):
|
212
|
+
def query(self) -> any:
|
213
|
+
return {
|
214
|
+
'title': 'Index', 'content': 'Hello, World!'
|
215
|
+
}
|
216
|
+
|
217
|
+
def template(self) -> str:
|
218
|
+
return self.indent("""
|
219
|
+
# {{ title }}
|
220
|
+
|
221
|
+
{{ content }}
|
222
|
+
|
223
|
+
This is **Markdown** template.
|
224
|
+
""", 0)
|
225
|
+
```
|
@@ -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.1.dist-info/METADATA,sha256=Cqzs0_ltiMxVu038tRVFsYh01HscpBmaC9FQoPjes-c,8100
|
4
|
+
tinyssg-1.2.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
5
|
+
tinyssg-1.2.1.dist-info/top_level.txt,sha256=8u1XtPYCatbklb6u3NbJbFUbwA8nDKZ6cY3FScGwJDQ,8
|
6
|
+
tinyssg-1.2.1.dist-info/RECORD,,
|
tinyssg-1.1.0.dist-info/RECORD
DELETED
@@ -1,6 +0,0 @@
|
|
1
|
-
tinyssg/__init__.py,sha256=puqhv4_E1ZG7c9NYUBhIn8ZtWRhPWcy0JspdWKmX9hU,27214
|
2
|
-
tinyssg/__main__.py,sha256=E8PEZ-wWYae76H_iWbY1Xu9VYGb6sfsWMN-hzqTMoBc,83
|
3
|
-
tinyssg-1.1.0.dist-info/METADATA,sha256=i0XNcAf4B3pS3CeA0cgnIWYnixswGHPpPm8tNbwFN_0,6072
|
4
|
-
tinyssg-1.1.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
5
|
-
tinyssg-1.1.0.dist-info/top_level.txt,sha256=8u1XtPYCatbklb6u3NbJbFUbwA8nDKZ6cY3FScGwJDQ,8
|
6
|
-
tinyssg-1.1.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|