annotate weather_server/logfile.py @ 23:88249e451566

server: show date when last report was >12h ago.
author Paul Fisher <paul@pfish.zone>
date Sun, 10 Nov 2019 19:42:04 -0500
parents beb42c835c52
children 20c8ec56e447
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
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
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
34 # probably handle file-writing with a queue that reports back its progress
0
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
35
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
36 class Logger:
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
37 """Logger which handles reading/writing a temperature log for one process.
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
38 """
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
39
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
40 instance_lock = threading.Lock()
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
41 instances: t.Dict[str, 'Logger'] = {}
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
42
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
43 @classmethod
21
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
44 def create(
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
45 cls,
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
46 filename: str,
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
47 *,
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
48 sample_field: str,
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
49 ) -> 'Logger':
0
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
50 """Creates a single shared instance of a logger for the given file."""
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
51 try:
21
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
52 instance = cls.instances[filename]
0
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
53 except KeyError:
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
54 with cls.instance_lock:
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
55 try:
21
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
56 instance = cls.instances[filename]
0
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
57 except KeyError:
21
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
58 cls.instances[filename] = Logger(
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
59 filename,
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
60 sample_field=sample_field)
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
61 instance = cls.instances[filename]
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
62 if instance._sample_field != sample_field:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
63 raise ValueError(
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
64 'Existing instance has different sample field: '
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
65 '{!r} != {!r}'.format(instance._sample_field, sample_field))
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
66 return instance
0
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
67
21
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
68 def __init__(self, filename: str, *, sample_field: str):
0
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
69 """You should probably call .create() instead."""
21
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
70 self._sample_field = sample_field
0
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
71 self._file = _open_or_create(filename)
21
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
72 self._data: t.List[t.Dict[str, t.Any], ...] = []
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
73 self._queue = queue.SimpleQueue()
0
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
74 self._last_size = 0
21
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
75 self._lock_status: t.Optional[int] = None
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
76 self._writer_thread = threading.Thread(target=self._writer)
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
77 self._writer_thread.start()
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
78
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
79 @property
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
80 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
81 req = _ReadRequest()
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
82 self._queue.put(req)
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
83 return req.future.result()
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
84
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
85 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
86 req = _WriteRequest(entries)
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
87 self._queue.put(req)
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
88 return req.future.result()
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
89
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
90 _POISON = object()
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
91
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
92 def close(self):
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
93 self._queue.put(self._POISON)
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
94 self._writer_thread.join()
0
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
95
21
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
96 def _writer(self) -> None:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
97 running = True
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
98 while running:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
99 item = self._queue.get()
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
100 if item is self._POISON:
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:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
107 with self._file_lock(fcntl.LOCK_SH):
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
108 self._catch_up()
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:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
112 item.future.set_result(tuple(self._data))
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:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
117 with self._file_lock(fcntl.LOCK_EX):
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
118 self._catch_up()
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.
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
120 self._file.truncate(self._file.tell())
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
121 if not self._data:
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:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
124 last = self._data[-1][self._sample_field]
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
125 for entry in item.entries:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
126 entry_key = entry[self._sample_field]
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:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
128 self._file.write(common.bson_encode(entry))
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
129 self._data.append(entry)
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
130 last = entry_key
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
131 self._file.flush()
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
132 self._last_size = self._file.tell()
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
133 except BaseException as x:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
134 item.future.set_exception(x)
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
135 else:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
136 item.future.set_result(None)
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
137 else:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
138 raise AssertionError(
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
139 'Unexpected item {!r} in the queue'.format(item))
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
140 self._file.close()
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
141
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
142 def _catch_up(self) -> None:
0
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
143 """Reads data and advances the file pointer to the end of the file."""
21
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
144 assert self._lock_status is not None, 'The lock must be held.'
0
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
145 size = self._size()
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
146 if size == self._last_size:
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
147 return
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
148 last_good = self._file.tell()
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
149 try:
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
150 items = bson.decode_file_iter(
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
151 self._file, codec_options=common.BSON_OPTIONS)
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
152 for item in items:
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
153 last_good = self._file.tell()
21
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
154 self._data.append(item)
0
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
155 except bson.InvalidBSON:
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
156 pass # We have reached the last valid document. Bail.
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
157 # Seek back to immediately after the end of the last valid doc.
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
158 self._last_size = last_good
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
159 self._file.seek(last_good, os.SEEK_SET)
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
160
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
161 def fileno(self) -> int:
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
162 return self._file.fileno()
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
163
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
164 def _size(self) -> int:
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
165 return os.stat(self.fileno()).st_size
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
166
21
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
167 @contextlib.contextmanager
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
168 def _file_lock(self, operation: int):
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
169 assert operation in (fcntl.LOCK_SH, fcntl.LOCK_EX), 'Invalid operation.'
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
170 fcntl.flock(self, operation)
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
171 self._lock_status = operation
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
172 try:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
173 yield
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
174 finally:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
175 self._lock_status = None
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
176 fcntl.flock(self, fcntl.LOCK_UN)
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 0
diff changeset
177
0
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
178
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
179 def _open_or_create(path: str) -> t.BinaryIO:
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
180 while True:
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, 'r+b')
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
183 except FileNotFoundError:
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
184 pass
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
185 try:
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
186 return open(path, 'x+b')
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
187 except FileExistsError:
efe7a1eff167 Create initial logger for weather server.
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
188 pass