Mercurial > personal > weather-server
annotate weather_server/logfile.py @ 30:c760ab7f93c2
bump version number
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Tue, 19 May 2020 10:15:29 -0400 |
parents | 20c8ec56e447 |
children | 9bc3687e1e5e |
rev | line source |
---|---|
0
efe7a1eff167
Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
1 """The part which handles writing things out and reading things in from CSV. |
efe7a1eff167
Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
2 """ |
efe7a1eff167
Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
3 |
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
4 import concurrent.futures as futures |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
5 import contextlib |
0
efe7a1eff167
Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
6 import fcntl |
efe7a1eff167
Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
7 import os |
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
8 import queue |
0
efe7a1eff167
Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
9 import threading |
efe7a1eff167
Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
10 import typing as t |
efe7a1eff167
Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
11 |
efe7a1eff167
Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
12 import bson |
efe7a1eff167
Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
13 |
efe7a1eff167
Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
14 from . import common |
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
15 |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
16 |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
17 class _WriteRequest: |
0
efe7a1eff167
Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
18 |
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
19 def __init__(self, entries: t.Iterable[t.Dict[str, t.Any]]): |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
20 """Creates a request to write the given data to the log.""" |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
21 # The data to be written. We take ownership of all the dicts! |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
22 self.entries = entries |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
23 # Once written, a future that will resolve to None if successful. |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
24 self.future = futures.Future() |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
25 |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
26 |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
27 class _ReadRequest: |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
28 |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
29 def __init__(self): |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
30 # The future that will be set with the log's contnets. |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
31 self.future = futures.Future() |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
32 |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
33 |
24
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
34 # Poison pill to tell a logger thread to stop. |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
35 _POISON = object() |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
36 |
0
efe7a1eff167
Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
37 |
efe7a1eff167
Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
38 class Logger: |
24
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
39 """Logger which handles reading/writing one temperature log file.""" |
0
efe7a1eff167
Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
40 |
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
41 def __init__(self, filename: str, *, sample_field: str): |
24
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
42 """Creates a new Logger for the given file. |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
43 |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
44 Args: |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
45 filename: The filename to open, or create if not already there. |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
46 sample_field: The field name to use as the strictly-increasing |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
47 value to ensure that no duplicate writes occur. |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
48 """ |
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
49 self._sample_field = sample_field |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
50 self._queue = queue.SimpleQueue() |
24
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
51 # Create a Future that will be resolved once the file is opened |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
52 # (or fails to be opened). |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
53 writer_started = futures.Future() |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
54 self._writer_thread = threading.Thread( |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
55 name=f'{filename!r} writer thread', |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
56 target=lambda: _writer_thread( |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
57 filename, self._queue, sample_field, writer_started), |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
58 daemon=True) |
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
59 self._writer_thread.start() |
24
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
60 writer_started.result() |
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
61 |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
62 @property |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
63 def data(self) -> t.Tuple[t.Dict[str, t.Any], ...]: |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
64 req = _ReadRequest() |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
65 self._queue.put(req) |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
66 return req.future.result() |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
67 |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
68 def write_rows(self, entries: t.Iterable[t.Dict[str, t.Any]]): |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
69 req = _WriteRequest(entries) |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
70 self._queue.put(req) |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
71 return req.future.result() |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
72 |
24
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
73 def __del__(self): |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
74 self.close() |
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
75 |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
76 def close(self): |
24
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
77 self._queue.put(_POISON) |
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
78 self._writer_thread.join() |
0
efe7a1eff167
Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
79 |
24
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
80 |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
81 def _writer_thread( |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
82 filename: str, |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
83 q: queue.Queue, |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
84 sample_field: str, |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
85 started: futures.Future, |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
86 ) -> None: |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
87 if not started.set_running_or_notify_cancel(): |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
88 return |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
89 try: |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
90 file = _open_or_create(filename) |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
91 started.set_result(None) |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
92 except BaseException as e: |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
93 started.set_exception(e) |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
94 return |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
95 with file: |
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
96 running = True |
24
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
97 data: t.List[t.Dict[str, object]] = [] |
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
98 while running: |
24
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
99 item = q.get() |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
100 if item is _POISON: |
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
101 # None is the poison pill that makes us stop. |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
102 running = False |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
103 elif isinstance(item, _ReadRequest): |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
104 if not item.future.set_running_or_notify_cancel(): |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
105 continue |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
106 try: |
24
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
107 with _file_lock(file, fcntl.LOCK_SH): |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
108 data.extend(_catch_up(file)) |
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
109 except BaseException as x: |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
110 item.future.set_exception(x) |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
111 else: |
24
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
112 item.future.set_result(tuple(data)) |
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
113 elif isinstance(item, _WriteRequest): |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
114 if not item.future.set_running_or_notify_cancel(): |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
115 continue |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
116 try: |
24
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
117 with _file_lock(file, fcntl.LOCK_EX): |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
118 data.extend(_catch_up(file)) |
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
119 # Since we're at the last good point, truncate after. |
24
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
120 file.truncate(file.tell()) |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
121 if not data: |
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
122 last = None |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
123 else: |
24
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
124 last = data[-1][sample_field] |
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
125 for entry in item.entries: |
24
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
126 entry_key = entry[sample_field] |
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
127 if last is None or last < entry_key: |
24
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
128 file.write(common.bson_encode(entry)) |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
129 data.append(entry) |
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
130 last = entry_key |
24
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
131 file.flush() |
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
132 except BaseException as x: |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
133 item.future.set_exception(x) |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
134 else: |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
135 item.future.set_result(None) |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
136 else: |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
137 raise AssertionError( |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
138 'Unexpected item {!r} in the queue'.format(item)) |
24
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
139 |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
140 |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
141 @contextlib.contextmanager |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
142 def _file_lock(file: t.BinaryIO, operation: int) -> t.Iterator[None]: |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
143 assert operation in (fcntl.LOCK_SH, fcntl.LOCK_EX), 'Invalid operation.' |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
144 fcntl.flock(file, operation) |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
145 try: |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
146 yield |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
147 finally: |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
148 fcntl.flock(file, fcntl.LOCK_UN) |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
149 |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
150 |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
151 def _size(file: t.BinaryIO) -> int: |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
152 return os.stat(file.fileno()).st_size |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
153 |
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
154 |
24
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
155 def _catch_up(file: t.BinaryIO) -> t.Iterable[t.Dict[str, object]]: |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
156 """Reads data and advances the file pointer to the end of the file.""" |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
157 size = _size(file) |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
158 pointer = file.tell() |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
159 if size == pointer: |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
160 return () |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
161 output: t.List[t.Dict[str, object]] = [] |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
162 try: |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
163 items = bson.decode_file_iter( |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
164 file, codec_options=common.BSON_OPTIONS) |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
165 for item in items: |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
166 pointer = file.tell() |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
167 output.append(item) |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
168 except bson.InvalidBSON: |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
169 pass # We have reached the last valid document. Bail. |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
170 # Seek back to immediately after the end of the last valid doc. |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
171 file.seek(pointer, os.SEEK_SET) |
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
172 return output |
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
173 |
0
efe7a1eff167
Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
174 |
efe7a1eff167
Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
175 def _open_or_create(path: str) -> t.BinaryIO: |
efe7a1eff167
Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
176 while True: |
efe7a1eff167
Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
177 try: |
efe7a1eff167
Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
178 return open(path, 'r+b') |
efe7a1eff167
Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
179 except FileNotFoundError: |
efe7a1eff167
Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
180 pass |
efe7a1eff167
Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
181 try: |
efe7a1eff167
Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
182 return open(path, 'x+b') |
efe7a1eff167
Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
183 except FileExistsError: |
efe7a1eff167
Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
184 pass |