LSST Applications g0b6bd0c080+a72a5dd7e6,g1182afd7b4+2a019aa3bb,g17e5ecfddb+2b8207f7de,g1d67935e3f+06cf436103,g38293774b4+ac198e9f13,g396055baef+6a2097e274,g3b44f30a73+6611e0205b,g480783c3b1+98f8679e14,g48ccf36440+89c08d0516,g4b93dc025c+98f8679e14,g5c4744a4d9+a302e8c7f0,g613e996a0d+e1c447f2e0,g6c8d09e9e7+25247a063c,g7271f0639c+98f8679e14,g7a9cd813b8+124095ede6,g9d27549199+a302e8c7f0,ga1cf026fa3+ac198e9f13,ga32aa97882+7403ac30ac,ga786bb30fb+7a139211af,gaa63f70f4e+9994eb9896,gabf319e997+ade567573c,gba47b54d5d+94dc90c3ea,gbec6a3398f+06cf436103,gc6308e37c7+07dd123edb,gc655b1545f+ade567573c,gcc9029db3c+ab229f5caf,gd01420fc67+06cf436103,gd877ba84e5+06cf436103,gdb4cecd868+6f279b5b48,ge2d134c3d5+cc4dbb2e3f,ge448b5faa6+86d1ceac1d,gecc7e12556+98f8679e14,gf3ee170dca+25247a063c,gf4ac96e456+ade567573c,gf9f5ea5b4d+ac198e9f13,gff490e6085+8c2580be5c,w.2022.27
LSST Data Management Base Package
Classes | Functions
lsst.daf.persistence.safeFileIo Namespace Reference

Classes

class  DoNotWrite
 
class  FileForWriteOnceCompareSameFailure
 
class  SafeLockedFileForWrite
 

Functions

def safeMakeDir (directory)
 
def setFileMode (filename)
 
def FileForWriteOnceCompareSame (name)
 
def SafeFile (name)
 
def SafeFilename (name)
 
def SafeLockedFileForRead (name)
 

Function Documentation

◆ FileForWriteOnceCompareSame()

def lsst.daf.persistence.safeFileIo.FileForWriteOnceCompareSame (   name)
Context manager to get a file that can be written only once and all other writes will succeed only if
they match the initial write.

The context manager provides a temporary file object. After the user is done, the temporary file becomes
the permanent file if the file at name does not already exist. If the file at name does exist the
temporary file is compared to the file at name. If they are the same then this is good and the temp file
is silently thrown away. If they are not the same then a runtime error is raised.

Definition at line 65 of file safeFileIo.py.

66 """Context manager to get a file that can be written only once and all other writes will succeed only if
67 they match the initial write.
68
69 The context manager provides a temporary file object. After the user is done, the temporary file becomes
70 the permanent file if the file at name does not already exist. If the file at name does exist the
71 temporary file is compared to the file at name. If they are the same then this is good and the temp file
72 is silently thrown away. If they are not the same then a runtime error is raised.
73 """
74 outDir, outName = os.path.split(name)
75 safeMakeDir(outDir)
76 temp = tempfile.NamedTemporaryFile(mode="w", dir=outDir, prefix=outName, delete=False)
77 try:
78 yield temp
79 finally:
80 try:
81 temp.close()
82 # If the symlink cannot be created then it will raise. If it can't be created because a file at
83 # 'name' already exists then we'll do a compare-same check.
84 os.symlink(temp.name, name)
85 # If the symlink was created then this is the process that created the first instance of the
86 # file, and we know its contents match. Move the temp file over the symlink.
87 os.rename(temp.name, name)
88 # At this point, we know the file has just been created. Set permissions according to the
89 # current umask.
90 setFileMode(name)
91 except OSError as e:
92 if e.errno != errno.EEXIST:
93 raise e
94 filesMatch = filecmp.cmp(temp.name, name, shallow=False)
95 os.remove(temp.name)
96 if filesMatch:
97 # if the files match then the compare-same check succeeded and we can silently return.
98 return
99 else:
100 # if the files do not match then the calling code was trying to write a non-matching file over
101 # the previous file, maybe it's a race condition? In any event, raise a runtime error.
102 raise FileForWriteOnceCompareSameFailure("Written file does not match existing file.")
103
104
105@contextmanager

◆ SafeFile()

def lsst.daf.persistence.safeFileIo.SafeFile (   name)
Context manager to create a file in a manner avoiding race conditions

