tinyssg 0.0.8__py3-none-any.whl → 0.0.10__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
tinyssg/__main__.py CHANGED
@@ -1,724 +1,4 @@
1
- import argparse
2
- import importlib.util
3
- import inspect
4
- import json
5
- import os
6
- import re
7
- import shutil
8
- import subprocess
9
- import sys
10
- import time
11
- import webbrowser
12
- from http.server import HTTPServer, SimpleHTTPRequestHandler
13
-
14
-
15
- class TinySSGPage:
16
- """
17
- Base class for HTML page generation
18
- """
19
- def render(self, src: str, data: dict) -> str:
20
- """
21
- Template rendering process
22
- """
23
- return TinySSGUtility.render_variables(src, data)
24
-
25
- def translate(self, basestr: str) -> str:
26
- """
27
- Process to convert rendered text to HTML
28
- """
29
- return basestr
30
-
31
- def query(self) -> dict:
32
- """
33
- Data Acquisition Process
34
- """
35
- return {}
36
-
37
- def template(self) -> str:
38
- """
39
- Template string
40
- """
41
- raise TinySSGException(f"The Page class corresponding to {self.__class__.__name__} does not appear to be implemented correctly.")
42
-
43
-
44
- class TinySSGException(Exception):
45
- """
46
- TinySSG Exception Class
47
- """
48
- pass
49
-
50
-
51
- class TinySSGUtility:
52
- """
53
- TinySSG Utility Class
54
- """
55
- @classmethod
56
- def render_variables(cls, src: str, data: dict, start_delimiter: str = r'\{\{\s?', end_delimiter: str = r'\s?\}\}') -> str:
57
- """
58
- Replace variables in the template with the values in the dictionary
59
- """
60
- result = src
61
- for key, value in data.items():
62
- result = re.sub(start_delimiter + re.escape(key) + end_delimiter, str(value), result)
63
- return result
64
-
65
- @classmethod
66
- def get_fullpath(cls, args: dict, pathkey: str) -> str:
67
- """
68
- Get the full path from the relative path
69
- """
70
- if isinstance(args['curdir'], str) and len(args['curdir']) > 0:
71
- return os.path.join(args['curdir'], args[pathkey])
72
- else:
73
- return os.path.join(os.getcwd(), args[pathkey])
74
-
75
- @classmethod
76
- def clear_output(cls, output_full_path: str) -> None:
77
- """
78
- Delete the output directory
79
- """
80
- if os.path.exists(output_full_path):
81
- shutil.rmtree(output_full_path)
82
-
83
- @classmethod
84
- def clear_start(cls, args: dict) -> None:
85
- """
86
- Delete the output directory
87
- """
88
- output_full_path = cls.get_fullpath(args, 'output')
89
- cls.clear_output(output_full_path)
90
-
91
- @classmethod
92
- def log_print(cls, message: str) -> None:
93
- """
94
- Output log message (Console Execution Only)
95
- """
96
- try:
97
- from IPython import get_ipython # type: ignore
98
- env = get_ipython().__class__.__name__
99
- if env == 'ZMQInteractiveShell':
100
- return
101
- except: # noqa: E722
102
- pass
103
-
104
- print(message)
105
-
106
- @classmethod
107
- def error_print(cls, message: str) -> None:
108
- """
109
- Output error message
110
- """
111
- print(message)
112
-
113
-
114
- class TinySSGGenerator:
115
- """
116
- Generator
117
- """
118
- @classmethod
119
- def check_duplicate_name(cls, files: list, dirs: list) -> list:
120
- """
121
- Check for duplicate names in files and directories
122
- """
123
- filenames = {os.path.splitext(f)[0] for f in files}
124
- dirnames = set(dirs)
125
-
126
- conflicts = list(filenames & dirnames)
127
-
128
- return conflicts
129
-
130
- @classmethod
131
- def extract_page_classes(cls, root: str, filename: str) -> list:
132
- """
133
- Extract page classes from the specified file.
134
- """
135
- page_classes = []
136
- check_base_name = TinySSGPage.__name__
137
-
138
- if filename.endswith('.py') and filename != '__init__.py':
139
- module_name = os.path.splitext(filename)[0] # Excluding extensions
140
- module_path = os.path.join(root, filename)
141
-
142
- spec = importlib.util.spec_from_file_location(module_name, module_path)
143
- if spec and spec.loader:
144
- module = importlib.util.module_from_spec(spec)
145
- spec.loader.exec_module(module)
146
- for _, members in inspect.getmembers(module):
147
- if inspect.isclass(members) and members.__module__ == module_name:
148
- parents = [m.__name__ for m in members.__mro__]
149
- if check_base_name in parents:
150
- page_classes.append(members)
151
-
152
- return page_classes, module_name, module_path
153
-
154
- @classmethod
155
- def check_input_file(cls, relative_path: str, filename: str, input_file: str) -> bool:
156
- """
157
- Check if the input file is the same as the file being processed
158
- """
159
- convert_filename = os.path.join(relative_path, os.path.splitext(filename)[0]).replace(os.sep, '/')
160
- convert_input_file = os.path.splitext(re.sub(r'^\./', '', input_file))[0].replace(os.sep, '/')
161
-
162
- return convert_filename == convert_input_file
163
-
164
- @classmethod
165
- def search_route(cls, args: dict) -> dict:
166
- """
167
- Search for Page classes in the specified directory
168
- """
169
- static_path = args['static']
170
- input_file = args['input']
171
-
172
- try:
173
- prev_dont_write_bytecode = sys.dont_write_bytecode
174
- sys.dont_write_bytecode = True
175
-
176
- routes = {}
177
-
178
- full_pages_path = TinySSGUtility.get_fullpath(args, 'page')
179
- page_counter = 0
180
-
181
- for root, dirs, files in os.walk(full_pages_path):
182
- relative_path = os.path.relpath(root, full_pages_path)
183
-
184
- conflicts = cls.check_duplicate_name(files, dirs)
185
- if len(conflicts) > 0:
186
- raise TinySSGException(f"The following names conflict between files and directories: {', '.join(conflicts)} in {relative_path}")
187
-
188
- if relative_path == '.':
189
- relative_path = ''
190
-
191
- if relative_path == static_path:
192
- raise TinySSGException(f"Static file directory name conflict: {os.path.join(full_pages_path, relative_path)}")
193
-
194
- if relative_path.endswith('__pycache__'):
195
- continue
196
-
197
- current_dict = routes
198
-
199
- if relative_path:
200
- for part in relative_path.split(os.sep):
201
- if part not in current_dict:
202
- current_dict[part] = {}
203
- current_dict = current_dict[part]
204
-
205
- for filename in files:
206
- if len(input_file) > 0 and not cls.check_input_file(relative_path, filename, input_file):
207
- continue
208
-
209
- if relative_path == '' and filename == f"{static_path}.py":
210
- raise TinySSGException(f"Static file directory name conflict: {os.path.join(root, filename)}")
211
-
212
- page_classes, module_name, module_path = cls.extract_page_classes(root, filename)
213
- page_counter += len(page_classes)
214
- if len(page_classes) > 1:
215
- current_dict[module_name] = {c.__name__: c for c in page_classes}
216
- elif len(page_classes) == 1:
217
- current_dict[module_name] = page_classes[0]
218
- else:
219
- TinySSGUtility.log_print(f"warning: No Page class found in {module_path}")
220
-
221
- if page_counter == 0:
222
- raise TinySSGException('No Page classes found.')
223
- finally:
224
- sys.dont_write_bytecode = prev_dont_write_bytecode
225
-
226
- return routes
227
-
228
- @classmethod
229
- def create_content(cls, page: TinySSGPage) -> str:
230
- """
231
- Generate HTML content from Page class
232
- """
233
- basefetch = page.query()
234
- fetchdata, slugkey = basefetch if isinstance(basefetch, tuple) else (basefetch, None)
235
- if isinstance(fetchdata, dict):
236
- baselist = [fetchdata]
237
- slugkey = None
238
- single_page = True
239
- elif isinstance(fetchdata, list):
240
- if len(fetchdata) == 0:
241
- return {}
242
- baselist = fetchdata
243
- for i in range(len(baselist)):
244
- if not isinstance(baselist[i], dict):
245
- raise TinySSGException('The query method must return a dictionary or a list of dictionaries.')
246
- single_page = False
247
- else:
248
- raise TinySSGException('The query method must return a dictionary or a list of dictionaries.')
249
-
250
- result = {}
251
-
252
- for i in range(len(baselist)):
253
- if isinstance(slugkey, str) and slugkey in baselist[i]:
254
- key = baselist[i][slugkey]
255
- else:
256
- key = str(i + 1)
257
- pagedata = baselist[i]
258
- pagetemp = page.template()
259
- basestr = page.render(pagetemp, pagedata).strip() + '\n'
260
- htmlstr = page.translate(basestr)
261
- if isinstance(htmlstr, str) and len(htmlstr) > 0:
262
- result[key] = htmlstr
263
-
264
- return result['1'] if single_page else result
265
-
266
- @classmethod
267
- def traverse_route(cls, route: dict, dict_path: str = '') -> dict:
268
- """
269
- Traverse the route dictionary and generate HTML content
270
- """
271
- result = {}
272
-
273
- for key, value in route.items():
274
-
275
- if isinstance(value, dict):
276
- current_path = f"{dict_path}/{key}"
277
- result[key] = cls.traverse_route(value, current_path)
278
- else:
279
- page = value()
280
- page.name = key
281
- if not isinstance(page.name, str) or len(page.name) == 0:
282
- raise TinySSGException('The name must be a non-empty string.')
283
- result[key] = cls.create_content(page)
284
-
285
- return result
286
-
287
- @classmethod
288
- def generate_routes(cls, args: dict) -> dict:
289
- """
290
- Generate HTML content dictionary from Page classes
291
- """
292
- route = cls.search_route(args)
293
- return cls.traverse_route(route)
294
-
295
- @classmethod
296
- def output_file(cls, data: dict, full_path: str) -> None:
297
- """
298
- Output the HTML content dictionary to the file
299
- """
300
- for key, value in data.items():
301
- if isinstance(value, dict) and len(value) > 0:
302
- relative_path = os.path.join(full_path, key)
303
- if not os.path.exists(relative_path):
304
- os.makedirs(relative_path)
305
- cls.output_file(value, relative_path)
306
- elif isinstance(value, str):
307
- with open(os.path.join(full_path, key + '.html'), 'w', encoding='utf-8') as f:
308
- f.write(value)
309
-
310
- @classmethod
311
- def generator_start(cls, args: dict) -> None:
312
- """
313
- Generate HTML files from Page classes
314
- """
315
- input_full_path = TinySSGUtility.get_fullpath(args, 'page')
316
-
317
- if not os.path.isdir(input_full_path):
318
- raise TinySSGException(f"The specified page directory does not exist. ({input_full_path})")
319
-
320
- page_data = cls.generate_routes(args)
321
- output_full_path = TinySSGUtility.get_fullpath(args, 'output')
322
-
323
- if not os.path.exists(output_full_path):
324
- os.makedirs(output_full_path)
325
-
326
- cls.output_file(page_data, output_full_path)
327
-
328
- static_full_path = TinySSGUtility.get_fullpath(args, 'static')
329
- output_static_full_path = os.path.join(output_full_path, args['static'])
330
-
331
- if os.path.isdir(static_full_path):
332
- if not os.path.exists(output_static_full_path):
333
- os.makedirs(output_static_full_path)
334
- shutil.copytree(static_full_path, output_static_full_path, dirs_exist_ok=True)
335
-
336
-
337
- class TinySSGDebugHTTPServer(HTTPServer):
338
- """
339
- Custom HTTP server class
340
- """
341
- def __init__(self, server_address: tuple, RequestHandlerClass: any, args: dict, route: dict, reload: bool) -> None:
342
- super().__init__(server_address, RequestHandlerClass)
343
- self.args = args
344
- self.route = route
345
- self.reload = reload
346
-
347
-
348
- class TinySSGDebugHTTPHandler(SimpleHTTPRequestHandler):
349
- """
350
- Custom HTTP request handler
351
- """
352
- def __init__(self, *args, **kwargs) -> None:
353
- try:
354
- super().__init__(*args, **kwargs)
355
- except ConnectionResetError:
356
- pass
357
-
358
- def end_headers(self):
359
- self.send_header('Cache-Control', 'no-store')
360
- return super().end_headers()
361
-
362
- def log_message(self, format: str, *args: any) -> None:
363
- TinySSGDebug.print_httpd_log_message(self, self.server, format, *args)
364
-
365
- def do_GET(self) -> None:
366
- TinySSGDebug.httpd_get_handler(self, self.server)
367
-
368
-
369
- class TinySSGDebug:
370
- """
371
- Debug Server
372
- """
373
- @classmethod
374
- def watchdog_script(cls) -> str:
375
- """
376
- JavaScript code that checks for file updates from a web browser and reloads the file if there are any updates
377
- """
378
- return '''
379
- <script type="module">
380
- let __reload_check = () => {
381
- fetch('/change').then(response => response.json()).then(data => {
382
- if (data.reload) {
383
- console.log('Change detected. Reloading...');
384
- location.reload();
385
- } else {
386
- setTimeout(__reload_check, 1000);
387
- }
388
- });
389
- };
390
- setTimeout(__reload_check, 1000);
391
- </script>'''
392
-
393
- @classmethod
394
- def send_ok_response(cls, handler: TinySSGDebugHTTPHandler, content_type: str, content: str = '', add_headers: dict = {}) -> None:
395
- """
396
- Send an OK response
397
- """
398
- encoded_content = content.encode('utf-8')
399
- handler.send_response(200)
400
- handler.send_header('Content-type', content_type)
401
- handler.send_header('Content-Length', len(encoded_content))
402
- for key, value in add_headers.items():
403
- handler.send_header(key, value)
404
- handler.end_headers()
405
- handler.wfile.write(encoded_content)
406
-
407
- @classmethod
408
- def send_no_ok_response(cls, handler: TinySSGDebugHTTPHandler, status: int, content: str = '', add_headers: dict = {}) -> None:
409
- """
410
- Send a non-OK response
411
- """
412
- handler.send_response(status)
413
- for key, value in add_headers.items():
414
- handler.send_header(key, value)
415
- if isinstance(content, str) and len(content) > 0:
416
- encoded_content = content.encode('utf-8')
417
- handler.send_header('Content-type', 'text/plain')
418
- handler.send_header('Content-Length', len(encoded_content))
419
- handler.end_headers()
420
- handler.wfile.write(encoded_content)
421
- else:
422
- handler.end_headers()
423
-
424
- @classmethod
425
- def print_httpd_log_message(cls, handler: TinySSGDebugHTTPHandler, server: TinySSGDebugHTTPServer, format: str, *args: any) -> None:
426
- """
427
- Output the log message (HTTPServer)
428
- """
429
- if not server.args['nolog'] and not str(args[0]).startswith('GET /change'):
430
- SimpleHTTPRequestHandler.log_message(handler, format, *args)
431
- sys.stdout.flush()
432
-
433
- @classmethod
434
- def httpd_get_handler(cls, handler: TinySSGDebugHTTPHandler, server: TinySSGDebugHTTPServer) -> None:
435
- """
436
- Process the GET request
437
- """
438
- if handler.path == '/change':
439
- cls.send_ok_response(handler, 'application/json', json.dumps({'reload': server.reload}))
440
- server.reload = False
441
- elif handler.path == '/stop':
442
- cls.send_ok_response(handler, 'text/plain', 'Server Stopped.')
443
- server.shutdown()
444
- elif handler.path.startswith(f"/{server.args['output']}/{server.args['static']}/"):
445
- redirect_path = re.sub('/' + re.escape(server.args['output']), '', handler.path)
446
- handler.path = redirect_path
447
- SimpleHTTPRequestHandler.do_GET(handler)
448
- elif handler.path == f"/{server.args['output']}":
449
- cls.send_no_ok_response(handler, 301, '', {'Location': f"/{server.args['output']}/"})
450
- elif handler.path.startswith(f"/{server.args['output']}/"):
451
- baselen = len(f"/{server.args['output']}/")
452
- basename = re.sub(r'\.html$', '', handler.path[baselen:])
453
- basename = f"{basename}index" if basename.endswith('/') or basename == '' else basename
454
- output_path = basename.split('/')
455
- current_route = server.route
456
-
457
- for path in output_path:
458
- if not isinstance(current_route, dict) or path not in current_route:
459
- cls.send_no_ok_response(handler, 404, 'Not Found')
460
- return
461
- current_route = current_route[path]
462
-
463
- if isinstance(current_route, dict):
464
- cls.send_no_ok_response(handler, 301, '', {'Location': f"{handler.path}/"})
465
- elif not isinstance(current_route, str):
466
- TinySSGUtility.error_print(f"Error: The Page class for {handler.path} may not be implemented correctly.")
467
- cls.send_no_ok_response(handler, 500, 'Internal Server Error')
468
- else:
469
- current_route = current_route if server.args['noreload'] else re.sub(r'(\s*</head>)', f"{cls.watchdog_script()}\n\\1", current_route)
470
- cls.send_ok_response(handler, 'text/html', current_route)
471
- else:
472
- cls.send_no_ok_response(handler, 404, 'Not Found')
473
-
474
- @classmethod
475
- def stop_server(cls, process: any) -> None:
476
- """
477
- Stop the debug server
478
- """
479
- process.kill()
480
-
481
- @classmethod
482
- def server_stop_output(cls, process) -> None:
483
- """
484
- Output the server stop message
485
- """
486
- TinySSGUtility.error_print(f"Server return code:{process.poll()}")
487
- TinySSGUtility.error_print('Server Output:\n')
488
- TinySSGUtility.error_print(process.stdout.read() if process.stdout else '')
489
- TinySSGUtility.error_print(process.stderr.read() if process.stderr else '')
490
-
491
- @classmethod
492
- def server_start(cls, args: dict) -> None:
493
- """
494
- Run the debug server
495
- """
496
- reload = args['mode'] == 'servreload'
497
- route = TinySSGGenerator.generate_routes(args)
498
- server_address = ('', args['port'])
499
- httpd = TinySSGDebugHTTPServer(server_address, TinySSGDebugHTTPHandler, args, route, reload)
500
- TinySSGUtility.error_print(f"Starting server on http://localhost:{args['port']}/{args['output']}/")
501
- httpd.serve_forever()
502
-
503
-
504
- class TinySSGLauncher:
505
- """
506
- Watchdog and Server Launcher
507
- """
508
- @classmethod
509
- def check_for_changes(cls, mod_time: float, args: dict, pathlits: list) -> bool:
510
- """
511
- Check for changes in the specified directories
512
- """
513
- path_times = []
514
- new_mod_time = 0
515
-
516
- try:
517
- for path in pathlits:
518
- time_list = [os.path.getmtime(os.path.join(root, file)) for root, _, files in os.walk(path) for file in files]
519
- if len(time_list) > 0:
520
- this_path_time = max(time_list)
521
- path_times.append(this_path_time)
522
-
523
- if len(path_times) > 0:
524
- new_mod_time = max(path_times)
525
-
526
- if new_mod_time > mod_time:
527
- mod_time = new_mod_time + args['wait']
528
- return True, mod_time
529
- except Exception as e:
530
- TinySSGUtility.log_print(f"update check warning: {e}")
531
-
532
- return False, mod_time
533
-
534
- @classmethod
535
- def launch_server(cls, args: dict, reload: bool) -> None:
536
- """
537
- Launch the server
538
- """
539
- servcommand = 'serv' if not reload else 'servreload'
540
-
541
- newargv = args.copy()
542
- newargv['mode'] = servcommand
543
-
544
- command = [sys.executable, __file__, '--config', f"{json.dumps(newargv)}", 'config']
545
-
546
- process = subprocess.Popen(
547
- command,
548
- stdout=None if not args['nolog'] else subprocess.PIPE,
549
- stderr=None if not args['nolog'] else subprocess.PIPE,
550
- text=True,
551
- encoding='utf-8'
552
- )
553
-
554
- time.sleep(1)
555
-
556
- if process.poll() is None:
557
- return process
558
- else:
559
- TinySSGUtility.log_print('Server start failed.')
560
- TinySSGDebug.stop_server(process)
561
- return None
562
-
563
- @classmethod
564
- def open_browser(cls, args: dict) -> None:
565
- """
566
- Open the browser or Display Jupyter Iframe
567
- """
568
- url = f"http://localhost:{args['port']}/{args['output']}/"
569
-
570
- is_jupyter = False
571
-
572
- try:
573
- from IPython import get_ipython # type: ignore
574
- env = get_ipython().__class__.__name__
575
- if env == 'ZMQInteractiveShell':
576
- is_jupyter = True
577
- except: # noqa: E722
578
- pass
579
-
580
- if is_jupyter:
581
- from IPython import display
582
- display.display(display.IFrame(url, width=args['jwidth'], height=args['jheight']))
583
- else:
584
- webbrowser.open(url)
585
-
586
- @classmethod
587
- def launcher_start(cls, args: dict) -> None:
588
- """
589
- Launch the debug server and file change detection
590
- """
591
- if isinstance(args['curdir'], str) and len(args['curdir']) > 0:
592
- os.chdir(args['curdir'])
593
-
594
- cur_dir = os.getcwd()
595
- page_dir = os.path.join(cur_dir, args['page'])
596
- static_dir = os.path.join(cur_dir, args['static'])
597
- lib_dir = os.path.join(cur_dir, args['lib'])
598
- mod_time = 0.0
599
- should_reload = False
600
-
601
- if not os.path.isdir(page_dir):
602
- raise TinySSGException(f"The specified page directory does not exist. ({page_dir})")
603
-
604
- check_dirs = [page_dir]
605
-
606
- if os.path.isdir(static_dir):
607
- check_dirs.append(static_dir)
608
-
609
- if os.path.isdir(lib_dir):
610
- check_dirs.append(lib_dir)
611
-
612
- if not args['noreload']:
613
- _, mod_time = cls.check_for_changes(0.0, args, check_dirs)
614
-
615
- process = cls.launch_server(args, False)
616
-
617
- if process is None:
618
- return
619
-
620
- if not args['noopen']:
621
- cls.open_browser(args)
622
-
623
- while True:
624
- try:
625
- time.sleep(1)
626
- if process.poll() is not None:
627
- TinySSGUtility.log_print('Server stopped.')
628
- TinySSGDebug.server_stop_output(process)
629
- break
630
- if not args['noreload']:
631
- should_reload, mod_time = cls.check_for_changes(mod_time, args, check_dirs)
632
- if should_reload:
633
- TinySSGUtility.log_print('File changed. Reloading...')
634
- TinySSGDebug.stop_server(process)
635
- time.sleep(1)
636
- process = cls.launch_server(args, True)
637
- except KeyboardInterrupt:
638
- TinySSGDebug.stop_server(process)
639
- TinySSGUtility.error_print('Server stopped.')
640
- TinySSGDebug.server_stop_output(process)
641
- break
642
-
643
-
644
- class TinySSG:
645
- """
646
- TinySSG Main Class
647
- """
648
- @classmethod
649
- def main(cls, args: dict) -> None:
650
- """
651
- Main function
652
- """
653
- exitcode = 0
654
-
655
- try:
656
- if args['mode'] == 'gen':
657
- if args['input'] == '':
658
- TinySSGUtility.clear_start(args)
659
- TinySSGGenerator.generator_start(args)
660
- TinySSGUtility.log_print('HTML files generated.')
661
- elif args['mode'] == 'dev':
662
- TinySSGLauncher.launcher_start(args)
663
- elif args['mode'] == 'cls':
664
- TinySSGUtility.clear_start(args)
665
- TinySSGUtility.log_print('Output directory cleared.')
666
- elif args['mode'] == 'serv' or args['mode'] == 'servreload':
667
- TinySSGDebug.server_start(args)
668
- elif args['mode'] == 'config':
669
- config = json.loads(args['config'])
670
- default_args = cls.get_default_arg_dict()
671
- for key, value in default_args.items():
672
- if key not in config:
673
- config[key] = value
674
- cls.main(config)
675
- else:
676
- raise TinySSGException('Invalid mode.')
677
- except TinySSGException as e:
678
- TinySSGUtility.error_print(f"Error: {e}")
679
- exitcode = 1
680
-
681
- sys.exit(exitcode)
682
-
683
- @classmethod
684
- def get_arg_parser(cls) -> argparse.ArgumentParser:
685
- """
686
- Set the argument parser
687
- """
688
- parser = argparse.ArgumentParser(prog='python -m tinyssg', description='TinySSG Simple Static Site Generate Tool')
689
- parser.add_argument('mode', choices=['dev', 'gen', 'cls', 'serv', 'servreload', 'config'], help='Select the mode to run (gen = Generate HTML files, dev = Run the debug server)')
690
- parser.add_argument('--port', '-P', type=int, default=8000, help='Port number for the debug server')
691
- parser.add_argument('--page', '-p', type=str, default='pages', help='Page file path')
692
- parser.add_argument('--static', '-s', type=str, default='static', help='Static file path')
693
- parser.add_argument('--lib', '-l', type=str, default='libs', help='Library file path')
694
- parser.add_argument('--input', '-i', type=str, default='', help='Input file name (Used to generate specific files only)')
695
- parser.add_argument('--output', '-o', type=str, default='dist', help='Output directory path')
696
- parser.add_argument('--wait', '-w', type=int, default=5, help='Wait time for file change detection')
697
- parser.add_argument('--nolog', '-n', action='store_true', help='Do not output debug server log')
698
- parser.add_argument('--noreload', '-r', action='store_true', help='Do not reload the server when the file changes')
699
- parser.add_argument('--noopen', '-N', action='store_true', help='Do not open the browser when starting the server')
700
- parser.add_argument('--curdir', '-C', type=str, default='', help='Current directory (For Jupyter)')
701
- parser.add_argument('--config', '-c', type=str, default='', help='Configuration json string')
702
- parser.add_argument('--jwidth', '-jw', type=str, default='600', help='Jupyter iframe width')
703
- parser.add_argument('--jheight', '-jh', type=str, default='600', help='Jupyter iframe height')
704
-
705
- return parser
706
-
707
- @classmethod
708
- def get_default_arg_dict(cls):
709
- parser = cls.get_arg_parser()
710
- return vars(parser.parse_args(['dev']))
711
-
712
- @classmethod
713
- def cli_main(cls):
714
- """
715
- Command line interface
716
- """
717
- parser = cls.get_arg_parser()
718
- parse_args = parser.parse_args()
719
- args = vars(parse_args)
720
- cls.main(args)
721
-
1
+ from tinyssg import TinySSG
722
2
 
723
3
  if __name__ == '__main__':
724
4
  TinySSG.cli_main()