##// END OF EJS Templates
merge with crew.
Vadim Gelfer -
r2177:6886bc0b merge default
parent child Browse files
Show More
@@ -6,7 +6,7 b''
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from demandload import *
8 from demandload import *
9 demandload(globals(), "cStringIO changelog errno manifest os tempfile")
9 demandload(globals(), "cStringIO changelog errno manifest os tempfile util")
10
10
11 # writes to metadata files are ordered. reads: changelog, manifest,
11 # writes to metadata files are ordered. reads: changelog, manifest,
12 # normal files. writes: normal files, manifest, changelog.
12 # normal files. writes: normal files, manifest, changelog.
@@ -36,19 +36,21 b' class appendfile(object):'
36 def __init__(self, fp, tmpname):
36 def __init__(self, fp, tmpname):
37 if tmpname:
37 if tmpname:
38 self.tmpname = tmpname
38 self.tmpname = tmpname
39 self.tmpfp = open(self.tmpname, 'ab+')
39 self.tmpfp = util.posixfile(self.tmpname, 'ab+')
40 else:
40 else:
41 fd, self.tmpname = tempfile.mkstemp(prefix="hg-appendfile-")
41 fd, self.tmpname = tempfile.mkstemp(prefix="hg-appendfile-")
42 self.tmpfp = os.fdopen(fd, 'ab+')
42 os.close(fd)
43 self.tmpfp = util.posixfile(self.tmpname, 'ab+')
43 self.realfp = fp
44 self.realfp = fp
44 self.offset = fp.tell()
45 self.offset = fp.tell()
45 # real file is not written by anyone else. cache its size so
46 # real file is not written by anyone else. cache its size so
46 # seek and read can be fast.
47 # seek and read can be fast.
47 self.realsize = os.fstat(fp.fileno()).st_size
48 self.realsize = util.fstat(fp).st_size
49 self.name = fp.name
48
50
49 def end(self):
51 def end(self):
50 self.tmpfp.flush() # make sure the stat is correct
52 self.tmpfp.flush() # make sure the stat is correct
51 return self.realsize + os.fstat(self.tmpfp.fileno()).st_size
53 return self.realsize + util.fstat(self.tmpfp).st_size
52
54
53 def tell(self):
55 def tell(self):
54 return self.offset
56 return self.offset
@@ -160,7 +160,7 b' class bundlerepository(localrepo.localre'
160 def __init__(self, ui, path, bundlename):
160 def __init__(self, ui, path, bundlename):
161 localrepo.localrepository.__init__(self, ui, path)
161 localrepo.localrepository.__init__(self, ui, path)
162 f = open(bundlename, "rb")
162 f = open(bundlename, "rb")
163 s = os.fstat(f.fileno())
163 s = util.fstat(f)
164 self.bundlefile = f
164 self.bundlefile = f
165 header = self.bundlefile.read(6)
165 header = self.bundlefile.read(6)
166 if not header.startswith("HG"):
166 if not header.startswith("HG"):
@@ -14,7 +14,7 b' from node import *'
14 from i18n import gettext as _
14 from i18n import gettext as _
15 from demandload import demandload
15 from demandload import demandload
16 demandload(globals(), "binascii changegroup errno heapq mdiff os")
16 demandload(globals(), "binascii changegroup errno heapq mdiff os")
17 demandload(globals(), "sha struct zlib")
17 demandload(globals(), "sha struct util zlib")
18
18
19 # revlog version strings
19 # revlog version strings
20 REVLOGV0 = 0
20 REVLOGV0 = 0
@@ -322,7 +322,7 b' class revlog(object):'
322 i = ""
322 i = ""
323 else:
323 else:
324 try:
324 try:
325 st = os.fstat(f.fileno())
325 st = util.fstat(f)
326 except AttributeError, inst:
326 except AttributeError, inst:
327 st = None
327 st = None
328 else:
328 else:
@@ -9,7 +9,7 b' from node import *'
9 from remoterepo import *
9 from remoterepo import *
10 from i18n import gettext as _
10 from i18n import gettext as _
11 from demandload import *
11 from demandload import *
12 demandload(globals(), "hg os re stat")
12 demandload(globals(), "hg os re stat util")
13
13
14 class sshrepository(remoterepository):
14 class sshrepository(remoterepository):
15 def __init__(self, ui, path):
15 def __init__(self, ui, path):
@@ -57,7 +57,7 b' class sshrepository(remoterepository):'
57
57
58 def readerr(self):
58 def readerr(self):
59 while 1:
59 while 1:
60 size = os.fstat(self.pipee.fileno())[stat.ST_SIZE]
60 size = util.fstat(self.pipee).st_size
61 if size == 0: break
61 if size == 0: break
62 l = self.pipee.readline()
62 l = self.pipee.readline()
63 if not l: break
63 if not l: break
@@ -409,8 +409,18 b' def rename(src, dst):'
409 """forcibly rename a file"""
409 """forcibly rename a file"""
410 try:
410 try:
411 os.rename(src, dst)
411 os.rename(src, dst)
412 except:
412 except OSError, err:
413 os.unlink(dst)
413 # on windows, rename to existing file is not allowed, so we
414 # must delete destination first. but if file is open, unlink
415 # schedules it for delete but does not delete it. rename
416 # happens immediately even for open files, so we create
417 # temporary file, delete it, rename destination to that name,
418 # then delete that. then rename is safe to do.
419 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
420 os.close(fd)
421 os.unlink(temp)
422 os.rename(dst, temp)
423 os.unlink(temp)
414 os.rename(src, dst)
424 os.rename(src, dst)
415
425
416 def unlink(f):
426 def unlink(f):
@@ -452,90 +462,13 b' def audit_path(path):'
452 or os.pardir in parts):
462 or os.pardir in parts):
453 raise Abort(_("path contains illegal component: %s\n") % path)
463 raise Abort(_("path contains illegal component: %s\n") % path)
454
464
455 def opener(base, audit=True):
456 """
457 return a function that opens files relative to base
458
459 this function is used to hide the details of COW semantics and
460 remote file access from higher level code.
461 """
462 p = base
463 audit_p = audit
464
465 def mktempcopy(name):
466 d, fn = os.path.split(name)
467 fd, temp = tempfile.mkstemp(prefix=".%s-" % fn, dir=d)
468 fp = os.fdopen(fd, "wb")
469 try:
470 fp.write(file(name, "rb").read())
471 except:
472 try: os.unlink(temp)
473 except: pass
474 raise
475 fp.close()
476 st = os.lstat(name)
477 os.chmod(temp, st.st_mode)
478 return temp
479
480 class atomictempfile(file):
481 """the file will only be copied when rename is called"""
482 def __init__(self, name, mode):
483 self.__name = name
484 self.temp = mktempcopy(name)
485 file.__init__(self, self.temp, mode)
486 def rename(self):
487 if not self.closed:
488 file.close(self)
489 rename(self.temp, self.__name)
490 def __del__(self):
491 if not self.closed:
492 try:
493 os.unlink(self.temp)
494 except: pass
495 file.close(self)
496
497 class atomicfile(atomictempfile):
498 """the file will only be copied on close"""
499 def __init__(self, name, mode):
500 atomictempfile.__init__(self, name, mode)
501 def close(self):
502 self.rename()
503 def __del__(self):
504 self.rename()
505
506 def o(path, mode="r", text=False, atomic=False, atomictemp=False):
507 if audit_p:
508 audit_path(path)
509 f = os.path.join(p, path)
510
511 if not text:
512 mode += "b" # for that other OS
513
514 if mode[0] != "r":
515 try:
516 nlink = nlinks(f)
517 except OSError:
518 d = os.path.dirname(f)
519 if not os.path.isdir(d):
520 os.makedirs(d)
521 else:
522 if atomic:
523 return atomicfile(f, mode)
524 elif atomictemp:
525 return atomictempfile(f, mode)
526 if nlink > 1:
527 rename(mktempcopy(f), f)
528 return file(f, mode)
529
530 return o
531
532 def _makelock_file(info, pathname):
465 def _makelock_file(info, pathname):
533 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
466 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
534 os.write(ld, info)
467 os.write(ld, info)
535 os.close(ld)
468 os.close(ld)
536
469
537 def _readlock_file(pathname):
470 def _readlock_file(pathname):
538 return file(pathname).read()
471 return posixfile(pathname).read()
539
472
540 def nlinks(pathname):
473 def nlinks(pathname):
541 """Return number of hardlinks for the given file."""
474 """Return number of hardlinks for the given file."""
@@ -547,6 +480,15 b' else:'
547 def os_link(src, dst):
480 def os_link(src, dst):
548 raise OSError(0, _("Hardlinks not supported"))
481 raise OSError(0, _("Hardlinks not supported"))
549
482
483 def fstat(fp):
484 '''stat file object that may not have fileno method.'''
485 try:
486 return os.fstat(fp.fileno())
487 except AttributeError:
488 return os.stat(fp.name)
489
490 posixfile = file
491
550 # Platform specific variants
492 # Platform specific variants
551 if os.name == 'nt':
493 if os.name == 'nt':
552 demandload(globals(), "msvcrt")
494 demandload(globals(), "msvcrt")
@@ -725,6 +667,84 b' else:'
725 return _("stopped by signal %d") % val, val
667 return _("stopped by signal %d") % val, val
726 raise ValueError(_("invalid exit code"))
668 raise ValueError(_("invalid exit code"))
727
669
670 def opener(base, audit=True):
671 """
672 return a function that opens files relative to base
673
674 this function is used to hide the details of COW semantics and
675 remote file access from higher level code.
676 """
677 p = base
678 audit_p = audit
679
680 def mktempcopy(name):
681 d, fn = os.path.split(name)
682 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
683 os.close(fd)
684 fp = posixfile(temp, "wb")
685 try:
686 fp.write(posixfile(name, "rb").read())
687 except:
688 try: os.unlink(temp)
689 except: pass
690 raise
691 fp.close()
692 st = os.lstat(name)
693 os.chmod(temp, st.st_mode)
694 return temp
695
696 class atomictempfile(posixfile):
697 """the file will only be copied when rename is called"""
698 def __init__(self, name, mode):
699 self.__name = name
700 self.temp = mktempcopy(name)
701 posixfile.__init__(self, self.temp, mode)
702 def rename(self):
703 if not self.closed:
704 posixfile.close(self)
705 rename(self.temp, self.__name)
706 def __del__(self):
707 if not self.closed:
708 try:
709 os.unlink(self.temp)
710 except: pass
711 posixfile.close(self)
712
713 class atomicfile(atomictempfile):
714 """the file will only be copied on close"""
715 def __init__(self, name, mode):
716 atomictempfile.__init__(self, name, mode)
717 def close(self):
718 self.rename()
719 def __del__(self):
720 self.rename()
721
722 def o(path, mode="r", text=False, atomic=False, atomictemp=False):
723 if audit_p:
724 audit_path(path)
725 f = os.path.join(p, path)
726
727 if not text:
728 mode += "b" # for that other OS
729
730 if mode[0] != "r":
731 try:
732 nlink = nlinks(f)
733 except OSError:
734 d = os.path.dirname(f)
735 if not os.path.isdir(d):
736 os.makedirs(d)
737 else:
738 if atomic:
739 return atomicfile(f, mode)
740 elif atomictemp:
741 return atomictempfile(f, mode)
742 if nlink > 1:
743 rename(mktempcopy(f), f)
744 return posixfile(f, mode)
745
746 return o
747
728 class chunkbuffer(object):
748 class chunkbuffer(object):
729 """Allow arbitrary sized chunks of data to be efficiently read from an
749 """Allow arbitrary sized chunks of data to be efficiently read from an
730 iterator over chunks of arbitrary size."""
750 iterator over chunks of arbitrary size."""
@@ -16,9 +16,9 b' import win32api'
16 from demandload import *
16 from demandload import *
17 from i18n import gettext as _
17 from i18n import gettext as _
18 demandload(globals(), 'errno os pywintypes win32con win32file win32process')
18 demandload(globals(), 'errno os pywintypes win32con win32file win32process')
19 demandload(globals(), 'winerror')
19 demandload(globals(), 'cStringIO winerror')
20
20
21 class WinError(OSError):
21 class WinError:
22 winerror_map = {
22 winerror_map = {
23 winerror.ERROR_ACCESS_DENIED: errno.EACCES,
23 winerror.ERROR_ACCESS_DENIED: errno.EACCES,
24 winerror.ERROR_ACCOUNT_DISABLED: errno.EACCES,
24 winerror.ERROR_ACCOUNT_DISABLED: errno.EACCES,
@@ -105,7 +105,7 b' class WinError(OSError):'
105 winerror.ERROR_OUTOFMEMORY: errno.ENOMEM,
105 winerror.ERROR_OUTOFMEMORY: errno.ENOMEM,
106 winerror.ERROR_PASSWORD_EXPIRED: errno.EACCES,
106 winerror.ERROR_PASSWORD_EXPIRED: errno.EACCES,
107 winerror.ERROR_PATH_BUSY: errno.EBUSY,
107 winerror.ERROR_PATH_BUSY: errno.EBUSY,
108 winerror.ERROR_PATH_NOT_FOUND: errno.ENOTDIR,
108 winerror.ERROR_PATH_NOT_FOUND: errno.ENOENT,
109 winerror.ERROR_PIPE_BUSY: errno.EBUSY,
109 winerror.ERROR_PIPE_BUSY: errno.EBUSY,
110 winerror.ERROR_PIPE_CONNECTED: errno.EPIPE,
110 winerror.ERROR_PIPE_CONNECTED: errno.EPIPE,
111 winerror.ERROR_PIPE_LISTENING: errno.EPIPE,
111 winerror.ERROR_PIPE_LISTENING: errno.EPIPE,
@@ -129,6 +129,19 b' class WinError(OSError):'
129
129
130 def __init__(self, err):
130 def __init__(self, err):
131 self.win_errno, self.win_function, self.win_strerror = err
131 self.win_errno, self.win_function, self.win_strerror = err
132 if self.win_strerror.endswith('.'):
133 self.win_strerror = self.win_strerror[:-1]
134
135 class WinIOError(WinError, IOError):
136 def __init__(self, err, filename=None):
137 WinError.__init__(self, err)
138 IOError.__init__(self, self.winerror_map.get(self.win_errno, 0),
139 self.win_strerror)
140 self.filename = filename
141
142 class WinOSError(WinError, OSError):
143 def __init__(self, err):
144 WinError.__init__(self, err)
132 OSError.__init__(self, self.winerror_map.get(self.win_errno, 0),
145 OSError.__init__(self, self.winerror_map.get(self.win_errno, 0),
133 self.win_strerror)
146 self.win_strerror)
134
147
@@ -137,7 +150,7 b' def os_link(src, dst):'
137 try:
150 try:
138 win32file.CreateHardLink(dst, src)
151 win32file.CreateHardLink(dst, src)
139 except pywintypes.error, details:
152 except pywintypes.error, details:
140 raise WinError(details)
153 raise WinOSError(details)
141
154
142 def nlinks(pathname):
155 def nlinks(pathname):
143 """Return number of hardlinks for the given file."""
156 """Return number of hardlinks for the given file."""
@@ -169,3 +182,99 b' def system_rcpath_win32():'
169 proc = win32api.GetCurrentProcess()
182 proc = win32api.GetCurrentProcess()
170 filename = win32process.GetModuleFileNameEx(proc, 0)
183 filename = win32process.GetModuleFileNameEx(proc, 0)
171 return [os.path.join(os.path.dirname(filename), 'mercurial.ini')]
184 return [os.path.join(os.path.dirname(filename), 'mercurial.ini')]
185
186 class posixfile(object):
187 '''file object with posix-like semantics. on windows, normal
188 files can not be deleted or renamed if they are open. must open
189 with win32file.FILE_SHARE_DELETE. this flag does not exist on
190 windows <= nt.'''
191
192 # tried to use win32file._open_osfhandle to pass fd to os.fdopen,
193 # but does not work at all. wrap win32 file api instead.
194
195 def __init__(self, name, mode='rb'):
196 access = 0
197 if 'r' in mode or '+' in mode:
198 access |= win32file.GENERIC_READ
199 if 'w' in mode or 'a' in mode:
200 access |= win32file.GENERIC_WRITE
201 if 'r' in mode:
202 creation = win32file.OPEN_EXISTING
203 elif 'a' in mode:
204 creation = win32file.OPEN_ALWAYS
205 else:
206 creation = win32file.CREATE_ALWAYS
207 try:
208 self.handle = win32file.CreateFile(name,
209 access,
210 win32file.FILE_SHARE_READ |
211 win32file.FILE_SHARE_WRITE |
212 win32file.FILE_SHARE_DELETE,
213 None,
214 creation,
215 win32file.FILE_ATTRIBUTE_NORMAL,
216 0)
217 except pywintypes.error, err:
218 raise WinIOError(err, name)
219 self.closed = False
220 self.name = name
221 self.mode = mode
222
223 def read(self, count=-1):
224 try:
225 cs = cStringIO.StringIO()
226 while count:
227 wincount = int(count)
228 if wincount == -1:
229 wincount = 1048576
230 val, data = win32file.ReadFile(self.handle, wincount)
231 if not data: break
232 cs.write(data)
233 if count != -1:
234 count -= len(data)
235 return cs.getvalue()
236 except pywintypes.error, err:
237 raise WinIOError(err)
238
239 def write(self, data):
240 try:
241 if 'a' in self.mode:
242 win32file.SetFilePointer(self.handle, 0, win32file.FILE_END)
243 nwrit = 0
244 while nwrit < len(data):
245 val, nwrit = win32file.WriteFile(self.handle, data)
246 data = data[nwrit:]
247 except pywintypes.error, err:
248 raise WinIOError(err)
249
250 def seek(self, pos, whence=0):
251 try:
252 win32file.SetFilePointer(self.handle, int(pos), whence)
253 except pywintypes.error, err:
254 raise WinIOError(err)
255
256 def tell(self):
257 try:
258 return win32file.SetFilePointer(self.handle, 0,
259 win32file.FILE_CURRENT)
260 except pywintypes.error, err:
261 raise WinIOError(err)
262
263 def close(self):
264 if not self.closed:
265 self.handle = None
266 self.closed = True
267
268 def flush(self):
269 try:
270 win32file.FlushFileBuffers(self.handle)
271 except pywintypes.error, err:
272 raise WinIOError(err)
273
274 def truncate(self, pos=0):
275 try:
276 win32file.SetFilePointer(self.handle, int(pos),
277 win32file.FILE_BEGIN)
278 win32file.SetEndOfFile(self.handle)
279 except pywintypes.error, err:
280 raise WinIOError(err)
General Comments 0
You need to be logged in to leave comments. Login now