The context manager provides a temporary file object. After the user is done,
we move that file into the desired place and close the fd to avoid resource
leakage.

Definition at line 106 of file safeFileIo.py.

106def SafeFile(name):
107 """Context manager to create a file in a manner avoiding race conditions
108
109 The context manager provides a temporary file object. After the user is done,
110 we move that file into the desired place and close the fd to avoid resource
111 leakage.
112 """
113 outDir, outName = os.path.split(name)
114 safeMakeDir(outDir)
115 doWrite = True
116 with tempfile.NamedTemporaryFile(mode="w", dir=outDir, prefix=outName, delete=False) as temp:
117 try:
118 yield temp
119 except DoNotWrite:
120 doWrite = False
121 finally:
122 if doWrite:
123 os.rename(temp.name, name)
124 setFileMode(name)
125
126
127@contextmanager

◆ SafeFilename()

def lsst.daf.persistence.safeFileIo.SafeFilename (   name)
Context manager for creating a file in a manner avoiding race conditions

The context manager provides a temporary filename with no open file descriptors
(as this can cause trouble on some systems). After the user is done, we move the
file into the desired place.

Definition at line 128 of file safeFileIo.py.

128def SafeFilename(name):
129 """Context manager for creating a file in a manner avoiding race conditions
130
131 The context manager provides a temporary filename with no open file descriptors
132 (as this can cause trouble on some systems). After the user is done, we move the
133 file into the desired place.
134 """
135 outDir, outName = os.path.split(name)
136 safeMakeDir(outDir)
137 temp = tempfile.NamedTemporaryFile(mode="w", dir=outDir, prefix=outName, delete=False)
138 tempName = temp.name
139 temp.close() # We don't use the fd, just want a filename
140 try:
141 yield tempName
142 finally:
143 os.rename(tempName, name)
144 setFileMode(name)
145
146
147@contextmanager

◆ SafeLockedFileForRead()

def lsst.daf.persistence.safeFileIo.SafeLockedFileForRead (   name)
Context manager for reading a file that may be locked with an exclusive lock via
SafeLockedFileForWrite. This will first acquire a shared lock before returning the file. When the file is
closed the shared lock will be unlocked.

Parameters
----------
name : string
    The file name to be opened, may include path.

Yields
------
file object
    The file to be read from.

Definition at line 148 of file safeFileIo.py.

148def SafeLockedFileForRead(name):
149 """Context manager for reading a file that may be locked with an exclusive lock via
150 SafeLockedFileForWrite. This will first acquire a shared lock before returning the file. When the file is
151 closed the shared lock will be unlocked.
152
153 Parameters
154 ----------
155 name : string
156 The file name to be opened, may include path.
157
158 Yields
159 ------
160 file object
161 The file to be read from.
162 """
163 log = Log.getLogger("daf.persistence.butler")
164 try:
165 with open(name, 'r') as f:
166 log.debug("Acquiring shared lock on {}".format(name))
167 fcntl.flock(f, fcntl.LOCK_SH)
168 log.debug("Acquired shared lock on {}".format(name))
169 yield f
170 finally:
171 log.debug("Releasing shared lock on {}".format(name))
172
173
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
Definition: history.py:174

◆ safeMakeDir()

def lsst.daf.persistence.safeFileIo.safeMakeDir (   directory)
Make a directory in a manner avoiding race conditions

Definition at line 39 of file safeFileIo.py.

39def safeMakeDir(directory):
40 """Make a directory in a manner avoiding race conditions"""
41 if directory != "" and not os.path.exists(directory):
42 try:
43 os.makedirs(directory)
44 except OSError as e:
45 # Don't fail if directory exists due to race
46 if e.errno != errno.EEXIST:
47 raise e
48
49

◆ setFileMode()

def lsst.daf.persistence.safeFileIo.setFileMode (   filename)
Set a file mode according to the user's umask

Definition at line 50 of file safeFileIo.py.

50def setFileMode(filename):
51 """Set a file mode according to the user's umask"""
52 # Get the current umask, which we can only do by setting it and then reverting to the original.
53 umask = os.umask(0o077)
54 os.umask(umask)
55 # chmod the new file to match what it would have been if it hadn't started life as a temporary
56 # file (which have more restricted permissions).
57 os.chmod(filename, (~umask & 0o666))
58
59