bl2mods/docs/build.py
2023-03-22 12:57:56 -04:00

132 lines
3 KiB
Python
Executable file

#!/usr/bin/env python3
import subprocess
import sys
import time
from datetime import datetime
from http.server import HTTPServer, ThreadingHTTPServer, SimpleHTTPRequestHandler
from pathlib import Path
from watchdog.events import FileSystemEventHandler
from watchdog.observers.polling import PollingObserver
docpath = Path(__file__).resolve().parent
class DocWatcher(FileSystemEventHandler):
proc = None
last_restart = 0
def __init__(self, path):
self.path = str(path)
def on_any_event(self, event):
if event.event_type not in ['modified', 'created', 'deleted']:
return
path = Path(event.src_path)
directory = str(path.parent).replace(self.path, '')
if not path.suffix or not directory:
return
if directory.startswith('/docs/_') or str(path).endswith('/docs/build.py'):
return
if path.suffix[1:] not in {'py', 'rst', 'yml'}:
return
self.run_proc()
def run_proc(self):
timestamp = datetime.timestamp(datetime.now())
if self.last_restart != None and timestamp - 3 < self.last_restart:
print(timestamp - 3 < self.last_restart, timestamp -3, self.last_restart)
return
self.last_restart = timestamp
build_docs(False)
class ServerHandler(SimpleHTTPRequestHandler):
def send_response(self, code, message=None):
if any(map(self.path.endswith, ['/', '.html'])):
agent = self.headers.get('User-Agent', 'n/a')
sys.stdout.write(f'\n{self.address_string()} {self.path} {code} "{agent}"')
sys.stdout.flush()
self.send_response_only(code, message)
self.send_header('Server', self.version_string())
self.send_header('Date', self.date_time_string())
class Server(ThreadingHTTPServer):
def __init__(self, host, port, directory):
ThreadingHTTPServer.__init__(self, (host, port), SimpleHTTPRequestHandler)
self.directory = directory
def finish_request(self, request, client_address):
ServerHandler(request, client_address, self, directory=self.directory)
def run(self):
with self as httpd:
host, port = httpd.socket.getsockname()[:2]
print(f'Serving HTTP on {host} port {port} (http://{host}:{port}/)')
try:
httpd.serve_forever()
except KeyboardInterrupt:
print('\nKeyboard interrupt received, exiting.')
return 0
except Exception as e:
print(f'\nError running server: {e.__class__.__name__}: {e}')
print('Bye! :3')
def build_docs(wait=False):
proc = subprocess.Popen([sys.executable, '-m', 'sphinx', '-M', 'html', str(docpath), docpath.joinpath('_build')])
if wait:
while proc.poll() == None:
time.sleep(0.1)
return proc
def main():
if len(sys.argv) < 2:
print('Must include an option: build, serve')
return 1
arg = sys.argv[1]
if arg == 'serve':
build_docs(False)
s = Server('0.0.0.0', 8080, docpath.joinpath('_build/html'))
watcher = PollingObserver()
watcher.schedule(DocWatcher(docpath.parent), docpath.parent, recursive=True)
watcher.start()
s.run()
watcher.stop()
return 0
elif arg == 'build':
return build_docs().returncode
print(f'Invalid option: {arg}')
print('Valid options: build, serve')
return 1
if __name__ == '__main__':
sys.exit(main())