LSST Applications  21.0.0-172-gfb10e10a+18fedfabac,22.0.0+297cba6710,22.0.0+80564b0ff1,22.0.0+8d77f4f51a,22.0.0+a28f4c53b1,22.0.0+dcf3732eb2,22.0.1-1-g7d6de66+2a20fdde0d,22.0.1-1-g8e32f31+297cba6710,22.0.1-1-geca5380+7fa3b7d9b6,22.0.1-12-g44dc1dc+2a20fdde0d,22.0.1-15-g6a90155+515f58c32b,22.0.1-16-g9282f48+790f5f2caa,22.0.1-2-g92698f7+dcf3732eb2,22.0.1-2-ga9b0f51+7fa3b7d9b6,22.0.1-2-gd1925c9+bf4f0e694f,22.0.1-24-g1ad7a390+a9625a72a8,22.0.1-25-g5bf6245+3ad8ecd50b,22.0.1-25-gb120d7b+8b5510f75f,22.0.1-27-g97737f7+2a20fdde0d,22.0.1-32-gf62ce7b1+aa4237961e,22.0.1-4-g0b3f228+2a20fdde0d,22.0.1-4-g243d05b+871c1b8305,22.0.1-4-g3a563be+32dcf1063f,22.0.1-4-g44f2e3d+9e4ab0f4fa,22.0.1-42-gca6935d93+ba5e5ca3eb,22.0.1-5-g15c806e+85460ae5f3,22.0.1-5-g58711c4+611d128589,22.0.1-5-g75bb458+99c117b92f,22.0.1-6-g1c63a23+7fa3b7d9b6,22.0.1-6-g50866e6+84ff5a128b,22.0.1-6-g8d3140d+720564cf76,22.0.1-6-gd805d02+cc5644f571,22.0.1-8-ge5750ce+85460ae5f3,master-g6e05de7fdc+babf819c66,master-g99da0e417a+8d77f4f51a,w.2021.48
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.

106 def 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.

128 def 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.

148 def 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.

39 def 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.

50 def 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