-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathevent.py
More file actions
148 lines (121 loc) · 4.98 KB
/
event.py
File metadata and controls
148 lines (121 loc) · 4.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import threading
import time
import traceback
from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
import watchfiles
from rich.console import Console
from render_engine_cli.utils import get_site
console = Console()
def spawn_server(server_address: tuple[str, int], directory: str) -> ThreadingHTTPServer:
"""
Create and return an instance of ThreadingHTTPServer that serves files
from the specified directory.
Params:
server_address: A tuple of a string and integer representing the server address (host, port).
directory: A string representing the directory from which the server should serve files.
"""
class _RequestHandler(SimpleHTTPRequestHandler):
def __init__(self, *args, **kwargs):
super().__init__(*args, directory=directory, **kwargs)
def _httpd() -> ThreadingHTTPServer:
return ThreadingHTTPServer(server_address, _RequestHandler)
return _httpd()
class ServerEventHandler:
"""
Initializes a handler that looks for file changes in a directory (`dirs_to_watch`), as
well as creates a server to serve files in a given directory. The
class contains helper methods to manage server events in a thread.
Meanwhile, the `watch` method uses an instance of this handler class to monitor for file
changes. The files to be monitored are all files/directories within the `dirs_to_watch`,
which defaults to the project root directory. The `patterns` and `ignore_patterns` are used
to filter the files to be monitored using regular expressions.
Params:
server_address: A tuple of the form (host, port)
site: A Site instance
dirs_to_watch: The directories to watch
patterns: A list of regular expressions to filter files
ignore_patterns: A list of regular expressions to ignore
"""
def __init__(
self,
server_address: tuple[str, int],
import_path: str,
site: str,
output_path: str,
dirs_to_watch: str | None = None,
patterns: list[str] | None = None,
ignore_patterns: list[str] | None = None,
*args,
**kwargs,
) -> None:
self.p = None
self.server_address = server_address
self.import_path = import_path
self.site = site
self.output_path = output_path
self.dirs_to_watch = dirs_to_watch
self.patterns = patterns
self.ignore_patterns = ignore_patterns
def start_server(self) -> None:
if not getattr(self, "server", False):
console.print(
f"[bold green]Spawning server on http://{self.server_address[0]}:{self.server_address[1]}[/bold green]"
)
self.server = spawn_server(self.server_address, self.output_path)
self._thread = threading.Thread(target=self.server.serve_forever)
self._thread.start()
def stop_server(self) -> None:
console.print("[bold red]Stopping server[/bold red]")
self.server.shutdown()
self._thread.join()
def rebuild(self) -> None:
console.print("[bold purple]Reloading and Rebuilding site...[/bold purple]")
site = get_site(self.import_path, self.site, reload=True)
try:
site.render()
except Exception:
console.print("[bold red]Failed to render site[/bold red]")
console.print(traceback.format_exc())
pass
def stop_watcher(self) -> bool:
"""
logic to stop the watcher.
By default this code looks for the KeyboardInterrupt
"""
# return if keyboard interrupt is raised
try:
time.sleep(1)
return False
except KeyboardInterrupt:
return True
def watch(self) -> None:
"""
This function `watch` starts the server on the output path
and monitors the specified directories in (`dirs_to_watch`) for changes.
After it starts the server, it "waits" and monitors the directory for
changes. If a change is detected, the `on_any_event` method is called,
which will stop the server and rebuild the site before restarting the
server.
If a KeyboardInterrupt is raised, it stops the observer and server.
"""
console.print(f"[yellow]Serving {self.output_path}[/yellow]")
while not self.stop_watcher():
try:
if self.dirs_to_watch:
for _ in watchfiles.watch(*self.dirs_to_watch):
self.rebuild()
except KeyboardInterrupt:
break
def __enter__(self):
"""Starting Context manager for the class"""
try:
self._thread.server_close()
except AttributeError:
pass
self.start_server()
self.watch()
def __exit__(self, exc_type, exc_value, traceback) -> None:
"""Stopping Context manager for the class"""
self.stop_server()
console.print("[bold red]FIN![/bold red]")
return None