This repository has been archived by the owner on Mar 1, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathmetadata.py
60 lines (56 loc) · 1.58 KB
/
metadata.py
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
from contextlib import contextmanager
import tempfile
import os
import json
import fcntl
from collections import OrderedDict
def load(path):
with open(os.path.join(path, "metadata.json")) as f:
return json.load(f)
def save(path, metadata):
p = os.path.join(path, "metadata.json")
with _atomic_write(p) as f:
# http://www.psf.upfronthosting.co.za/issue25457
data = OrderedDict(sorted(metadata.items(), key=str))
json.dump(data,
f,
indent=4,
separators=(',', ': '))
@contextmanager
def update(path):
"""
allow concurrent update of metadata
"""
p = os.path.join(path, "metadata.json")
# we have to open writeable to get a lock
with open(p, "a") as f:
fcntl.lockf(f, fcntl.LOCK_EX)
data = load(path)
yield(data)
save(path, data)
fcntl.lockf(f, fcntl.LOCK_UN)
@contextmanager
def _atomic_write(filename):
path = os.path.dirname(filename)
try:
file = tempfile.NamedTemporaryFile(delete=False, dir=path, mode="w+")
yield file
file.flush()
os.fsync(file.fileno())
os.rename(file.name, filename)
finally:
try:
os.remove(file.name)
except OSError as e:
if e.errno == 2:
pass
else:
raise e
if __name__ == "__main__":
import sys
if len(sys.argv) < 2:
sys.stderr.write("USAGE: %s measurment...\n" % sys.argv[0])
sys.exit(1)
for arg in sys.argv[1:]:
with update(arg) as m:
pass