Openers are a concept used in the code of Mercurial for accessing files in the repository store (.hg/store).
The basic building block is util.opener (http://selenic.com/repo/index.cgi/hg/file/105d8676a688/mercurial/util.py#l1437). Most interesting parts of that are:
class opener(object):
"""Open files relative to a base directory
This class is used to hide the details of COW semantics and
remote file access from higher level code.
"""
def __init__(self, base, audit=True):
self.base = base
...
def __call__(self, path, mode="r", text=False, atomictemp=False):
self.audit_path(path)
f = os.path.join(self.base, path)
if not text and "b" not in mode:
mode += "b" # for that other OS
nlink = -1
if mode not in ("r", "rb"):
try:
nlink = nlinks(f)
except OSError:
nlink = 0
d = os.path.dirname(f)
if not os.path.isdir(d):
makedirs(d, self.createmode)
if atomictemp:
return atomictempfile(f, mode, self.createmode)
if nlink > 1:
rename(mktempcopy(f), f)
fp = posixfile(f, mode)
if nlink == 0:
self._fixfilemode(f)
return fp__call__ implements "function call" of opener objects, so an opener can be treated like being a function having the signature function(path, mode="r", text=False, atomictemp=False).
That function implements "copy on write": This is an essential part of what needs to be done if a store is shared by two or more repositories using hardlinks.
These hardlinks occur -- even on Windows NTFS -- when doing a local "hg clone" without specifying "--pull", thus sharing the files of a store by using hardlinks.
If a file in the store is shared by hardlinks, the opener checks by doing nlink = nlinks(f) how many links are already pointing to the file (this is only done if the file needs to be opened for modification).
If the number of links is larger than one, the file is copied before it is opened, which is done by:
if nlink > 1:
rename(mktempcopy(f), f)This creates an independent, non-hardlinked copy in the file system.
After that, the file is opened and the file object is returned to be used by the caller, which then can for example call fp.write(str) on that.
