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 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.1
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): return
70
- return '''
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>'''
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,,
@@ -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,,