tinyssg 0.0.8__py3-none-any.whl → 0.0.10__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/__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()