changeset 43076 | 2372284d9457 |
parent 42782 | e94c8f584ee2 |
child 43077 | 687b865b95ad |
43075:57875cf423c9 | 43076:2372284d9457 |
---|---|
32 import sys |
32 import sys |
33 import time |
33 import time |
34 import traceback |
34 import traceback |
35 import warnings |
35 import warnings |
36 |
36 |
37 from .thirdparty import ( |
37 from .thirdparty import attr |
38 attr, |
|
39 ) |
|
40 from hgdemandimport import tracing |
38 from hgdemandimport import tracing |
41 from . import ( |
39 from . import ( |
42 encoding, |
40 encoding, |
43 error, |
41 error, |
44 i18n, |
42 i18n, |
140 |
138 |
141 # Python compatibility |
139 # Python compatibility |
142 |
140 |
143 _notset = object() |
141 _notset = object() |
144 |
142 |
143 |
|
145 def bitsfrom(container): |
144 def bitsfrom(container): |
146 bits = 0 |
145 bits = 0 |
147 for bit in container: |
146 for bit in container: |
148 bits |= bit |
147 bits |= bit |
149 return bits |
148 return bits |
149 |
|
150 |
150 |
151 # python 2.6 still have deprecation warning enabled by default. We do not want |
151 # python 2.6 still have deprecation warning enabled by default. We do not want |
152 # to display anything to standard user so detect if we are running test and |
152 # to display anything to standard user so detect if we are running test and |
153 # only use python deprecation warning in this case. |
153 # only use python deprecation warning in this case. |
154 _dowarn = bool(encoding.environ.get('HGEMITWARNINGS')) |
154 _dowarn = bool(encoding.environ.get('HGEMITWARNINGS')) |
162 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'mercurial') |
162 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'mercurial') |
163 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'hgext') |
163 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'hgext') |
164 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'hgext3rd') |
164 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'hgext3rd') |
165 if _dowarn and pycompat.ispy3: |
165 if _dowarn and pycompat.ispy3: |
166 # silence warning emitted by passing user string to re.sub() |
166 # silence warning emitted by passing user string to re.sub() |
167 warnings.filterwarnings(r'ignore', r'bad escape', DeprecationWarning, |
167 warnings.filterwarnings( |
168 r'mercurial') |
168 r'ignore', r'bad escape', DeprecationWarning, r'mercurial' |
169 warnings.filterwarnings(r'ignore', r'invalid escape sequence', |
169 ) |
170 DeprecationWarning, r'mercurial') |
170 warnings.filterwarnings( |
171 r'ignore', r'invalid escape sequence', DeprecationWarning, r'mercurial' |
|
172 ) |
|
171 # TODO: reinvent imp.is_frozen() |
173 # TODO: reinvent imp.is_frozen() |
172 warnings.filterwarnings(r'ignore', r'the imp module is deprecated', |
174 warnings.filterwarnings( |
173 DeprecationWarning, r'mercurial') |
175 r'ignore', |
176 r'the imp module is deprecated', |
|
177 DeprecationWarning, |
|
178 r'mercurial', |
|
179 ) |
|
180 |
|
174 |
181 |
175 def nouideprecwarn(msg, version, stacklevel=1): |
182 def nouideprecwarn(msg, version, stacklevel=1): |
176 """Issue an python native deprecation warning |
183 """Issue an python native deprecation warning |
177 |
184 |
178 This is a noop outside of tests, use 'ui.deprecwarn' when possible. |
185 This is a noop outside of tests, use 'ui.deprecwarn' when possible. |
179 """ |
186 """ |
180 if _dowarn: |
187 if _dowarn: |
181 msg += ("\n(compatibility will be dropped after Mercurial-%s," |
188 msg += ( |
182 " update your code.)") % version |
189 "\n(compatibility will be dropped after Mercurial-%s," |
190 " update your code.)" |
|
191 ) % version |
|
183 warnings.warn(pycompat.sysstr(msg), DeprecationWarning, stacklevel + 1) |
192 warnings.warn(pycompat.sysstr(msg), DeprecationWarning, stacklevel + 1) |
193 |
|
184 |
194 |
185 DIGESTS = { |
195 DIGESTS = { |
186 'md5': hashlib.md5, |
196 'md5': hashlib.md5, |
187 'sha1': hashlib.sha1, |
197 'sha1': hashlib.sha1, |
188 'sha512': hashlib.sha512, |
198 'sha512': hashlib.sha512, |
190 # List of digest types from strongest to weakest |
200 # List of digest types from strongest to weakest |
191 DIGESTS_BY_STRENGTH = ['sha512', 'sha1', 'md5'] |
201 DIGESTS_BY_STRENGTH = ['sha512', 'sha1', 'md5'] |
192 |
202 |
193 for k in DIGESTS_BY_STRENGTH: |
203 for k in DIGESTS_BY_STRENGTH: |
194 assert k in DIGESTS |
204 assert k in DIGESTS |
205 |
|
195 |
206 |
196 class digester(object): |
207 class digester(object): |
197 """helper to compute digests. |
208 """helper to compute digests. |
198 |
209 |
199 This helper can be used to compute one or more digests given their name. |
210 This helper can be used to compute one or more digests given their name. |
238 for k in DIGESTS_BY_STRENGTH: |
249 for k in DIGESTS_BY_STRENGTH: |
239 if k in supported: |
250 if k in supported: |
240 return k |
251 return k |
241 return None |
252 return None |
242 |
253 |
254 |
|
243 class digestchecker(object): |
255 class digestchecker(object): |
244 """file handle wrapper that additionally checks content against a given |
256 """file handle wrapper that additionally checks content against a given |
245 size and digests. |
257 size and digests. |
246 |
258 |
247 d = digestchecker(fh, size, {'md5': '...'}) |
259 d = digestchecker(fh, size, {'md5': '...'}) |
262 self._got += len(content) |
274 self._got += len(content) |
263 return content |
275 return content |
264 |
276 |
265 def validate(self): |
277 def validate(self): |
266 if self._size != self._got: |
278 if self._size != self._got: |
267 raise error.Abort(_('size mismatch: expected %d, got %d') % |
279 raise error.Abort( |
268 (self._size, self._got)) |
280 _('size mismatch: expected %d, got %d') |
281 % (self._size, self._got) |
|
282 ) |
|
269 for k, v in self._digests.items(): |
283 for k, v in self._digests.items(): |
270 if v != self._digester[k]: |
284 if v != self._digester[k]: |
271 # i18n: first parameter is a digest name |
285 # i18n: first parameter is a digest name |
272 raise error.Abort(_('%s mismatch: expected %s, got %s') % |
286 raise error.Abort( |
273 (k, v, self._digester[k])) |
287 _('%s mismatch: expected %s, got %s') |
288 % (k, v, self._digester[k]) |
|
289 ) |
|
290 |
|
274 |
291 |
275 try: |
292 try: |
276 buffer = buffer |
293 buffer = buffer |
277 except NameError: |
294 except NameError: |
295 |
|
278 def buffer(sliceable, offset=0, length=None): |
296 def buffer(sliceable, offset=0, length=None): |
279 if length is not None: |
297 if length is not None: |
280 return memoryview(sliceable)[offset:offset + length] |
298 return memoryview(sliceable)[offset : offset + length] |
281 return memoryview(sliceable)[offset:] |
299 return memoryview(sliceable)[offset:] |
282 |
300 |
301 |
|
283 _chunksize = 4096 |
302 _chunksize = 4096 |
303 |
|
284 |
304 |
285 class bufferedinputpipe(object): |
305 class bufferedinputpipe(object): |
286 """a manually buffered input pipe |
306 """a manually buffered input pipe |
287 |
307 |
288 Python will not let us use buffered IO and lazy reading with 'polling' at |
308 Python will not let us use buffered IO and lazy reading with 'polling' at |
294 empty from the output (allowing collaboration of the buffer with polling). |
314 empty from the output (allowing collaboration of the buffer with polling). |
295 |
315 |
296 This class lives in the 'util' module because it makes use of the 'os' |
316 This class lives in the 'util' module because it makes use of the 'os' |
297 module from the python stdlib. |
317 module from the python stdlib. |
298 """ |
318 """ |
319 |
|
299 def __new__(cls, fh): |
320 def __new__(cls, fh): |
300 # If we receive a fileobjectproxy, we need to use a variation of this |
321 # If we receive a fileobjectproxy, we need to use a variation of this |
301 # class that notifies observers about activity. |
322 # class that notifies observers about activity. |
302 if isinstance(fh, fileobjectproxy): |
323 if isinstance(fh, fileobjectproxy): |
303 cls = observedbufferedinputpipe |
324 cls = observedbufferedinputpipe |
350 while (not self._eof) and lfi < 0: |
371 while (not self._eof) and lfi < 0: |
351 self._fillbuffer() |
372 self._fillbuffer() |
352 if self._buffer: |
373 if self._buffer: |
353 lfi = self._buffer[-1].find('\n') |
374 lfi = self._buffer[-1].find('\n') |
354 size = lfi + 1 |
375 size = lfi + 1 |
355 if lfi < 0: # end of file |
376 if lfi < 0: # end of file |
356 size = self._lenbuf |
377 size = self._lenbuf |
357 elif len(self._buffer) > 1: |
378 elif len(self._buffer) > 1: |
358 # we need to take previous chunks into account |
379 # we need to take previous chunks into account |
359 size += self._lenbuf - len(self._buffer[-1]) |
380 size += self._lenbuf - len(self._buffer[-1]) |
360 return self._frombuffer(size) |
381 return self._frombuffer(size) |
368 buf = self._buffer[0] |
389 buf = self._buffer[0] |
369 if len(self._buffer) > 1: |
390 if len(self._buffer) > 1: |
370 buf = ''.join(self._buffer) |
391 buf = ''.join(self._buffer) |
371 |
392 |
372 data = buf[:size] |
393 data = buf[:size] |
373 buf = buf[len(data):] |
394 buf = buf[len(data) :] |
374 if buf: |
395 if buf: |
375 self._buffer = [buf] |
396 self._buffer = [buf] |
376 self._lenbuf = len(buf) |
397 self._lenbuf = len(buf) |
377 else: |
398 else: |
378 self._buffer = [] |
399 self._buffer = [] |
387 else: |
408 else: |
388 self._lenbuf += len(data) |
409 self._lenbuf += len(data) |
389 self._buffer.append(data) |
410 self._buffer.append(data) |
390 |
411 |
391 return data |
412 return data |
413 |
|
392 |
414 |
393 def mmapread(fp): |
415 def mmapread(fp): |
394 try: |
416 try: |
395 fd = getattr(fp, 'fileno', lambda: fp)() |
417 fd = getattr(fp, 'fileno', lambda: fp)() |
396 return mmap.mmap(fd, 0, access=mmap.ACCESS_READ) |
418 return mmap.mmap(fd, 0, access=mmap.ACCESS_READ) |
399 # if the file is empty, and if so, return an empty buffer. |
421 # if the file is empty, and if so, return an empty buffer. |
400 if os.fstat(fd).st_size == 0: |
422 if os.fstat(fd).st_size == 0: |
401 return '' |
423 return '' |
402 raise |
424 raise |
403 |
425 |
426 |
|
404 class fileobjectproxy(object): |
427 class fileobjectproxy(object): |
405 """A proxy around file objects that tells a watcher when events occur. |
428 """A proxy around file objects that tells a watcher when events occur. |
406 |
429 |
407 This type is intended to only be used for testing purposes. Think hard |
430 This type is intended to only be used for testing purposes. Think hard |
408 before using it in important code. |
431 before using it in important code. |
409 """ |
432 """ |
433 |
|
410 __slots__ = ( |
434 __slots__ = ( |
411 r'_orig', |
435 r'_orig', |
412 r'_observer', |
436 r'_observer', |
413 ) |
437 ) |
414 |
438 |
417 object.__setattr__(self, r'_observer', observer) |
441 object.__setattr__(self, r'_observer', observer) |
418 |
442 |
419 def __getattribute__(self, name): |
443 def __getattribute__(self, name): |
420 ours = { |
444 ours = { |
421 r'_observer', |
445 r'_observer', |
422 |
|
423 # IOBase |
446 # IOBase |
424 r'close', |
447 r'close', |
425 # closed if a property |
448 # closed if a property |
426 r'fileno', |
449 r'fileno', |
427 r'flush', |
450 r'flush', |
483 |
506 |
484 return res |
507 return res |
485 |
508 |
486 def close(self, *args, **kwargs): |
509 def close(self, *args, **kwargs): |
487 return object.__getattribute__(self, r'_observedcall')( |
510 return object.__getattribute__(self, r'_observedcall')( |
488 r'close', *args, **kwargs) |
511 r'close', *args, **kwargs |
512 ) |
|
489 |
513 |
490 def fileno(self, *args, **kwargs): |
514 def fileno(self, *args, **kwargs): |
491 return object.__getattribute__(self, r'_observedcall')( |
515 return object.__getattribute__(self, r'_observedcall')( |
492 r'fileno', *args, **kwargs) |
516 r'fileno', *args, **kwargs |
517 ) |
|
493 |
518 |
494 def flush(self, *args, **kwargs): |
519 def flush(self, *args, **kwargs): |
495 return object.__getattribute__(self, r'_observedcall')( |
520 return object.__getattribute__(self, r'_observedcall')( |
496 r'flush', *args, **kwargs) |
521 r'flush', *args, **kwargs |
522 ) |
|
497 |
523 |
498 def isatty(self, *args, **kwargs): |
524 def isatty(self, *args, **kwargs): |
499 return object.__getattribute__(self, r'_observedcall')( |
525 return object.__getattribute__(self, r'_observedcall')( |
500 r'isatty', *args, **kwargs) |
526 r'isatty', *args, **kwargs |
527 ) |
|
501 |
528 |
502 def readable(self, *args, **kwargs): |
529 def readable(self, *args, **kwargs): |
503 return object.__getattribute__(self, r'_observedcall')( |
530 return object.__getattribute__(self, r'_observedcall')( |
504 r'readable', *args, **kwargs) |
531 r'readable', *args, **kwargs |
532 ) |
|
505 |
533 |
506 def readline(self, *args, **kwargs): |
534 def readline(self, *args, **kwargs): |
507 return object.__getattribute__(self, r'_observedcall')( |
535 return object.__getattribute__(self, r'_observedcall')( |
508 r'readline', *args, **kwargs) |
536 r'readline', *args, **kwargs |
537 ) |
|
509 |
538 |
510 def readlines(self, *args, **kwargs): |
539 def readlines(self, *args, **kwargs): |
511 return object.__getattribute__(self, r'_observedcall')( |
540 return object.__getattribute__(self, r'_observedcall')( |
512 r'readlines', *args, **kwargs) |
541 r'readlines', *args, **kwargs |
542 ) |
|
513 |
543 |
514 def seek(self, *args, **kwargs): |
544 def seek(self, *args, **kwargs): |
515 return object.__getattribute__(self, r'_observedcall')( |
545 return object.__getattribute__(self, r'_observedcall')( |
516 r'seek', *args, **kwargs) |
546 r'seek', *args, **kwargs |
547 ) |
|
517 |
548 |
518 def seekable(self, *args, **kwargs): |
549 def seekable(self, *args, **kwargs): |
519 return object.__getattribute__(self, r'_observedcall')( |
550 return object.__getattribute__(self, r'_observedcall')( |
520 r'seekable', *args, **kwargs) |
551 r'seekable', *args, **kwargs |
552 ) |
|
521 |
553 |
522 def tell(self, *args, **kwargs): |
554 def tell(self, *args, **kwargs): |
523 return object.__getattribute__(self, r'_observedcall')( |
555 return object.__getattribute__(self, r'_observedcall')( |
524 r'tell', *args, **kwargs) |
556 r'tell', *args, **kwargs |
557 ) |
|
525 |
558 |
526 def truncate(self, *args, **kwargs): |
559 def truncate(self, *args, **kwargs): |
527 return object.__getattribute__(self, r'_observedcall')( |
560 return object.__getattribute__(self, r'_observedcall')( |
528 r'truncate', *args, **kwargs) |
561 r'truncate', *args, **kwargs |
562 ) |
|
529 |
563 |
530 def writable(self, *args, **kwargs): |
564 def writable(self, *args, **kwargs): |
531 return object.__getattribute__(self, r'_observedcall')( |
565 return object.__getattribute__(self, r'_observedcall')( |
532 r'writable', *args, **kwargs) |
566 r'writable', *args, **kwargs |
567 ) |
|
533 |
568 |
534 def writelines(self, *args, **kwargs): |
569 def writelines(self, *args, **kwargs): |
535 return object.__getattribute__(self, r'_observedcall')( |
570 return object.__getattribute__(self, r'_observedcall')( |
536 r'writelines', *args, **kwargs) |
571 r'writelines', *args, **kwargs |
572 ) |
|
537 |
573 |
538 def read(self, *args, **kwargs): |
574 def read(self, *args, **kwargs): |
539 return object.__getattribute__(self, r'_observedcall')( |
575 return object.__getattribute__(self, r'_observedcall')( |
540 r'read', *args, **kwargs) |
576 r'read', *args, **kwargs |
577 ) |
|
541 |
578 |
542 def readall(self, *args, **kwargs): |
579 def readall(self, *args, **kwargs): |
543 return object.__getattribute__(self, r'_observedcall')( |
580 return object.__getattribute__(self, r'_observedcall')( |
544 r'readall', *args, **kwargs) |
581 r'readall', *args, **kwargs |
582 ) |
|
545 |
583 |
546 def readinto(self, *args, **kwargs): |
584 def readinto(self, *args, **kwargs): |
547 return object.__getattribute__(self, r'_observedcall')( |
585 return object.__getattribute__(self, r'_observedcall')( |
548 r'readinto', *args, **kwargs) |
586 r'readinto', *args, **kwargs |
587 ) |
|
549 |
588 |
550 def write(self, *args, **kwargs): |
589 def write(self, *args, **kwargs): |
551 return object.__getattribute__(self, r'_observedcall')( |
590 return object.__getattribute__(self, r'_observedcall')( |
552 r'write', *args, **kwargs) |
591 r'write', *args, **kwargs |
592 ) |
|
553 |
593 |
554 def detach(self, *args, **kwargs): |
594 def detach(self, *args, **kwargs): |
555 return object.__getattribute__(self, r'_observedcall')( |
595 return object.__getattribute__(self, r'_observedcall')( |
556 r'detach', *args, **kwargs) |
596 r'detach', *args, **kwargs |
597 ) |
|
557 |
598 |
558 def read1(self, *args, **kwargs): |
599 def read1(self, *args, **kwargs): |
559 return object.__getattribute__(self, r'_observedcall')( |
600 return object.__getattribute__(self, r'_observedcall')( |
560 r'read1', *args, **kwargs) |
601 r'read1', *args, **kwargs |
602 ) |
|
603 |
|
561 |
604 |
562 class observedbufferedinputpipe(bufferedinputpipe): |
605 class observedbufferedinputpipe(bufferedinputpipe): |
563 """A variation of bufferedinputpipe that is aware of fileobjectproxy. |
606 """A variation of bufferedinputpipe that is aware of fileobjectproxy. |
564 |
607 |
565 ``bufferedinputpipe`` makes low-level calls to ``os.read()`` that |
608 ``bufferedinputpipe`` makes low-level calls to ``os.read()`` that |
568 |
611 |
569 This variation of ``bufferedinputpipe`` can notify observers about |
612 This variation of ``bufferedinputpipe`` can notify observers about |
570 ``os.read()`` events. It also re-publishes other events, such as |
613 ``os.read()`` events. It also re-publishes other events, such as |
571 ``read()`` and ``readline()``. |
614 ``read()`` and ``readline()``. |
572 """ |
615 """ |
616 |
|
573 def _fillbuffer(self): |
617 def _fillbuffer(self): |
574 res = super(observedbufferedinputpipe, self)._fillbuffer() |
618 res = super(observedbufferedinputpipe, self)._fillbuffer() |
575 |
619 |
576 fn = getattr(self._input._observer, r'osread', None) |
620 fn = getattr(self._input._observer, r'osread', None) |
577 if fn: |
621 if fn: |
596 fn = getattr(self._input._observer, r'bufferedreadline', None) |
640 fn = getattr(self._input._observer, r'bufferedreadline', None) |
597 if fn: |
641 if fn: |
598 fn(res) |
642 fn(res) |
599 |
643 |
600 return res |
644 return res |
645 |
|
601 |
646 |
602 PROXIED_SOCKET_METHODS = { |
647 PROXIED_SOCKET_METHODS = { |
603 r'makefile', |
648 r'makefile', |
604 r'recv', |
649 r'recv', |
605 r'recvfrom', |
650 r'recvfrom', |
612 r'settimeout', |
657 r'settimeout', |
613 r'gettimeout', |
658 r'gettimeout', |
614 r'setsockopt', |
659 r'setsockopt', |
615 } |
660 } |
616 |
661 |
662 |
|
617 class socketproxy(object): |
663 class socketproxy(object): |
618 """A proxy around a socket that tells a watcher when events occur. |
664 """A proxy around a socket that tells a watcher when events occur. |
619 |
665 |
620 This is like ``fileobjectproxy`` except for sockets. |
666 This is like ``fileobjectproxy`` except for sockets. |
621 |
667 |
622 This type is intended to only be used for testing purposes. Think hard |
668 This type is intended to only be used for testing purposes. Think hard |
623 before using it in important code. |
669 before using it in important code. |
624 """ |
670 """ |
671 |
|
625 __slots__ = ( |
672 __slots__ = ( |
626 r'_orig', |
673 r'_orig', |
627 r'_observer', |
674 r'_observer', |
628 ) |
675 ) |
629 |
676 |
662 |
709 |
663 return res |
710 return res |
664 |
711 |
665 def makefile(self, *args, **kwargs): |
712 def makefile(self, *args, **kwargs): |
666 res = object.__getattribute__(self, r'_observedcall')( |
713 res = object.__getattribute__(self, r'_observedcall')( |
667 r'makefile', *args, **kwargs) |
714 r'makefile', *args, **kwargs |
715 ) |
|
668 |
716 |
669 # The file object may be used for I/O. So we turn it into a |
717 # The file object may be used for I/O. So we turn it into a |
670 # proxy using our observer. |
718 # proxy using our observer. |
671 observer = object.__getattribute__(self, r'_observer') |
719 observer = object.__getattribute__(self, r'_observer') |
672 return makeloggingfileobject(observer.fh, res, observer.name, |
720 return makeloggingfileobject( |
673 reads=observer.reads, |
721 observer.fh, |
674 writes=observer.writes, |
722 res, |
675 logdata=observer.logdata, |
723 observer.name, |
676 logdataapis=observer.logdataapis) |
724 reads=observer.reads, |
725 writes=observer.writes, |
|
726 logdata=observer.logdata, |
|
727 logdataapis=observer.logdataapis, |
|
728 ) |
|
677 |
729 |
678 def recv(self, *args, **kwargs): |
730 def recv(self, *args, **kwargs): |
679 return object.__getattribute__(self, r'_observedcall')( |
731 return object.__getattribute__(self, r'_observedcall')( |
680 r'recv', *args, **kwargs) |
732 r'recv', *args, **kwargs |
733 ) |
|
681 |
734 |
682 def recvfrom(self, *args, **kwargs): |
735 def recvfrom(self, *args, **kwargs): |
683 return object.__getattribute__(self, r'_observedcall')( |
736 return object.__getattribute__(self, r'_observedcall')( |
684 r'recvfrom', *args, **kwargs) |
737 r'recvfrom', *args, **kwargs |
738 ) |
|
685 |
739 |
686 def recvfrom_into(self, *args, **kwargs): |
740 def recvfrom_into(self, *args, **kwargs): |
687 return object.__getattribute__(self, r'_observedcall')( |
741 return object.__getattribute__(self, r'_observedcall')( |
688 r'recvfrom_into', *args, **kwargs) |
742 r'recvfrom_into', *args, **kwargs |
743 ) |
|
689 |
744 |
690 def recv_into(self, *args, **kwargs): |
745 def recv_into(self, *args, **kwargs): |
691 return object.__getattribute__(self, r'_observedcall')( |
746 return object.__getattribute__(self, r'_observedcall')( |
692 r'recv_info', *args, **kwargs) |
747 r'recv_info', *args, **kwargs |
748 ) |
|
693 |
749 |
694 def send(self, *args, **kwargs): |
750 def send(self, *args, **kwargs): |
695 return object.__getattribute__(self, r'_observedcall')( |
751 return object.__getattribute__(self, r'_observedcall')( |
696 r'send', *args, **kwargs) |
752 r'send', *args, **kwargs |
753 ) |
|
697 |
754 |
698 def sendall(self, *args, **kwargs): |
755 def sendall(self, *args, **kwargs): |
699 return object.__getattribute__(self, r'_observedcall')( |
756 return object.__getattribute__(self, r'_observedcall')( |
700 r'sendall', *args, **kwargs) |
757 r'sendall', *args, **kwargs |
758 ) |
|
701 |
759 |
702 def sendto(self, *args, **kwargs): |
760 def sendto(self, *args, **kwargs): |
703 return object.__getattribute__(self, r'_observedcall')( |
761 return object.__getattribute__(self, r'_observedcall')( |
704 r'sendto', *args, **kwargs) |
762 r'sendto', *args, **kwargs |
763 ) |
|
705 |
764 |
706 def setblocking(self, *args, **kwargs): |
765 def setblocking(self, *args, **kwargs): |
707 return object.__getattribute__(self, r'_observedcall')( |
766 return object.__getattribute__(self, r'_observedcall')( |
708 r'setblocking', *args, **kwargs) |
767 r'setblocking', *args, **kwargs |
768 ) |
|
709 |
769 |
710 def settimeout(self, *args, **kwargs): |
770 def settimeout(self, *args, **kwargs): |
711 return object.__getattribute__(self, r'_observedcall')( |
771 return object.__getattribute__(self, r'_observedcall')( |
712 r'settimeout', *args, **kwargs) |
772 r'settimeout', *args, **kwargs |
773 ) |
|
713 |
774 |
714 def gettimeout(self, *args, **kwargs): |
775 def gettimeout(self, *args, **kwargs): |
715 return object.__getattribute__(self, r'_observedcall')( |
776 return object.__getattribute__(self, r'_observedcall')( |
716 r'gettimeout', *args, **kwargs) |
777 r'gettimeout', *args, **kwargs |
778 ) |
|
717 |
779 |
718 def setsockopt(self, *args, **kwargs): |
780 def setsockopt(self, *args, **kwargs): |
719 return object.__getattribute__(self, r'_observedcall')( |
781 return object.__getattribute__(self, r'_observedcall')( |
720 r'setsockopt', *args, **kwargs) |
782 r'setsockopt', *args, **kwargs |
783 ) |
|
784 |
|
721 |
785 |
722 class baseproxyobserver(object): |
786 class baseproxyobserver(object): |
723 def _writedata(self, data): |
787 def _writedata(self, data): |
724 if not self.logdata: |
788 if not self.logdata: |
725 if self.logdataapis: |
789 if self.logdataapis: |
730 # Simple case writes all data on a single line. |
794 # Simple case writes all data on a single line. |
731 if b'\n' not in data: |
795 if b'\n' not in data: |
732 if self.logdataapis: |
796 if self.logdataapis: |
733 self.fh.write(': %s\n' % stringutil.escapestr(data)) |
797 self.fh.write(': %s\n' % stringutil.escapestr(data)) |
734 else: |
798 else: |
735 self.fh.write('%s> %s\n' |
799 self.fh.write( |
736 % (self.name, stringutil.escapestr(data))) |
800 '%s> %s\n' % (self.name, stringutil.escapestr(data)) |
801 ) |
|
737 self.fh.flush() |
802 self.fh.flush() |
738 return |
803 return |
739 |
804 |
740 # Data with newlines is written to multiple lines. |
805 # Data with newlines is written to multiple lines. |
741 if self.logdataapis: |
806 if self.logdataapis: |
742 self.fh.write(':\n') |
807 self.fh.write(':\n') |
743 |
808 |
744 lines = data.splitlines(True) |
809 lines = data.splitlines(True) |
745 for line in lines: |
810 for line in lines: |
746 self.fh.write('%s> %s\n' |
811 self.fh.write( |
747 % (self.name, stringutil.escapestr(line))) |
812 '%s> %s\n' % (self.name, stringutil.escapestr(line)) |
813 ) |
|
748 self.fh.flush() |
814 self.fh.flush() |
815 |
|
749 |
816 |
750 class fileobjectobserver(baseproxyobserver): |
817 class fileobjectobserver(baseproxyobserver): |
751 """Logs file object activity.""" |
818 """Logs file object activity.""" |
752 def __init__(self, fh, name, reads=True, writes=True, logdata=False, |
819 |
753 logdataapis=True): |
820 def __init__( |
821 self, fh, name, reads=True, writes=True, logdata=False, logdataapis=True |
|
822 ): |
|
754 self.fh = fh |
823 self.fh = fh |
755 self.name = name |
824 self.name = name |
756 self.logdata = logdata |
825 self.logdata = logdata |
757 self.logdataapis = logdataapis |
826 self.logdataapis = logdataapis |
758 self.reads = reads |
827 self.reads = reads |
789 def readinto(self, res, dest): |
858 def readinto(self, res, dest): |
790 if not self.reads: |
859 if not self.reads: |
791 return |
860 return |
792 |
861 |
793 if self.logdataapis: |
862 if self.logdataapis: |
794 self.fh.write('%s> readinto(%d) -> %r' % (self.name, len(dest), |
863 self.fh.write( |
795 res)) |
864 '%s> readinto(%d) -> %r' % (self.name, len(dest), res) |
865 ) |
|
796 |
866 |
797 data = dest[0:res] if res is not None else b'' |
867 data = dest[0:res] if res is not None else b'' |
798 |
868 |
799 # _writedata() uses "in" operator and is confused by memoryview because |
869 # _writedata() uses "in" operator and is confused by memoryview because |
800 # characters are ints on Python 3. |
870 # characters are ints on Python 3. |
827 def bufferedread(self, res, size): |
897 def bufferedread(self, res, size): |
828 if not self.reads: |
898 if not self.reads: |
829 return |
899 return |
830 |
900 |
831 if self.logdataapis: |
901 if self.logdataapis: |
832 self.fh.write('%s> bufferedread(%d) -> %d' % ( |
902 self.fh.write( |
833 self.name, size, len(res))) |
903 '%s> bufferedread(%d) -> %d' % (self.name, size, len(res)) |
904 ) |
|
834 |
905 |
835 self._writedata(res) |
906 self._writedata(res) |
836 |
907 |
837 def bufferedreadline(self, res): |
908 def bufferedreadline(self, res): |
838 if not self.reads: |
909 if not self.reads: |
839 return |
910 return |
840 |
911 |
841 if self.logdataapis: |
912 if self.logdataapis: |
842 self.fh.write('%s> bufferedreadline() -> %d' % ( |
913 self.fh.write( |
843 self.name, len(res))) |
914 '%s> bufferedreadline() -> %d' % (self.name, len(res)) |
915 ) |
|
844 |
916 |
845 self._writedata(res) |
917 self._writedata(res) |
846 |
918 |
847 def makeloggingfileobject(logh, fh, name, reads=True, writes=True, |
919 |
848 logdata=False, logdataapis=True): |
920 def makeloggingfileobject( |
921 logh, fh, name, reads=True, writes=True, logdata=False, logdataapis=True |
|
922 ): |
|
849 """Turn a file object into a logging file object.""" |
923 """Turn a file object into a logging file object.""" |
850 |
924 |
851 observer = fileobjectobserver(logh, name, reads=reads, writes=writes, |
925 observer = fileobjectobserver( |
852 logdata=logdata, logdataapis=logdataapis) |
926 logh, |
927 name, |
|
928 reads=reads, |
|
929 writes=writes, |
|
930 logdata=logdata, |
|
931 logdataapis=logdataapis, |
|
932 ) |
|
853 return fileobjectproxy(fh, observer) |
933 return fileobjectproxy(fh, observer) |
934 |
|
854 |
935 |
855 class socketobserver(baseproxyobserver): |
936 class socketobserver(baseproxyobserver): |
856 """Logs socket activity.""" |
937 """Logs socket activity.""" |
857 def __init__(self, fh, name, reads=True, writes=True, states=True, |
938 |
858 logdata=False, logdataapis=True): |
939 def __init__( |
940 self, |
|
941 fh, |
|
942 name, |
|
943 reads=True, |
|
944 writes=True, |
|
945 states=True, |
|
946 logdata=False, |
|
947 logdataapis=True, |
|
948 ): |
|
859 self.fh = fh |
949 self.fh = fh |
860 self.name = name |
950 self.name = name |
861 self.reads = reads |
951 self.reads = reads |
862 self.writes = writes |
952 self.writes = writes |
863 self.states = states |
953 self.states = states |
866 |
956 |
867 def makefile(self, res, mode=None, bufsize=None): |
957 def makefile(self, res, mode=None, bufsize=None): |
868 if not self.states: |
958 if not self.states: |
869 return |
959 return |
870 |
960 |
871 self.fh.write('%s> makefile(%r, %r)\n' % ( |
961 self.fh.write('%s> makefile(%r, %r)\n' % (self.name, mode, bufsize)) |
872 self.name, mode, bufsize)) |
|
873 |
962 |
874 def recv(self, res, size, flags=0): |
963 def recv(self, res, size, flags=0): |
875 if not self.reads: |
964 if not self.reads: |
876 return |
965 return |
877 |
966 |
878 if self.logdataapis: |
967 if self.logdataapis: |
879 self.fh.write('%s> recv(%d, %d) -> %d' % ( |
968 self.fh.write( |
880 self.name, size, flags, len(res))) |
969 '%s> recv(%d, %d) -> %d' % (self.name, size, flags, len(res)) |
970 ) |
|
881 self._writedata(res) |
971 self._writedata(res) |
882 |
972 |
883 def recvfrom(self, res, size, flags=0): |
973 def recvfrom(self, res, size, flags=0): |
884 if not self.reads: |
974 if not self.reads: |
885 return |
975 return |
886 |
976 |
887 if self.logdataapis: |
977 if self.logdataapis: |
888 self.fh.write('%s> recvfrom(%d, %d) -> %d' % ( |
978 self.fh.write( |
889 self.name, size, flags, len(res[0]))) |
979 '%s> recvfrom(%d, %d) -> %d' |
980 % (self.name, size, flags, len(res[0])) |
|
981 ) |
|
890 |
982 |
891 self._writedata(res[0]) |
983 self._writedata(res[0]) |
892 |
984 |
893 def recvfrom_into(self, res, buf, size, flags=0): |
985 def recvfrom_into(self, res, buf, size, flags=0): |
894 if not self.reads: |
986 if not self.reads: |
895 return |
987 return |
896 |
988 |
897 if self.logdataapis: |
989 if self.logdataapis: |
898 self.fh.write('%s> recvfrom_into(%d, %d) -> %d' % ( |
990 self.fh.write( |
899 self.name, size, flags, res[0])) |
991 '%s> recvfrom_into(%d, %d) -> %d' |
900 |
992 % (self.name, size, flags, res[0]) |
901 self._writedata(buf[0:res[0]]) |
993 ) |
994 |
|
995 self._writedata(buf[0 : res[0]]) |
|
902 |
996 |
903 def recv_into(self, res, buf, size=0, flags=0): |
997 def recv_into(self, res, buf, size=0, flags=0): |
904 if not self.reads: |
998 if not self.reads: |
905 return |
999 return |
906 |
1000 |
907 if self.logdataapis: |
1001 if self.logdataapis: |
908 self.fh.write('%s> recv_into(%d, %d) -> %d' % ( |
1002 self.fh.write( |
909 self.name, size, flags, res)) |
1003 '%s> recv_into(%d, %d) -> %d' % (self.name, size, flags, res) |
1004 ) |
|
910 |
1005 |
911 self._writedata(buf[0:res]) |
1006 self._writedata(buf[0:res]) |
912 |
1007 |
913 def send(self, res, data, flags=0): |
1008 def send(self, res, data, flags=0): |
914 if not self.writes: |
1009 if not self.writes: |
915 return |
1010 return |
916 |
1011 |
917 self.fh.write('%s> send(%d, %d) -> %d' % ( |
1012 self.fh.write( |
918 self.name, len(data), flags, len(res))) |
1013 '%s> send(%d, %d) -> %d' % (self.name, len(data), flags, len(res)) |
1014 ) |
|
919 self._writedata(data) |
1015 self._writedata(data) |
920 |
1016 |
921 def sendall(self, res, data, flags=0): |
1017 def sendall(self, res, data, flags=0): |
922 if not self.writes: |
1018 if not self.writes: |
923 return |
1019 return |
924 |
1020 |
925 if self.logdataapis: |
1021 if self.logdataapis: |
926 # Returns None on success. So don't bother reporting return value. |
1022 # Returns None on success. So don't bother reporting return value. |
927 self.fh.write('%s> sendall(%d, %d)' % ( |
1023 self.fh.write('%s> sendall(%d, %d)' % (self.name, len(data), flags)) |
928 self.name, len(data), flags)) |
|
929 |
1024 |
930 self._writedata(data) |
1025 self._writedata(data) |
931 |
1026 |
932 def sendto(self, res, data, flagsoraddress, address=None): |
1027 def sendto(self, res, data, flagsoraddress, address=None): |
933 if not self.writes: |
1028 if not self.writes: |
937 flags = flagsoraddress |
1032 flags = flagsoraddress |
938 else: |
1033 else: |
939 flags = 0 |
1034 flags = 0 |
940 |
1035 |
941 if self.logdataapis: |
1036 if self.logdataapis: |
942 self.fh.write('%s> sendto(%d, %d, %r) -> %d' % ( |
1037 self.fh.write( |
943 self.name, len(data), flags, address, res)) |
1038 '%s> sendto(%d, %d, %r) -> %d' |
1039 % (self.name, len(data), flags, address, res) |
|
1040 ) |
|
944 |
1041 |
945 self._writedata(data) |
1042 self._writedata(data) |
946 |
1043 |
947 def setblocking(self, res, flag): |
1044 def setblocking(self, res, flag): |
948 if not self.states: |
1045 if not self.states: |
964 |
1061 |
965 def setsockopt(self, res, level, optname, value): |
1062 def setsockopt(self, res, level, optname, value): |
966 if not self.states: |
1063 if not self.states: |
967 return |
1064 return |
968 |
1065 |
969 self.fh.write('%s> setsockopt(%r, %r, %r) -> %r\n' % ( |
1066 self.fh.write( |
970 self.name, level, optname, value, res)) |
1067 '%s> setsockopt(%r, %r, %r) -> %r\n' |
971 |
1068 % (self.name, level, optname, value, res) |
972 def makeloggingsocket(logh, fh, name, reads=True, writes=True, states=True, |
1069 ) |
973 logdata=False, logdataapis=True): |
1070 |
1071 |
|
1072 def makeloggingsocket( |
|
1073 logh, |
|
1074 fh, |
|
1075 name, |
|
1076 reads=True, |
|
1077 writes=True, |
|
1078 states=True, |
|
1079 logdata=False, |
|
1080 logdataapis=True, |
|
1081 ): |
|
974 """Turn a socket into a logging socket.""" |
1082 """Turn a socket into a logging socket.""" |
975 |
1083 |
976 observer = socketobserver(logh, name, reads=reads, writes=writes, |
1084 observer = socketobserver( |
977 states=states, logdata=logdata, |
1085 logh, |
978 logdataapis=logdataapis) |
1086 name, |
1087 reads=reads, |
|
1088 writes=writes, |
|
1089 states=states, |
|
1090 logdata=logdata, |
|
1091 logdataapis=logdataapis, |
|
1092 ) |
|
979 return socketproxy(fh, observer) |
1093 return socketproxy(fh, observer) |
1094 |
|
980 |
1095 |
981 def version(): |
1096 def version(): |
982 """Return version information if available.""" |
1097 """Return version information if available.""" |
983 try: |
1098 try: |
984 from . import __version__ |
1099 from . import __version__ |
1100 |
|
985 return __version__.version |
1101 return __version__.version |
986 except ImportError: |
1102 except ImportError: |
987 return 'unknown' |
1103 return 'unknown' |
1104 |
|
988 |
1105 |
989 def versiontuple(v=None, n=4): |
1106 def versiontuple(v=None, n=4): |
990 """Parses a Mercurial version string into an N-tuple. |
1107 """Parses a Mercurial version string into an N-tuple. |
991 |
1108 |
992 The version string to be parsed is specified with the ``v`` argument. |
1109 The version string to be parsed is specified with the ``v`` argument. |
1066 if n == 3: |
1183 if n == 3: |
1067 return (vints[0], vints[1], vints[2]) |
1184 return (vints[0], vints[1], vints[2]) |
1068 if n == 4: |
1185 if n == 4: |
1069 return (vints[0], vints[1], vints[2], extra) |
1186 return (vints[0], vints[1], vints[2], extra) |
1070 |
1187 |
1188 |
|
1071 def cachefunc(func): |
1189 def cachefunc(func): |
1072 '''cache the result of function calls''' |
1190 '''cache the result of function calls''' |
1073 # XXX doesn't handle keywords args |
1191 # XXX doesn't handle keywords args |
1074 if func.__code__.co_argcount == 0: |
1192 if func.__code__.co_argcount == 0: |
1075 cache = [] |
1193 cache = [] |
1194 |
|
1076 def f(): |
1195 def f(): |
1077 if len(cache) == 0: |
1196 if len(cache) == 0: |
1078 cache.append(func()) |
1197 cache.append(func()) |
1079 return cache[0] |
1198 return cache[0] |
1199 |
|
1080 return f |
1200 return f |
1081 cache = {} |
1201 cache = {} |
1082 if func.__code__.co_argcount == 1: |
1202 if func.__code__.co_argcount == 1: |
1083 # we gain a small amount of time because |
1203 # we gain a small amount of time because |
1084 # we don't need to pack/unpack the list |
1204 # we don't need to pack/unpack the list |
1085 def f(arg): |
1205 def f(arg): |
1086 if arg not in cache: |
1206 if arg not in cache: |
1087 cache[arg] = func(arg) |
1207 cache[arg] = func(arg) |
1088 return cache[arg] |
1208 return cache[arg] |
1209 |
|
1089 else: |
1210 else: |
1211 |
|
1090 def f(*args): |
1212 def f(*args): |
1091 if args not in cache: |
1213 if args not in cache: |
1092 cache[args] = func(*args) |
1214 cache[args] = func(*args) |
1093 return cache[args] |
1215 return cache[args] |
1094 |
1216 |
1095 return f |
1217 return f |
1218 |
|
1096 |
1219 |
1097 class cow(object): |
1220 class cow(object): |
1098 """helper class to make copy-on-write easier |
1221 """helper class to make copy-on-write easier |
1099 |
1222 |
1100 Call preparewrite before doing any writes. |
1223 Call preparewrite before doing any writes. |
1109 |
1232 |
1110 def copy(self): |
1233 def copy(self): |
1111 """always do a cheap copy""" |
1234 """always do a cheap copy""" |
1112 self._copied = getattr(self, '_copied', 0) + 1 |
1235 self._copied = getattr(self, '_copied', 0) + 1 |
1113 return self |
1236 return self |
1237 |
|
1114 |
1238 |
1115 class sortdict(collections.OrderedDict): |
1239 class sortdict(collections.OrderedDict): |
1116 '''a simple sorted dictionary |
1240 '''a simple sorted dictionary |
1117 |
1241 |
1118 >>> d1 = sortdict([(b'a', 0), (b'b', 1)]) |
1242 >>> d1 = sortdict([(b'a', 0), (b'b', 1)]) |
1134 def update(self, src): |
1258 def update(self, src): |
1135 if isinstance(src, dict): |
1259 if isinstance(src, dict): |
1136 src = src.iteritems() |
1260 src = src.iteritems() |
1137 for k, v in src: |
1261 for k, v in src: |
1138 self[k] = v |
1262 self[k] = v |
1263 |
|
1139 |
1264 |
1140 class cowdict(cow, dict): |
1265 class cowdict(cow, dict): |
1141 """copy-on-write dict |
1266 """copy-on-write dict |
1142 |
1267 |
1143 Be sure to call d = d.preparewrite() before writing to d. |
1268 Be sure to call d = d.preparewrite() before writing to d. |
1161 False |
1286 False |
1162 >>> b is b.preparewrite() |
1287 >>> b is b.preparewrite() |
1163 True |
1288 True |
1164 """ |
1289 """ |
1165 |
1290 |
1291 |
|
1166 class cowsortdict(cow, sortdict): |
1292 class cowsortdict(cow, sortdict): |
1167 """copy-on-write sortdict |
1293 """copy-on-write sortdict |
1168 |
1294 |
1169 Be sure to call d = d.preparewrite() before writing to d. |
1295 Be sure to call d = d.preparewrite() before writing to d. |
1170 """ |
1296 """ |
1171 |
1297 |
1298 |
|
1172 class transactional(object): |
1299 class transactional(object): |
1173 """Base class for making a transactional type into a context manager.""" |
1300 """Base class for making a transactional type into a context manager.""" |
1301 |
|
1174 __metaclass__ = abc.ABCMeta |
1302 __metaclass__ = abc.ABCMeta |
1175 |
1303 |
1176 @abc.abstractmethod |
1304 @abc.abstractmethod |
1177 def close(self): |
1305 def close(self): |
1178 """Successfully closes the transaction.""" |
1306 """Successfully closes the transaction.""" |
1191 try: |
1319 try: |
1192 if exc_type is None: |
1320 if exc_type is None: |
1193 self.close() |
1321 self.close() |
1194 finally: |
1322 finally: |
1195 self.release() |
1323 self.release() |
1324 |
|
1196 |
1325 |
1197 @contextlib.contextmanager |
1326 @contextlib.contextmanager |
1198 def acceptintervention(tr=None): |
1327 def acceptintervention(tr=None): |
1199 """A context manager that closes the transaction on InterventionRequired |
1328 """A context manager that closes the transaction on InterventionRequired |
1200 |
1329 |
1210 tr.close() |
1339 tr.close() |
1211 raise |
1340 raise |
1212 finally: |
1341 finally: |
1213 tr.release() |
1342 tr.release() |
1214 |
1343 |
1344 |
|
1215 @contextlib.contextmanager |
1345 @contextlib.contextmanager |
1216 def nullcontextmanager(): |
1346 def nullcontextmanager(): |
1217 yield |
1347 yield |
1218 |
1348 |
1349 |
|
1219 class _lrucachenode(object): |
1350 class _lrucachenode(object): |
1220 """A node in a doubly linked list. |
1351 """A node in a doubly linked list. |
1221 |
1352 |
1222 Holds a reference to nodes on either side as well as a key-value |
1353 Holds a reference to nodes on either side as well as a key-value |
1223 pair for the dictionary entry. |
1354 pair for the dictionary entry. |
1224 """ |
1355 """ |
1356 |
|
1225 __slots__ = (r'next', r'prev', r'key', r'value', r'cost') |
1357 __slots__ = (r'next', r'prev', r'key', r'value', r'cost') |
1226 |
1358 |
1227 def __init__(self): |
1359 def __init__(self): |
1228 self.next = None |
1360 self.next = None |
1229 self.prev = None |
1361 self.prev = None |
1235 def markempty(self): |
1367 def markempty(self): |
1236 """Mark the node as emptied.""" |
1368 """Mark the node as emptied.""" |
1237 self.key = _notset |
1369 self.key = _notset |
1238 self.value = None |
1370 self.value = None |
1239 self.cost = 0 |
1371 self.cost = 0 |
1372 |
|
1240 |
1373 |
1241 class lrucachedict(object): |
1374 class lrucachedict(object): |
1242 """Dict that caches most recent accesses and sets. |
1375 """Dict that caches most recent accesses and sets. |
1243 |
1376 |
1244 The dict consists of an actual backing dict - indexed by original |
1377 The dict consists of an actual backing dict - indexed by original |
1258 cause the total cost of the cache to go beyond the maximum cost limit, |
1391 cause the total cost of the cache to go beyond the maximum cost limit, |
1259 nodes will be evicted to make room for the new code. This can be used |
1392 nodes will be evicted to make room for the new code. This can be used |
1260 to e.g. set a max memory limit and associate an estimated bytes size |
1393 to e.g. set a max memory limit and associate an estimated bytes size |
1261 cost to each item in the cache. By default, no maximum cost is enforced. |
1394 cost to each item in the cache. By default, no maximum cost is enforced. |
1262 """ |
1395 """ |
1396 |
|
1263 def __init__(self, max, maxcost=0): |
1397 def __init__(self, max, maxcost=0): |
1264 self._cache = {} |
1398 self._cache = {} |
1265 |
1399 |
1266 self._head = head = _lrucachenode() |
1400 self._head = head = _lrucachenode() |
1267 head.prev = head |
1401 head.prev = head |
1528 del self._cache[n.key] |
1662 del self._cache[n.key] |
1529 self.totalcost -= n.cost |
1663 self.totalcost -= n.cost |
1530 n.markempty() |
1664 n.markempty() |
1531 n = n.prev |
1665 n = n.prev |
1532 |
1666 |
1667 |
|
1533 def lrucachefunc(func): |
1668 def lrucachefunc(func): |
1534 '''cache most recent results of function calls''' |
1669 '''cache most recent results of function calls''' |
1535 cache = {} |
1670 cache = {} |
1536 order = collections.deque() |
1671 order = collections.deque() |
1537 if func.__code__.co_argcount == 1: |
1672 if func.__code__.co_argcount == 1: |
1673 |
|
1538 def f(arg): |
1674 def f(arg): |
1539 if arg not in cache: |
1675 if arg not in cache: |
1540 if len(cache) > 20: |
1676 if len(cache) > 20: |
1541 del cache[order.popleft()] |
1677 del cache[order.popleft()] |
1542 cache[arg] = func(arg) |
1678 cache[arg] = func(arg) |
1543 else: |
1679 else: |
1544 order.remove(arg) |
1680 order.remove(arg) |
1545 order.append(arg) |
1681 order.append(arg) |
1546 return cache[arg] |
1682 return cache[arg] |
1683 |
|
1547 else: |
1684 else: |
1685 |
|
1548 def f(*args): |
1686 def f(*args): |
1549 if args not in cache: |
1687 if args not in cache: |
1550 if len(cache) > 20: |
1688 if len(cache) > 20: |
1551 del cache[order.popleft()] |
1689 del cache[order.popleft()] |
1552 cache[args] = func(*args) |
1690 cache[args] = func(*args) |
1555 order.append(args) |
1693 order.append(args) |
1556 return cache[args] |
1694 return cache[args] |
1557 |
1695 |
1558 return f |
1696 return f |
1559 |
1697 |
1698 |
|
1560 class propertycache(object): |
1699 class propertycache(object): |
1561 def __init__(self, func): |
1700 def __init__(self, func): |
1562 self.func = func |
1701 self.func = func |
1563 self.name = func.__name__ |
1702 self.name = func.__name__ |
1703 |
|
1564 def __get__(self, obj, type=None): |
1704 def __get__(self, obj, type=None): |
1565 result = self.func(obj) |
1705 result = self.func(obj) |
1566 self.cachevalue(obj, result) |
1706 self.cachevalue(obj, result) |
1567 return result |
1707 return result |
1568 |
1708 |
1569 def cachevalue(self, obj, value): |
1709 def cachevalue(self, obj, value): |
1570 # __dict__ assignment required to bypass __setattr__ (eg: repoview) |
1710 # __dict__ assignment required to bypass __setattr__ (eg: repoview) |
1571 obj.__dict__[self.name] = value |
1711 obj.__dict__[self.name] = value |
1712 |
|
1572 |
1713 |
1573 def clearcachedproperty(obj, prop): |
1714 def clearcachedproperty(obj, prop): |
1574 '''clear a cached property value, if one has been set''' |
1715 '''clear a cached property value, if one has been set''' |
1575 prop = pycompat.sysstr(prop) |
1716 prop = pycompat.sysstr(prop) |
1576 if prop in obj.__dict__: |
1717 if prop in obj.__dict__: |
1577 del obj.__dict__[prop] |
1718 del obj.__dict__[prop] |
1578 |
1719 |
1720 |
|
1579 def increasingchunks(source, min=1024, max=65536): |
1721 def increasingchunks(source, min=1024, max=65536): |
1580 '''return no less than min bytes per chunk while data remains, |
1722 '''return no less than min bytes per chunk while data remains, |
1581 doubling min after each chunk until it reaches max''' |
1723 doubling min after each chunk until it reaches max''' |
1724 |
|
1582 def log2(x): |
1725 def log2(x): |
1583 if not x: |
1726 if not x: |
1584 return 0 |
1727 return 0 |
1585 i = 0 |
1728 i = 0 |
1586 while x: |
1729 while x: |
1605 blen = 0 |
1748 blen = 0 |
1606 buf = [] |
1749 buf = [] |
1607 if buf: |
1750 if buf: |
1608 yield ''.join(buf) |
1751 yield ''.join(buf) |
1609 |
1752 |
1753 |
|
1610 def always(fn): |
1754 def always(fn): |
1611 return True |
1755 return True |
1612 |
1756 |
1757 |
|
1613 def never(fn): |
1758 def never(fn): |
1614 return False |
1759 return False |
1760 |
|
1615 |
1761 |
1616 def nogc(func): |
1762 def nogc(func): |
1617 """disable garbage collector |
1763 """disable garbage collector |
1618 |
1764 |
1619 Python's garbage collector triggers a GC each time a certain number of |
1765 Python's garbage collector triggers a GC each time a certain number of |
1624 containers. |
1770 containers. |
1625 |
1771 |
1626 This garbage collector issue have been fixed in 2.7. But it still affect |
1772 This garbage collector issue have been fixed in 2.7. But it still affect |
1627 CPython's performance. |
1773 CPython's performance. |
1628 """ |
1774 """ |
1775 |
|
1629 def wrapper(*args, **kwargs): |
1776 def wrapper(*args, **kwargs): |
1630 gcenabled = gc.isenabled() |
1777 gcenabled = gc.isenabled() |
1631 gc.disable() |
1778 gc.disable() |
1632 try: |
1779 try: |
1633 return func(*args, **kwargs) |
1780 return func(*args, **kwargs) |
1634 finally: |
1781 finally: |
1635 if gcenabled: |
1782 if gcenabled: |
1636 gc.enable() |
1783 gc.enable() |
1784 |
|
1637 return wrapper |
1785 return wrapper |
1786 |
|
1638 |
1787 |
1639 if pycompat.ispypy: |
1788 if pycompat.ispypy: |
1640 # PyPy runs slower with gc disabled |
1789 # PyPy runs slower with gc disabled |
1641 nogc = lambda x: x |
1790 nogc = lambda x: x |
1791 |
|
1642 |
1792 |
1643 def pathto(root, n1, n2): |
1793 def pathto(root, n1, n2): |
1644 '''return the relative path from one place to another. |
1794 '''return the relative path from one place to another. |
1645 root should use os.sep to separate directories |
1795 root should use os.sep to separate directories |
1646 n1 should use os.sep to separate directories |
1796 n1 should use os.sep to separate directories |
1664 a.pop() |
1814 a.pop() |
1665 b.pop() |
1815 b.pop() |
1666 b.reverse() |
1816 b.reverse() |
1667 return pycompat.ossep.join((['..'] * len(a)) + b) or '.' |
1817 return pycompat.ossep.join((['..'] * len(a)) + b) or '.' |
1668 |
1818 |
1819 |
|
1669 # the location of data files matching the source code |
1820 # the location of data files matching the source code |
1670 if procutil.mainfrozen() and getattr(sys, 'frozen', None) != 'macosx_app': |
1821 if procutil.mainfrozen() and getattr(sys, 'frozen', None) != 'macosx_app': |
1671 # executable version (py2exe) doesn't support __file__ |
1822 # executable version (py2exe) doesn't support __file__ |
1672 datapath = os.path.dirname(pycompat.sysexecutable) |
1823 datapath = os.path.dirname(pycompat.sysexecutable) |
1673 else: |
1824 else: |
1674 datapath = os.path.dirname(pycompat.fsencode(__file__)) |
1825 datapath = os.path.dirname(pycompat.fsencode(__file__)) |
1675 |
1826 |
1676 i18n.setdatapath(datapath) |
1827 i18n.setdatapath(datapath) |
1677 |
1828 |
1829 |
|
1678 def checksignature(func): |
1830 def checksignature(func): |
1679 '''wrap a function with code to check for calling errors''' |
1831 '''wrap a function with code to check for calling errors''' |
1832 |
|
1680 def check(*args, **kwargs): |
1833 def check(*args, **kwargs): |
1681 try: |
1834 try: |
1682 return func(*args, **kwargs) |
1835 return func(*args, **kwargs) |
1683 except TypeError: |
1836 except TypeError: |
1684 if len(traceback.extract_tb(sys.exc_info()[2])) == 1: |
1837 if len(traceback.extract_tb(sys.exc_info()[2])) == 1: |
1685 raise error.SignatureError |
1838 raise error.SignatureError |
1686 raise |
1839 raise |
1687 |
1840 |
1688 return check |
1841 return check |
1842 |
|
1689 |
1843 |
1690 # a whilelist of known filesystems where hardlink works reliably |
1844 # a whilelist of known filesystems where hardlink works reliably |
1691 _hardlinkfswhitelist = { |
1845 _hardlinkfswhitelist = { |
1692 'apfs', |
1846 'apfs', |
1693 'btrfs', |
1847 'btrfs', |
1701 'tmpfs', |
1855 'tmpfs', |
1702 'ufs', |
1856 'ufs', |
1703 'xfs', |
1857 'xfs', |
1704 'zfs', |
1858 'zfs', |
1705 } |
1859 } |
1860 |
|
1706 |
1861 |
1707 def copyfile(src, dest, hardlink=False, copystat=False, checkambig=False): |
1862 def copyfile(src, dest, hardlink=False, copystat=False, checkambig=False): |
1708 '''copy a file, preserving mode and optionally other stat info like |
1863 '''copy a file, preserving mode and optionally other stat info like |
1709 atime/mtime |
1864 atime/mtime |
1710 |
1865 |
1732 if hardlink: |
1887 if hardlink: |
1733 try: |
1888 try: |
1734 oslink(src, dest) |
1889 oslink(src, dest) |
1735 return |
1890 return |
1736 except (IOError, OSError): |
1891 except (IOError, OSError): |
1737 pass # fall back to normal copy |
1892 pass # fall back to normal copy |
1738 if os.path.islink(src): |
1893 if os.path.islink(src): |
1739 os.symlink(os.readlink(src), dest) |
1894 os.symlink(os.readlink(src), dest) |
1740 # copytime is ignored for symlinks, but in general copytime isn't needed |
1895 # copytime is ignored for symlinks, but in general copytime isn't needed |
1741 # for them anyway |
1896 # for them anyway |
1742 else: |
1897 else: |
1750 if oldstat and oldstat.stat: |
1905 if oldstat and oldstat.stat: |
1751 newstat = filestat.frompath(dest) |
1906 newstat = filestat.frompath(dest) |
1752 if newstat.isambig(oldstat): |
1907 if newstat.isambig(oldstat): |
1753 # stat of copied file is ambiguous to original one |
1908 # stat of copied file is ambiguous to original one |
1754 advanced = ( |
1909 advanced = ( |
1755 oldstat.stat[stat.ST_MTIME] + 1) & 0x7fffffff |
1910 oldstat.stat[stat.ST_MTIME] + 1 |
1911 ) & 0x7FFFFFFF |
|
1756 os.utime(dest, (advanced, advanced)) |
1912 os.utime(dest, (advanced, advanced)) |
1757 except shutil.Error as inst: |
1913 except shutil.Error as inst: |
1758 raise error.Abort(str(inst)) |
1914 raise error.Abort(str(inst)) |
1759 |
1915 |
1916 |
|
1760 def copyfiles(src, dst, hardlink=None, progress=None): |
1917 def copyfiles(src, dst, hardlink=None, progress=None): |
1761 """Copy a directory tree using hardlinks if possible.""" |
1918 """Copy a directory tree using hardlinks if possible.""" |
1762 num = 0 |
1919 num = 0 |
1763 |
1920 |
1764 def settopic(): |
1921 def settopic(): |
1765 if progress: |
1922 if progress: |
1766 progress.topic = _('linking') if hardlink else _('copying') |
1923 progress.topic = _('linking') if hardlink else _('copying') |
1767 |
1924 |
1768 if os.path.isdir(src): |
1925 if os.path.isdir(src): |
1769 if hardlink is None: |
1926 if hardlink is None: |
1770 hardlink = (os.stat(src).st_dev == |
1927 hardlink = ( |
1771 os.stat(os.path.dirname(dst)).st_dev) |
1928 os.stat(src).st_dev == os.stat(os.path.dirname(dst)).st_dev |
1929 ) |
|
1772 settopic() |
1930 settopic() |
1773 os.mkdir(dst) |
1931 os.mkdir(dst) |
1774 for name, kind in listdir(src): |
1932 for name, kind in listdir(src): |
1775 srcname = os.path.join(src, name) |
1933 srcname = os.path.join(src, name) |
1776 dstname = os.path.join(dst, name) |
1934 dstname = os.path.join(dst, name) |
1777 hardlink, n = copyfiles(srcname, dstname, hardlink, progress) |
1935 hardlink, n = copyfiles(srcname, dstname, hardlink, progress) |
1778 num += n |
1936 num += n |
1779 else: |
1937 else: |
1780 if hardlink is None: |
1938 if hardlink is None: |
1781 hardlink = (os.stat(os.path.dirname(src)).st_dev == |
1939 hardlink = ( |
1782 os.stat(os.path.dirname(dst)).st_dev) |
1940 os.stat(os.path.dirname(src)).st_dev |
1941 == os.stat(os.path.dirname(dst)).st_dev |
|
1942 ) |
|
1783 settopic() |
1943 settopic() |
1784 |
1944 |
1785 if hardlink: |
1945 if hardlink: |
1786 try: |
1946 try: |
1787 oslink(src, dst) |
1947 oslink(src, dst) |
1794 if progress: |
1954 if progress: |
1795 progress.increment() |
1955 progress.increment() |
1796 |
1956 |
1797 return hardlink, num |
1957 return hardlink, num |
1798 |
1958 |
1959 |
|
1799 _winreservednames = { |
1960 _winreservednames = { |
1800 'con', 'prn', 'aux', 'nul', |
1961 'con', |
1801 'com1', 'com2', 'com3', 'com4', 'com5', 'com6', 'com7', 'com8', 'com9', |
1962 'prn', |
1802 'lpt1', 'lpt2', 'lpt3', 'lpt4', 'lpt5', 'lpt6', 'lpt7', 'lpt8', 'lpt9', |
1963 'aux', |
1964 'nul', |
|
1965 'com1', |
|
1966 'com2', |
|
1967 'com3', |
|
1968 'com4', |
|
1969 'com5', |
|
1970 'com6', |
|
1971 'com7', |
|
1972 'com8', |
|
1973 'com9', |
|
1974 'lpt1', |
|
1975 'lpt2', |
|
1976 'lpt3', |
|
1977 'lpt4', |
|
1978 'lpt5', |
|
1979 'lpt6', |
|
1980 'lpt7', |
|
1981 'lpt8', |
|
1982 'lpt9', |
|
1803 } |
1983 } |
1804 _winreservedchars = ':*?"<>|' |
1984 _winreservedchars = ':*?"<>|' |
1985 |
|
1986 |
|
1805 def checkwinfilename(path): |
1987 def checkwinfilename(path): |
1806 r'''Check that the base-relative path is a valid filename on Windows. |
1988 r'''Check that the base-relative path is a valid filename on Windows. |
1807 Returns None if the path is ok, or a UI string describing the problem. |
1989 Returns None if the path is ok, or a UI string describing the problem. |
1808 |
1990 |
1809 >>> checkwinfilename(b"just/a/normal/path") |
1991 >>> checkwinfilename(b"just/a/normal/path") |
1833 for n in path.replace('\\', '/').split('/'): |
2015 for n in path.replace('\\', '/').split('/'): |
1834 if not n: |
2016 if not n: |
1835 continue |
2017 continue |
1836 for c in _filenamebytestr(n): |
2018 for c in _filenamebytestr(n): |
1837 if c in _winreservedchars: |
2019 if c in _winreservedchars: |
1838 return _("filename contains '%s', which is reserved " |
2020 return ( |
1839 "on Windows") % c |
2021 _("filename contains '%s', which is reserved " "on Windows") |
2022 % c |
|
2023 ) |
|
1840 if ord(c) <= 31: |
2024 if ord(c) <= 31: |
1841 return _("filename contains '%s', which is invalid " |
2025 return _( |
1842 "on Windows") % stringutil.escapestr(c) |
2026 "filename contains '%s', which is invalid " "on Windows" |
2027 ) % stringutil.escapestr(c) |
|
1843 base = n.split('.')[0] |
2028 base = n.split('.')[0] |
1844 if base and base.lower() in _winreservednames: |
2029 if base and base.lower() in _winreservednames: |
1845 return _("filename contains '%s', which is reserved " |
2030 return ( |
1846 "on Windows") % base |
2031 _("filename contains '%s', which is reserved " "on Windows") |
2032 % base |
|
2033 ) |
|
1847 t = n[-1:] |
2034 t = n[-1:] |
1848 if t in '. ' and n not in '..': |
2035 if t in '. ' and n not in '..': |
1849 return _("filename ends with '%s', which is not allowed " |
2036 return ( |
1850 "on Windows") % t |
2037 _("filename ends with '%s', which is not allowed " "on Windows") |
2038 % t |
|
2039 ) |
|
2040 |
|
1851 |
2041 |
1852 if pycompat.iswindows: |
2042 if pycompat.iswindows: |
1853 checkosfilename = checkwinfilename |
2043 checkosfilename = checkwinfilename |
1854 timer = time.clock |
2044 timer = time.clock |
1855 else: |
2045 else: |
1856 checkosfilename = platform.checkosfilename |
2046 checkosfilename = platform.checkosfilename |
1857 timer = time.time |
2047 timer = time.time |
1858 |
2048 |
1859 if safehasattr(time, "perf_counter"): |
2049 if safehasattr(time, "perf_counter"): |
1860 timer = time.perf_counter |
2050 timer = time.perf_counter |
2051 |
|
1861 |
2052 |
1862 def makelock(info, pathname): |
2053 def makelock(info, pathname): |
1863 """Create a lock file atomically if possible |
2054 """Create a lock file atomically if possible |
1864 |
2055 |
1865 This may leave a stale lock file if symlink isn't supported and signal |
2056 This may leave a stale lock file if symlink isn't supported and signal |
1868 try: |
2059 try: |
1869 return os.symlink(info, pathname) |
2060 return os.symlink(info, pathname) |
1870 except OSError as why: |
2061 except OSError as why: |
1871 if why.errno == errno.EEXIST: |
2062 if why.errno == errno.EEXIST: |
1872 raise |
2063 raise |
1873 except AttributeError: # no symlink in os |
2064 except AttributeError: # no symlink in os |
1874 pass |
2065 pass |
1875 |
2066 |
1876 flags = os.O_CREAT | os.O_WRONLY | os.O_EXCL | getattr(os, 'O_BINARY', 0) |
2067 flags = os.O_CREAT | os.O_WRONLY | os.O_EXCL | getattr(os, 'O_BINARY', 0) |
1877 ld = os.open(pathname, flags) |
2068 ld = os.open(pathname, flags) |
1878 os.write(ld, info) |
2069 os.write(ld, info) |
1879 os.close(ld) |
2070 os.close(ld) |
2071 |
|
1880 |
2072 |
1881 def readlock(pathname): |
2073 def readlock(pathname): |
1882 try: |
2074 try: |
1883 return readlink(pathname) |
2075 return readlink(pathname) |
1884 except OSError as why: |
2076 except OSError as why: |
1885 if why.errno not in (errno.EINVAL, errno.ENOSYS): |
2077 if why.errno not in (errno.EINVAL, errno.ENOSYS): |
1886 raise |
2078 raise |
1887 except AttributeError: # no symlink in os |
2079 except AttributeError: # no symlink in os |
1888 pass |
2080 pass |
1889 with posixfile(pathname, 'rb') as fp: |
2081 with posixfile(pathname, 'rb') as fp: |
1890 return fp.read() |
2082 return fp.read() |
2083 |
|
1891 |
2084 |
1892 def fstat(fp): |
2085 def fstat(fp): |
1893 '''stat file object that may not have fileno method.''' |
2086 '''stat file object that may not have fileno method.''' |
1894 try: |
2087 try: |
1895 return os.fstat(fp.fileno()) |
2088 return os.fstat(fp.fileno()) |
1896 except AttributeError: |
2089 except AttributeError: |
1897 return os.stat(fp.name) |
2090 return os.stat(fp.name) |
1898 |
2091 |
2092 |
|
1899 # File system features |
2093 # File system features |
2094 |
|
1900 |
2095 |
1901 def fscasesensitive(path): |
2096 def fscasesensitive(path): |
1902 """ |
2097 """ |
1903 Return true if the given path is on a case-sensitive filesystem |
2098 Return true if the given path is on a case-sensitive filesystem |
1904 |
2099 |
1909 d, b = os.path.split(path) |
2104 d, b = os.path.split(path) |
1910 b2 = b.upper() |
2105 b2 = b.upper() |
1911 if b == b2: |
2106 if b == b2: |
1912 b2 = b.lower() |
2107 b2 = b.lower() |
1913 if b == b2: |
2108 if b == b2: |
1914 return True # no evidence against case sensitivity |
2109 return True # no evidence against case sensitivity |
1915 p2 = os.path.join(d, b2) |
2110 p2 = os.path.join(d, b2) |
1916 try: |
2111 try: |
1917 s2 = os.lstat(p2) |
2112 s2 = os.lstat(p2) |
1918 if s2 == s1: |
2113 if s2 == s1: |
1919 return False |
2114 return False |
1920 return True |
2115 return True |
1921 except OSError: |
2116 except OSError: |
1922 return True |
2117 return True |
1923 |
2118 |
2119 |
|
1924 try: |
2120 try: |
1925 import re2 |
2121 import re2 |
2122 |
|
1926 _re2 = None |
2123 _re2 = None |
1927 except ImportError: |
2124 except ImportError: |
1928 _re2 = False |
2125 _re2 = False |
2126 |
|
1929 |
2127 |
1930 class _re(object): |
2128 class _re(object): |
1931 def _checkre2(self): |
2129 def _checkre2(self): |
1932 global _re2 |
2130 global _re2 |
1933 try: |
2131 try: |
1968 if _re2: |
2166 if _re2: |
1969 return re2.escape |
2167 return re2.escape |
1970 else: |
2168 else: |
1971 return remod.escape |
2169 return remod.escape |
1972 |
2170 |
2171 |
|
1973 re = _re() |
2172 re = _re() |
1974 |
2173 |
1975 _fspathcache = {} |
2174 _fspathcache = {} |
2175 |
|
2176 |
|
1976 def fspath(name, root): |
2177 def fspath(name, root): |
1977 '''Get name in the case stored in the filesystem |
2178 '''Get name in the case stored in the filesystem |
1978 |
2179 |
1979 The name should be relative to root, and be normcase-ed for efficiency. |
2180 The name should be relative to root, and be normcase-ed for efficiency. |
1980 |
2181 |
1981 Note that this function is unnecessary, and should not be |
2182 Note that this function is unnecessary, and should not be |
1982 called, for case-sensitive filesystems (simply because it's expensive). |
2183 called, for case-sensitive filesystems (simply because it's expensive). |
1983 |
2184 |
1984 The root should be normcase-ed, too. |
2185 The root should be normcase-ed, too. |
1985 ''' |
2186 ''' |
2187 |
|
1986 def _makefspathcacheentry(dir): |
2188 def _makefspathcacheentry(dir): |
1987 return dict((normcase(n), n) for n in os.listdir(dir)) |
2189 return dict((normcase(n), n) for n in os.listdir(dir)) |
1988 |
2190 |
1989 seps = pycompat.ossep |
2191 seps = pycompat.ossep |
1990 if pycompat.osaltsep: |
2192 if pycompat.osaltsep: |
1991 seps = seps + pycompat.osaltsep |
2193 seps = seps + pycompat.osaltsep |
1992 # Protect backslashes. This gets silly very quickly. |
2194 # Protect backslashes. This gets silly very quickly. |
1993 seps.replace('\\','\\\\') |
2195 seps.replace('\\', '\\\\') |
1994 pattern = remod.compile(br'([^%s]+)|([%s]+)' % (seps, seps)) |
2196 pattern = remod.compile(br'([^%s]+)|([%s]+)' % (seps, seps)) |
1995 dir = os.path.normpath(root) |
2197 dir = os.path.normpath(root) |
1996 result = [] |
2198 result = [] |
1997 for part, sep in pattern.findall(name): |
2199 for part, sep in pattern.findall(name): |
1998 if sep: |
2200 if sep: |
2013 result.append(found or part) |
2215 result.append(found or part) |
2014 dir = os.path.join(dir, part) |
2216 dir = os.path.join(dir, part) |
2015 |
2217 |
2016 return ''.join(result) |
2218 return ''.join(result) |
2017 |
2219 |
2220 |
|
2018 def checknlink(testfile): |
2221 def checknlink(testfile): |
2019 '''check whether hardlink count reporting works properly''' |
2222 '''check whether hardlink count reporting works properly''' |
2020 |
2223 |
2021 # testfile may be open, so we need a separate file for checking to |
2224 # testfile may be open, so we need a separate file for checking to |
2022 # work around issue2543 (or testfile may get lost on Samba shares) |
2225 # work around issue2543 (or testfile may get lost on Samba shares) |
2023 f1, f2, fp = None, None, None |
2226 f1, f2, fp = None, None, None |
2024 try: |
2227 try: |
2025 fd, f1 = pycompat.mkstemp(prefix='.%s-' % os.path.basename(testfile), |
2228 fd, f1 = pycompat.mkstemp( |
2026 suffix='1~', dir=os.path.dirname(testfile)) |
2229 prefix='.%s-' % os.path.basename(testfile), |
2230 suffix='1~', |
|
2231 dir=os.path.dirname(testfile), |
|
2232 ) |
|
2027 os.close(fd) |
2233 os.close(fd) |
2028 f2 = '%s2~' % f1[:-2] |
2234 f2 = '%s2~' % f1[:-2] |
2029 |
2235 |
2030 oslink(f1, f2) |
2236 oslink(f1, f2) |
2031 # nlinks() may behave differently for files on Windows shares if |
2237 # nlinks() may behave differently for files on Windows shares if |
2042 if f is not None: |
2248 if f is not None: |
2043 os.unlink(f) |
2249 os.unlink(f) |
2044 except OSError: |
2250 except OSError: |
2045 pass |
2251 pass |
2046 |
2252 |
2253 |
|
2047 def endswithsep(path): |
2254 def endswithsep(path): |
2048 '''Check path ends with os.sep or os.altsep.''' |
2255 '''Check path ends with os.sep or os.altsep.''' |
2049 return (path.endswith(pycompat.ossep) |
2256 return ( |
2050 or pycompat.osaltsep and path.endswith(pycompat.osaltsep)) |
2257 path.endswith(pycompat.ossep) |
2258 or pycompat.osaltsep |
|
2259 and path.endswith(pycompat.osaltsep) |
|
2260 ) |
|
2261 |
|
2051 |
2262 |
2052 def splitpath(path): |
2263 def splitpath(path): |
2053 '''Split path by os.sep. |
2264 '''Split path by os.sep. |
2054 Note that this function does not use os.altsep because this is |
2265 Note that this function does not use os.altsep because this is |
2055 an alternative of simple "xxx.split(os.sep)". |
2266 an alternative of simple "xxx.split(os.sep)". |
2056 It is recommended to use os.path.normpath() before using this |
2267 It is recommended to use os.path.normpath() before using this |
2057 function if need.''' |
2268 function if need.''' |
2058 return path.split(pycompat.ossep) |
2269 return path.split(pycompat.ossep) |
2270 |
|
2059 |
2271 |
2060 def mktempcopy(name, emptyok=False, createmode=None, enforcewritable=False): |
2272 def mktempcopy(name, emptyok=False, createmode=None, enforcewritable=False): |
2061 """Create a temporary file with the same contents from name |
2273 """Create a temporary file with the same contents from name |
2062 |
2274 |
2063 The permission bits are copied from the original file. |
2275 The permission bits are copied from the original file. |
2089 ofp = posixfile(temp, "wb") |
2301 ofp = posixfile(temp, "wb") |
2090 for chunk in filechunkiter(ifp): |
2302 for chunk in filechunkiter(ifp): |
2091 ofp.write(chunk) |
2303 ofp.write(chunk) |
2092 ifp.close() |
2304 ifp.close() |
2093 ofp.close() |
2305 ofp.close() |
2094 except: # re-raises |
2306 except: # re-raises |
2095 try: |
2307 try: |
2096 os.unlink(temp) |
2308 os.unlink(temp) |
2097 except OSError: |
2309 except OSError: |
2098 pass |
2310 pass |
2099 raise |
2311 raise |
2100 return temp |
2312 return temp |
2101 |
2313 |
2314 |
|
2102 class filestat(object): |
2315 class filestat(object): |
2103 """help to exactly detect change of a file |
2316 """help to exactly detect change of a file |
2104 |
2317 |
2105 'stat' attribute is result of 'os.stat()' if specified 'path' |
2318 'stat' attribute is result of 'os.stat()' if specified 'path' |
2106 exists. Otherwise, it is None. This can avoid preparative |
2319 exists. Otherwise, it is None. This can avoid preparative |
2107 'exists()' examination on client side of this class. |
2320 'exists()' examination on client side of this class. |
2108 """ |
2321 """ |
2322 |
|
2109 def __init__(self, stat): |
2323 def __init__(self, stat): |
2110 self.stat = stat |
2324 self.stat = stat |
2111 |
2325 |
2112 @classmethod |
2326 @classmethod |
2113 def frompath(cls, path): |
2327 def frompath(cls, path): |
2129 def __eq__(self, old): |
2343 def __eq__(self, old): |
2130 try: |
2344 try: |
2131 # if ambiguity between stat of new and old file is |
2345 # if ambiguity between stat of new and old file is |
2132 # avoided, comparison of size, ctime and mtime is enough |
2346 # avoided, comparison of size, ctime and mtime is enough |
2133 # to exactly detect change of a file regardless of platform |
2347 # to exactly detect change of a file regardless of platform |
2134 return (self.stat.st_size == old.stat.st_size and |
2348 return ( |
2135 self.stat[stat.ST_CTIME] == old.stat[stat.ST_CTIME] and |
2349 self.stat.st_size == old.stat.st_size |
2136 self.stat[stat.ST_MTIME] == old.stat[stat.ST_MTIME]) |
2350 and self.stat[stat.ST_CTIME] == old.stat[stat.ST_CTIME] |
2351 and self.stat[stat.ST_MTIME] == old.stat[stat.ST_MTIME] |
|
2352 ) |
|
2137 except AttributeError: |
2353 except AttributeError: |
2138 pass |
2354 pass |
2139 try: |
2355 try: |
2140 return self.stat is None and old.stat is None |
2356 return self.stat is None and old.stat is None |
2141 except AttributeError: |
2357 except AttributeError: |
2170 |
2386 |
2171 Advancing mtime "if isambig(oldstat)" ensures "S[n-1].mtime != |
2387 Advancing mtime "if isambig(oldstat)" ensures "S[n-1].mtime != |
2172 S[n].mtime", even if size of a file isn't changed. |
2388 S[n].mtime", even if size of a file isn't changed. |
2173 """ |
2389 """ |
2174 try: |
2390 try: |
2175 return (self.stat[stat.ST_CTIME] == old.stat[stat.ST_CTIME]) |
2391 return self.stat[stat.ST_CTIME] == old.stat[stat.ST_CTIME] |
2176 except AttributeError: |
2392 except AttributeError: |
2177 return False |
2393 return False |
2178 |
2394 |
2179 def avoidambig(self, path, old): |
2395 def avoidambig(self, path, old): |
2180 """Change file stat of specified path to avoid ambiguity |
2396 """Change file stat of specified path to avoid ambiguity |
2185 appropriate privileges for 'path'. This returns False in this |
2401 appropriate privileges for 'path'. This returns False in this |
2186 case. |
2402 case. |
2187 |
2403 |
2188 Otherwise, this returns True, as "ambiguity is avoided". |
2404 Otherwise, this returns True, as "ambiguity is avoided". |
2189 """ |
2405 """ |
2190 advanced = (old.stat[stat.ST_MTIME] + 1) & 0x7fffffff |
2406 advanced = (old.stat[stat.ST_MTIME] + 1) & 0x7FFFFFFF |
2191 try: |
2407 try: |
2192 os.utime(path, (advanced, advanced)) |
2408 os.utime(path, (advanced, advanced)) |
2193 except OSError as inst: |
2409 except OSError as inst: |
2194 if inst.errno == errno.EPERM: |
2410 if inst.errno == errno.EPERM: |
2195 # utime() on the file created by another user causes EPERM, |
2411 # utime() on the file created by another user causes EPERM, |
2199 return True |
2415 return True |
2200 |
2416 |
2201 def __ne__(self, other): |
2417 def __ne__(self, other): |
2202 return not self == other |
2418 return not self == other |
2203 |
2419 |
2420 |
|
2204 class atomictempfile(object): |
2421 class atomictempfile(object): |
2205 '''writable file object that atomically updates a file |
2422 '''writable file object that atomically updates a file |
2206 |
2423 |
2207 All writes will go to a temporary copy of the original file. Call |
2424 All writes will go to a temporary copy of the original file. Call |
2208 close() when you are done writing, and atomictempfile will rename |
2425 close() when you are done writing, and atomictempfile will rename |
2212 |
2429 |
2213 checkambig argument of constructor is used with filestat, and is |
2430 checkambig argument of constructor is used with filestat, and is |
2214 useful only if target file is guarded by any lock (e.g. repo.lock |
2431 useful only if target file is guarded by any lock (e.g. repo.lock |
2215 or repo.wlock). |
2432 or repo.wlock). |
2216 ''' |
2433 ''' |
2434 |
|
2217 def __init__(self, name, mode='w+b', createmode=None, checkambig=False): |
2435 def __init__(self, name, mode='w+b', createmode=None, checkambig=False): |
2218 self.__name = name # permanent name |
2436 self.__name = name # permanent name |
2219 self._tempname = mktempcopy(name, emptyok=('w' in mode), |
2437 self._tempname = mktempcopy( |
2220 createmode=createmode, |
2438 name, |
2221 enforcewritable=('w' in mode)) |
2439 emptyok=('w' in mode), |
2440 createmode=createmode, |
|
2441 enforcewritable=('w' in mode), |
|
2442 ) |
|
2222 |
2443 |
2223 self._fp = posixfile(self._tempname, mode) |
2444 self._fp = posixfile(self._tempname, mode) |
2224 self._checkambig = checkambig |
2445 self._checkambig = checkambig |
2225 |
2446 |
2226 # delegated methods |
2447 # delegated methods |
2238 if oldstat and oldstat.stat: |
2459 if oldstat and oldstat.stat: |
2239 rename(self._tempname, filename) |
2460 rename(self._tempname, filename) |
2240 newstat = filestat.frompath(filename) |
2461 newstat = filestat.frompath(filename) |
2241 if newstat.isambig(oldstat): |
2462 if newstat.isambig(oldstat): |
2242 # stat of changed file is ambiguous to original one |
2463 # stat of changed file is ambiguous to original one |
2243 advanced = (oldstat.stat[stat.ST_MTIME] + 1) & 0x7fffffff |
2464 advanced = (oldstat.stat[stat.ST_MTIME] + 1) & 0x7FFFFFFF |
2244 os.utime(filename, (advanced, advanced)) |
2465 os.utime(filename, (advanced, advanced)) |
2245 else: |
2466 else: |
2246 rename(self._tempname, filename) |
2467 rename(self._tempname, filename) |
2247 |
2468 |
2248 def discard(self): |
2469 def discard(self): |
2252 except OSError: |
2473 except OSError: |
2253 pass |
2474 pass |
2254 self._fp.close() |
2475 self._fp.close() |
2255 |
2476 |
2256 def __del__(self): |
2477 def __del__(self): |
2257 if safehasattr(self, '_fp'): # constructor actually did something |
2478 if safehasattr(self, '_fp'): # constructor actually did something |
2258 self.discard() |
2479 self.discard() |
2259 |
2480 |
2260 def __enter__(self): |
2481 def __enter__(self): |
2261 return self |
2482 return self |
2262 |
2483 |
2263 def __exit__(self, exctype, excvalue, traceback): |
2484 def __exit__(self, exctype, excvalue, traceback): |
2264 if exctype is not None: |
2485 if exctype is not None: |
2265 self.discard() |
2486 self.discard() |
2266 else: |
2487 else: |
2267 self.close() |
2488 self.close() |
2489 |
|
2268 |
2490 |
2269 def unlinkpath(f, ignoremissing=False, rmdir=True): |
2491 def unlinkpath(f, ignoremissing=False, rmdir=True): |
2270 """unlink and remove the directory if it is empty""" |
2492 """unlink and remove the directory if it is empty""" |
2271 if ignoremissing: |
2493 if ignoremissing: |
2272 tryunlink(f) |
2494 tryunlink(f) |
2277 try: |
2499 try: |
2278 removedirs(os.path.dirname(f)) |
2500 removedirs(os.path.dirname(f)) |
2279 except OSError: |
2501 except OSError: |
2280 pass |
2502 pass |
2281 |
2503 |
2504 |
|
2282 def tryunlink(f): |
2505 def tryunlink(f): |
2283 """Attempt to remove a file, ignoring ENOENT errors.""" |
2506 """Attempt to remove a file, ignoring ENOENT errors.""" |
2284 try: |
2507 try: |
2285 unlink(f) |
2508 unlink(f) |
2286 except OSError as e: |
2509 except OSError as e: |
2287 if e.errno != errno.ENOENT: |
2510 if e.errno != errno.ENOENT: |
2288 raise |
2511 raise |
2512 |
|
2289 |
2513 |
2290 def makedirs(name, mode=None, notindexed=False): |
2514 def makedirs(name, mode=None, notindexed=False): |
2291 """recursive directory creation with parent mode inheritance |
2515 """recursive directory creation with parent mode inheritance |
2292 |
2516 |
2293 Newly created directories are marked as "not to be indexed by |
2517 Newly created directories are marked as "not to be indexed by |
2313 return |
2537 return |
2314 raise |
2538 raise |
2315 if mode is not None: |
2539 if mode is not None: |
2316 os.chmod(name, mode) |
2540 os.chmod(name, mode) |
2317 |
2541 |
2542 |
|
2318 def readfile(path): |
2543 def readfile(path): |
2319 with open(path, 'rb') as fp: |
2544 with open(path, 'rb') as fp: |
2320 return fp.read() |
2545 return fp.read() |
2321 |
2546 |
2547 |
|
2322 def writefile(path, text): |
2548 def writefile(path, text): |
2323 with open(path, 'wb') as fp: |
2549 with open(path, 'wb') as fp: |
2324 fp.write(text) |
2550 fp.write(text) |
2325 |
2551 |
2552 |
|
2326 def appendfile(path, text): |
2553 def appendfile(path, text): |
2327 with open(path, 'ab') as fp: |
2554 with open(path, 'ab') as fp: |
2328 fp.write(text) |
2555 fp.write(text) |
2329 |
2556 |
2557 |
|
2330 class chunkbuffer(object): |
2558 class chunkbuffer(object): |
2331 """Allow arbitrary sized chunks of data to be efficiently read from an |
2559 """Allow arbitrary sized chunks of data to be efficiently read from an |
2332 iterator over chunks of arbitrary size.""" |
2560 iterator over chunks of arbitrary size.""" |
2333 |
2561 |
2334 def __init__(self, in_iter): |
2562 def __init__(self, in_iter): |
2335 """in_iter is the iterator that's iterating over the input chunks.""" |
2563 """in_iter is the iterator that's iterating over the input chunks.""" |
2564 |
|
2336 def splitbig(chunks): |
2565 def splitbig(chunks): |
2337 for chunk in chunks: |
2566 for chunk in chunks: |
2338 if len(chunk) > 2**20: |
2567 if len(chunk) > 2 ** 20: |
2339 pos = 0 |
2568 pos = 0 |
2340 while pos < len(chunk): |
2569 while pos < len(chunk): |
2341 end = pos + 2 ** 18 |
2570 end = pos + 2 ** 18 |
2342 yield chunk[pos:end] |
2571 yield chunk[pos:end] |
2343 pos = end |
2572 pos = end |
2344 else: |
2573 else: |
2345 yield chunk |
2574 yield chunk |
2575 |
|
2346 self.iter = splitbig(in_iter) |
2576 self.iter = splitbig(in_iter) |
2347 self._queue = collections.deque() |
2577 self._queue = collections.deque() |
2348 self._chunkoffset = 0 |
2578 self._chunkoffset = 0 |
2349 |
2579 |
2350 def read(self, l=None): |
2580 def read(self, l=None): |
2359 buf = [] |
2589 buf = [] |
2360 queue = self._queue |
2590 queue = self._queue |
2361 while left > 0: |
2591 while left > 0: |
2362 # refill the queue |
2592 # refill the queue |
2363 if not queue: |
2593 if not queue: |
2364 target = 2**18 |
2594 target = 2 ** 18 |
2365 for chunk in self.iter: |
2595 for chunk in self.iter: |
2366 queue.append(chunk) |
2596 queue.append(chunk) |
2367 target -= len(chunk) |
2597 target -= len(chunk) |
2368 if target <= 0: |
2598 if target <= 0: |
2369 break |
2599 break |
2399 buf.append(chunk[offset:]) |
2629 buf.append(chunk[offset:]) |
2400 self._chunkoffset = 0 |
2630 self._chunkoffset = 0 |
2401 |
2631 |
2402 # Partial chunk needed. |
2632 # Partial chunk needed. |
2403 else: |
2633 else: |
2404 buf.append(chunk[offset:offset + left]) |
2634 buf.append(chunk[offset : offset + left]) |
2405 self._chunkoffset += left |
2635 self._chunkoffset += left |
2406 left -= chunkremaining |
2636 left -= chunkremaining |
2407 |
2637 |
2408 return ''.join(buf) |
2638 return ''.join(buf) |
2639 |
|
2409 |
2640 |
2410 def filechunkiter(f, size=131072, limit=None): |
2641 def filechunkiter(f, size=131072, limit=None): |
2411 """Create a generator that produces the data in the file size |
2642 """Create a generator that produces the data in the file size |
2412 (default 131072) bytes at a time, up to optional limit (default is |
2643 (default 131072) bytes at a time, up to optional limit (default is |
2413 to read all data). Chunks may be less than size bytes if the |
2644 to read all data). Chunks may be less than size bytes if the |
2426 break |
2657 break |
2427 if limit: |
2658 if limit: |
2428 limit -= len(s) |
2659 limit -= len(s) |
2429 yield s |
2660 yield s |
2430 |
2661 |
2662 |
|
2431 class cappedreader(object): |
2663 class cappedreader(object): |
2432 """A file object proxy that allows reading up to N bytes. |
2664 """A file object proxy that allows reading up to N bytes. |
2433 |
2665 |
2434 Given a source file object, instances of this type allow reading up to |
2666 Given a source file object, instances of this type allow reading up to |
2435 N bytes from that source file object. Attempts to read past the allowed |
2667 N bytes from that source file object. Attempts to read past the allowed |
2437 |
2669 |
2438 It is assumed that I/O is not performed on the original file object |
2670 It is assumed that I/O is not performed on the original file object |
2439 in addition to I/O that is performed by this instance. If there is, |
2671 in addition to I/O that is performed by this instance. If there is, |
2440 state tracking will get out of sync and unexpected results will ensue. |
2672 state tracking will get out of sync and unexpected results will ensue. |
2441 """ |
2673 """ |
2674 |
|
2442 def __init__(self, fh, limit): |
2675 def __init__(self, fh, limit): |
2443 """Allow reading up to <limit> bytes from <fh>.""" |
2676 """Allow reading up to <limit> bytes from <fh>.""" |
2444 self._fh = fh |
2677 self._fh = fh |
2445 self._left = limit |
2678 self._left = limit |
2446 |
2679 |
2460 def readinto(self, b): |
2693 def readinto(self, b): |
2461 res = self.read(len(b)) |
2694 res = self.read(len(b)) |
2462 if res is None: |
2695 if res is None: |
2463 return None |
2696 return None |
2464 |
2697 |
2465 b[0:len(res)] = res |
2698 b[0 : len(res)] = res |
2466 return len(res) |
2699 return len(res) |
2700 |
|
2467 |
2701 |
2468 def unitcountfn(*unittable): |
2702 def unitcountfn(*unittable): |
2469 '''return a function that renders a readable count of some quantity''' |
2703 '''return a function that renders a readable count of some quantity''' |
2470 |
2704 |
2471 def go(count): |
2705 def go(count): |
2473 if abs(count) >= divisor * multiplier: |
2707 if abs(count) >= divisor * multiplier: |
2474 return format % (count / float(divisor)) |
2708 return format % (count / float(divisor)) |
2475 return unittable[-1][2] % count |
2709 return unittable[-1][2] % count |
2476 |
2710 |
2477 return go |
2711 return go |
2712 |
|
2478 |
2713 |
2479 def processlinerange(fromline, toline): |
2714 def processlinerange(fromline, toline): |
2480 """Check that linerange <fromline>:<toline> makes sense and return a |
2715 """Check that linerange <fromline>:<toline> makes sense and return a |
2481 0-based range. |
2716 0-based range. |
2482 |
2717 |
2495 raise error.ParseError(_("line range must be positive")) |
2730 raise error.ParseError(_("line range must be positive")) |
2496 if fromline < 1: |
2731 if fromline < 1: |
2497 raise error.ParseError(_("fromline must be strictly positive")) |
2732 raise error.ParseError(_("fromline must be strictly positive")) |
2498 return fromline - 1, toline |
2733 return fromline - 1, toline |
2499 |
2734 |
2735 |
|
2500 bytecount = unitcountfn( |
2736 bytecount = unitcountfn( |
2501 (100, 1 << 30, _('%.0f GB')), |
2737 (100, 1 << 30, _('%.0f GB')), |
2502 (10, 1 << 30, _('%.1f GB')), |
2738 (10, 1 << 30, _('%.1f GB')), |
2503 (1, 1 << 30, _('%.2f GB')), |
2739 (1, 1 << 30, _('%.2f GB')), |
2504 (100, 1 << 20, _('%.0f MB')), |
2740 (100, 1 << 20, _('%.0f MB')), |
2506 (1, 1 << 20, _('%.2f MB')), |
2742 (1, 1 << 20, _('%.2f MB')), |
2507 (100, 1 << 10, _('%.0f KB')), |
2743 (100, 1 << 10, _('%.0f KB')), |
2508 (10, 1 << 10, _('%.1f KB')), |
2744 (10, 1 << 10, _('%.1f KB')), |
2509 (1, 1 << 10, _('%.2f KB')), |
2745 (1, 1 << 10, _('%.2f KB')), |
2510 (1, 1, _('%.0f bytes')), |
2746 (1, 1, _('%.0f bytes')), |
2511 ) |
2747 ) |
2748 |
|
2512 |
2749 |
2513 class transformingwriter(object): |
2750 class transformingwriter(object): |
2514 """Writable file wrapper to transform data by function""" |
2751 """Writable file wrapper to transform data by function""" |
2515 |
2752 |
2516 def __init__(self, fp, encode): |
2753 def __init__(self, fp, encode): |
2523 def flush(self): |
2760 def flush(self): |
2524 self._fp.flush() |
2761 self._fp.flush() |
2525 |
2762 |
2526 def write(self, data): |
2763 def write(self, data): |
2527 return self._fp.write(self._encode(data)) |
2764 return self._fp.write(self._encode(data)) |
2765 |
|
2528 |
2766 |
2529 # Matches a single EOL which can either be a CRLF where repeated CR |
2767 # Matches a single EOL which can either be a CRLF where repeated CR |
2530 # are removed or a LF. We do not care about old Macintosh files, so a |
2768 # are removed or a LF. We do not care about old Macintosh files, so a |
2531 # stray CR is an error. |
2769 # stray CR is an error. |
2532 _eolre = remod.compile(br'\r*\n') |
2770 _eolre = remod.compile(br'\r*\n') |
2533 |
2771 |
2772 |
|
2534 def tolf(s): |
2773 def tolf(s): |
2535 return _eolre.sub('\n', s) |
2774 return _eolre.sub('\n', s) |
2536 |
2775 |
2776 |
|
2537 def tocrlf(s): |
2777 def tocrlf(s): |
2538 return _eolre.sub('\r\n', s) |
2778 return _eolre.sub('\r\n', s) |
2539 |
2779 |
2780 |
|
2540 def _crlfwriter(fp): |
2781 def _crlfwriter(fp): |
2541 return transformingwriter(fp, tocrlf) |
2782 return transformingwriter(fp, tocrlf) |
2783 |
|
2542 |
2784 |
2543 if pycompat.oslinesep == '\r\n': |
2785 if pycompat.oslinesep == '\r\n': |
2544 tonativeeol = tocrlf |
2786 tonativeeol = tocrlf |
2545 fromnativeeol = tolf |
2787 fromnativeeol = tolf |
2546 nativeeolwriter = _crlfwriter |
2788 nativeeolwriter = _crlfwriter |
2547 else: |
2789 else: |
2548 tonativeeol = pycompat.identity |
2790 tonativeeol = pycompat.identity |
2549 fromnativeeol = pycompat.identity |
2791 fromnativeeol = pycompat.identity |
2550 nativeeolwriter = pycompat.identity |
2792 nativeeolwriter = pycompat.identity |
2551 |
2793 |
2552 if (pyplatform.python_implementation() == 'CPython' and |
2794 if pyplatform.python_implementation() == 'CPython' and sys.version_info < ( |
2553 sys.version_info < (3, 0)): |
2795 3, |
2796 0, |
|
2797 ): |
|
2554 # There is an issue in CPython that some IO methods do not handle EINTR |
2798 # There is an issue in CPython that some IO methods do not handle EINTR |
2555 # correctly. The following table shows what CPython version (and functions) |
2799 # correctly. The following table shows what CPython version (and functions) |
2556 # are affected (buggy: has the EINTR bug, okay: otherwise): |
2800 # are affected (buggy: has the EINTR bug, okay: otherwise): |
2557 # |
2801 # |
2558 # | < 2.7.4 | 2.7.4 to 2.7.12 | >= 3.0 |
2802 # | < 2.7.4 | 2.7.4 to 2.7.12 | >= 3.0 |
2577 # to minimize the performance impact. |
2821 # to minimize the performance impact. |
2578 if sys.version_info >= (2, 7, 4): |
2822 if sys.version_info >= (2, 7, 4): |
2579 # fp.readline deals with EINTR correctly, use it as a workaround. |
2823 # fp.readline deals with EINTR correctly, use it as a workaround. |
2580 def _safeiterfile(fp): |
2824 def _safeiterfile(fp): |
2581 return iter(fp.readline, '') |
2825 return iter(fp.readline, '') |
2826 |
|
2582 else: |
2827 else: |
2583 # fp.read* are broken too, manually deal with EINTR in a stupid way. |
2828 # fp.read* are broken too, manually deal with EINTR in a stupid way. |
2584 # note: this may block longer than necessary because of bufsize. |
2829 # note: this may block longer than necessary because of bufsize. |
2585 def _safeiterfile(fp, bufsize=4096): |
2830 def _safeiterfile(fp, bufsize=4096): |
2586 fd = fp.fileno() |
2831 fd = fp.fileno() |
2614 fastpath = stat.S_ISREG(os.fstat(fp.fileno()).st_mode) |
2859 fastpath = stat.S_ISREG(os.fstat(fp.fileno()).st_mode) |
2615 if fastpath: |
2860 if fastpath: |
2616 return fp |
2861 return fp |
2617 else: |
2862 else: |
2618 return _safeiterfile(fp) |
2863 return _safeiterfile(fp) |
2864 |
|
2865 |
|
2619 else: |
2866 else: |
2620 # PyPy and CPython 3 do not have the EINTR issue thus no workaround needed. |
2867 # PyPy and CPython 3 do not have the EINTR issue thus no workaround needed. |
2621 def iterfile(fp): |
2868 def iterfile(fp): |
2622 return fp |
2869 return fp |
2623 |
2870 |
2871 |
|
2624 def iterlines(iterator): |
2872 def iterlines(iterator): |
2625 for chunk in iterator: |
2873 for chunk in iterator: |
2626 for line in chunk.splitlines(): |
2874 for line in chunk.splitlines(): |
2627 yield line |
2875 yield line |
2628 |
2876 |
2877 |
|
2629 def expandpath(path): |
2878 def expandpath(path): |
2630 return os.path.expanduser(os.path.expandvars(path)) |
2879 return os.path.expanduser(os.path.expandvars(path)) |
2880 |
|
2631 |
2881 |
2632 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False): |
2882 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False): |
2633 """Return the result of interpolating items in the mapping into string s. |
2883 """Return the result of interpolating items in the mapping into string s. |
2634 |
2884 |
2635 prefix is a single character string, or a two character string with |
2885 prefix is a single character string, or a two character string with |
2652 prefix_char = prefix |
2902 prefix_char = prefix |
2653 mapping[prefix_char] = prefix_char |
2903 mapping[prefix_char] = prefix_char |
2654 r = remod.compile(br'%s(%s)' % (prefix, patterns)) |
2904 r = remod.compile(br'%s(%s)' % (prefix, patterns)) |
2655 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s) |
2905 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s) |
2656 |
2906 |
2907 |
|
2657 def getport(port): |
2908 def getport(port): |
2658 """Return the port for a given network service. |
2909 """Return the port for a given network service. |
2659 |
2910 |
2660 If port is an integer, it's returned as is. If it's a string, it's |
2911 If port is an integer, it's returned as is. If it's a string, it's |
2661 looked up using socket.getservbyname(). If there's no matching |
2912 looked up using socket.getservbyname(). If there's no matching |
2667 pass |
2918 pass |
2668 |
2919 |
2669 try: |
2920 try: |
2670 return socket.getservbyname(pycompat.sysstr(port)) |
2921 return socket.getservbyname(pycompat.sysstr(port)) |
2671 except socket.error: |
2922 except socket.error: |
2672 raise error.Abort(_("no port number associated with service '%s'") |
2923 raise error.Abort( |
2673 % port) |
2924 _("no port number associated with service '%s'") % port |
2925 ) |
|
2926 |
|
2674 |
2927 |
2675 class url(object): |
2928 class url(object): |
2676 r"""Reliable URL parser. |
2929 r"""Reliable URL parser. |
2677 |
2930 |
2678 This parses URLs and provides attributes for the following |
2931 This parses URLs and provides attributes for the following |
2820 self.user, self.passwd = self.user.split(':', 1) |
3073 self.user, self.passwd = self.user.split(':', 1) |
2821 if not self.host: |
3074 if not self.host: |
2822 self.host = None |
3075 self.host = None |
2823 |
3076 |
2824 # Don't split on colons in IPv6 addresses without ports |
3077 # Don't split on colons in IPv6 addresses without ports |
2825 if (self.host and ':' in self.host and |
3078 if ( |
2826 not (self.host.startswith('[') and self.host.endswith(']'))): |
3079 self.host |
3080 and ':' in self.host |
|
3081 and not (self.host.startswith('[') and self.host.endswith(']')) |
|
3082 ): |
|
2827 self._hostport = self.host |
3083 self._hostport = self.host |
2828 self.host, self.port = self.host.rsplit(':', 1) |
3084 self.host, self.port = self.host.rsplit(':', 1) |
2829 if not self.host: |
3085 if not self.host: |
2830 self.host = None |
3086 self.host = None |
2831 |
3087 |
2832 if (self.host and self.scheme == 'file' and |
3088 if ( |
2833 self.host not in ('localhost', '127.0.0.1', '[::1]')): |
3089 self.host |
3090 and self.scheme == 'file' |
|
3091 and self.host not in ('localhost', '127.0.0.1', '[::1]') |
|
3092 ): |
|
2834 raise error.Abort(_('file:// URLs can only refer to localhost')) |
3093 raise error.Abort(_('file:// URLs can only refer to localhost')) |
2835 |
3094 |
2836 self.path = path |
3095 self.path = path |
2837 |
3096 |
2838 # leave the query string escaped |
3097 # leave the query string escaped |
2839 for a in ('user', 'passwd', 'host', 'port', |
3098 for a in ('user', 'passwd', 'host', 'port', 'path', 'fragment'): |
2840 'path', 'fragment'): |
|
2841 v = getattr(self, a) |
3099 v = getattr(self, a) |
2842 if v is not None: |
3100 if v is not None: |
2843 setattr(self, a, urlreq.unquote(v)) |
3101 setattr(self, a, urlreq.unquote(v)) |
2844 |
3102 |
2845 @encoding.strmethod |
3103 @encoding.strmethod |
2846 def __repr__(self): |
3104 def __repr__(self): |
2847 attrs = [] |
3105 attrs = [] |
2848 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path', |
3106 for a in ( |
2849 'query', 'fragment'): |
3107 'scheme', |
3108 'user', |
|
3109 'passwd', |
|
3110 'host', |
|
3111 'port', |
|
3112 'path', |
|
3113 'query', |
|
3114 'fragment', |
|
3115 ): |
|
2850 v = getattr(self, a) |
3116 v = getattr(self, a) |
2851 if v is not None: |
3117 if v is not None: |
2852 attrs.append('%s: %r' % (a, pycompat.bytestr(v))) |
3118 attrs.append('%s: %r' % (a, pycompat.bytestr(v))) |
2853 return '<url %s>' % ', '.join(attrs) |
3119 return '<url %s>' % ', '.join(attrs) |
2854 |
3120 |
2895 return s |
3161 return s |
2896 |
3162 |
2897 s = self.scheme + ':' |
3163 s = self.scheme + ':' |
2898 if self.user or self.passwd or self.host: |
3164 if self.user or self.passwd or self.host: |
2899 s += '//' |
3165 s += '//' |
2900 elif self.scheme and (not self.path or self.path.startswith('/') |
3166 elif self.scheme and ( |
2901 or hasdriveletter(self.path)): |
3167 not self.path |
3168 or self.path.startswith('/') |
|
3169 or hasdriveletter(self.path) |
|
3170 ): |
|
2902 s += '//' |
3171 s += '//' |
2903 if hasdriveletter(self.path): |
3172 if hasdriveletter(self.path): |
2904 s += '/' |
3173 s += '/' |
2905 if self.user: |
3174 if self.user: |
2906 s += urlreq.quote(self.user, safe=self._safechars) |
3175 s += urlreq.quote(self.user, safe=self._safechars) |
2942 return (s, None) |
3211 return (s, None) |
2943 # authinfo[1] is passed to urllib2 password manager, and its |
3212 # authinfo[1] is passed to urllib2 password manager, and its |
2944 # URIs must not contain credentials. The host is passed in the |
3213 # URIs must not contain credentials. The host is passed in the |
2945 # URIs list because Python < 2.4.3 uses only that to search for |
3214 # URIs list because Python < 2.4.3 uses only that to search for |
2946 # a password. |
3215 # a password. |
2947 return (s, (None, (s, self.host), |
3216 return (s, (None, (s, self.host), self.user, self.passwd or '')) |
2948 self.user, self.passwd or '')) |
|
2949 |
3217 |
2950 def isabs(self): |
3218 def isabs(self): |
2951 if self.scheme and self.scheme != 'file': |
3219 if self.scheme and self.scheme != 'file': |
2952 return True # remote URL |
3220 return True # remote URL |
2953 if hasdriveletter(self.path): |
3221 if hasdriveletter(self.path): |
2954 return True # absolute for our purposes - can't be joined() |
3222 return True # absolute for our purposes - can't be joined() |
2955 if self.path.startswith(br'\\'): |
3223 if self.path.startswith(br'\\'): |
2956 return True # Windows UNC path |
3224 return True # Windows UNC path |
2957 if self.path.startswith('/'): |
3225 if self.path.startswith('/'): |
2958 return True # POSIX-style |
3226 return True # POSIX-style |
2959 return False |
3227 return False |
2960 |
3228 |
2961 def localpath(self): |
3229 def localpath(self): |
2962 if self.scheme == 'file' or self.scheme == 'bundle': |
3230 if self.scheme == 'file' or self.scheme == 'bundle': |
2963 path = self.path or '/' |
3231 path = self.path or '/' |
2964 # For Windows, we need to promote hosts containing drive |
3232 # For Windows, we need to promote hosts containing drive |
2965 # letters to paths with drive letters. |
3233 # letters to paths with drive letters. |
2966 if hasdriveletter(self._hostport): |
3234 if hasdriveletter(self._hostport): |
2967 path = self._hostport + '/' + self.path |
3235 path = self._hostport + '/' + self.path |
2968 elif (self.host is not None and self.path |
3236 elif ( |
2969 and not hasdriveletter(path)): |
3237 self.host is not None and self.path and not hasdriveletter(path) |
3238 ): |
|
2970 path = '/' + path |
3239 path = '/' + path |
2971 return path |
3240 return path |
2972 return self._origpath |
3241 return self._origpath |
2973 |
3242 |
2974 def islocal(self): |
3243 def islocal(self): |
2975 '''whether localpath will return something that posixfile can open''' |
3244 '''whether localpath will return something that posixfile can open''' |
2976 return (not self.scheme or self.scheme == 'file' |
3245 return ( |
2977 or self.scheme == 'bundle') |
3246 not self.scheme or self.scheme == 'file' or self.scheme == 'bundle' |
3247 ) |
|
3248 |
|
2978 |
3249 |
2979 def hasscheme(path): |
3250 def hasscheme(path): |
2980 return bool(url(path).scheme) |
3251 return bool(url(path).scheme) |
2981 |
3252 |
3253 |
|
2982 def hasdriveletter(path): |
3254 def hasdriveletter(path): |
2983 return path and path[1:2] == ':' and path[0:1].isalpha() |
3255 return path and path[1:2] == ':' and path[0:1].isalpha() |
2984 |
3256 |
3257 |
|
2985 def urllocalpath(path): |
3258 def urllocalpath(path): |
2986 return url(path, parsequery=False, parsefragment=False).localpath() |
3259 return url(path, parsequery=False, parsefragment=False).localpath() |
3260 |
|
2987 |
3261 |
2988 def checksafessh(path): |
3262 def checksafessh(path): |
2989 """check if a path / url is a potentially unsafe ssh exploit (SEC) |
3263 """check if a path / url is a potentially unsafe ssh exploit (SEC) |
2990 |
3264 |
2991 This is a sanity check for ssh urls. ssh will parse the first item as |
3265 This is a sanity check for ssh urls. ssh will parse the first item as |
2995 |
3269 |
2996 Raises an error.Abort when the url is unsafe. |
3270 Raises an error.Abort when the url is unsafe. |
2997 """ |
3271 """ |
2998 path = urlreq.unquote(path) |
3272 path = urlreq.unquote(path) |
2999 if path.startswith('ssh://-') or path.startswith('svn+ssh://-'): |
3273 if path.startswith('ssh://-') or path.startswith('svn+ssh://-'): |
3000 raise error.Abort(_('potentially unsafe url: %r') % |
3274 raise error.Abort( |
3001 (pycompat.bytestr(path),)) |
3275 _('potentially unsafe url: %r') % (pycompat.bytestr(path),) |
3276 ) |
|
3277 |
|
3002 |
3278 |
3003 def hidepassword(u): |
3279 def hidepassword(u): |
3004 '''hide user credential in a url string''' |
3280 '''hide user credential in a url string''' |
3005 u = url(u) |
3281 u = url(u) |
3006 if u.passwd: |
3282 if u.passwd: |
3007 u.passwd = '***' |
3283 u.passwd = '***' |
3008 return bytes(u) |
3284 return bytes(u) |
3009 |
3285 |
3286 |
|
3010 def removeauth(u): |
3287 def removeauth(u): |
3011 '''remove all authentication information from a url string''' |
3288 '''remove all authentication information from a url string''' |
3012 u = url(u) |
3289 u = url(u) |
3013 u.user = u.passwd = None |
3290 u.user = u.passwd = None |
3014 return bytes(u) |
3291 return bytes(u) |
3292 |
|
3015 |
3293 |
3016 timecount = unitcountfn( |
3294 timecount = unitcountfn( |
3017 (1, 1e3, _('%.0f s')), |
3295 (1, 1e3, _('%.0f s')), |
3018 (100, 1, _('%.1f s')), |
3296 (100, 1, _('%.1f s')), |
3019 (10, 1, _('%.2f s')), |
3297 (10, 1, _('%.2f s')), |
3025 (10, 0.000001, _('%.2f us')), |
3303 (10, 0.000001, _('%.2f us')), |
3026 (1, 0.000001, _('%.3f us')), |
3304 (1, 0.000001, _('%.3f us')), |
3027 (100, 0.000000001, _('%.1f ns')), |
3305 (100, 0.000000001, _('%.1f ns')), |
3028 (10, 0.000000001, _('%.2f ns')), |
3306 (10, 0.000000001, _('%.2f ns')), |
3029 (1, 0.000000001, _('%.3f ns')), |
3307 (1, 0.000000001, _('%.3f ns')), |
3030 ) |
3308 ) |
3309 |
|
3031 |
3310 |
3032 @attr.s |
3311 @attr.s |
3033 class timedcmstats(object): |
3312 class timedcmstats(object): |
3034 """Stats information produced by the timedcm context manager on entering.""" |
3313 """Stats information produced by the timedcm context manager on entering.""" |
3035 |
3314 |
3044 |
3323 |
3045 def __bytes__(self): |
3324 def __bytes__(self): |
3046 return timecount(self.elapsed) if self.elapsed else '<unknown>' |
3325 return timecount(self.elapsed) if self.elapsed else '<unknown>' |
3047 |
3326 |
3048 __str__ = encoding.strmethod(__bytes__) |
3327 __str__ = encoding.strmethod(__bytes__) |
3328 |
|
3049 |
3329 |
3050 @contextlib.contextmanager |
3330 @contextlib.contextmanager |
3051 def timedcm(whencefmt, *whenceargs): |
3331 def timedcm(whencefmt, *whenceargs): |
3052 """A context manager that produces timing information for a given context. |
3332 """A context manager that produces timing information for a given context. |
3053 |
3333 |
3064 yield timing_stats |
3344 yield timing_stats |
3065 finally: |
3345 finally: |
3066 timing_stats.elapsed = timer() - timing_stats.start |
3346 timing_stats.elapsed = timer() - timing_stats.start |
3067 timedcm._nested -= 1 |
3347 timedcm._nested -= 1 |
3068 |
3348 |
3349 |
|
3069 timedcm._nested = 0 |
3350 timedcm._nested = 0 |
3351 |
|
3070 |
3352 |
3071 def timed(func): |
3353 def timed(func): |
3072 '''Report the execution time of a function call to stderr. |
3354 '''Report the execution time of a function call to stderr. |
3073 |
3355 |
3074 During development, use as a decorator when you need to measure |
3356 During development, use as a decorator when you need to measure |
3081 |
3363 |
3082 def wrapper(*args, **kwargs): |
3364 def wrapper(*args, **kwargs): |
3083 with timedcm(pycompat.bytestr(func.__name__)) as time_stats: |
3365 with timedcm(pycompat.bytestr(func.__name__)) as time_stats: |
3084 result = func(*args, **kwargs) |
3366 result = func(*args, **kwargs) |
3085 stderr = procutil.stderr |
3367 stderr = procutil.stderr |
3086 stderr.write('%s%s: %s\n' % ( |
3368 stderr.write( |
3087 ' ' * time_stats.level * 2, pycompat.bytestr(func.__name__), |
3369 '%s%s: %s\n' |
3088 time_stats)) |
3370 % ( |
3371 ' ' * time_stats.level * 2, |
|
3372 pycompat.bytestr(func.__name__), |
|
3373 time_stats, |
|
3374 ) |
|
3375 ) |
|
3089 return result |
3376 return result |
3377 |
|
3090 return wrapper |
3378 return wrapper |
3091 |
3379 |
3092 _sizeunits = (('m', 2**20), ('k', 2**10), ('g', 2**30), |
3380 |
3093 ('kb', 2**10), ('mb', 2**20), ('gb', 2**30), ('b', 1)) |
3381 _sizeunits = ( |
3382 ('m', 2 ** 20), |
|
3383 ('k', 2 ** 10), |
|
3384 ('g', 2 ** 30), |
|
3385 ('kb', 2 ** 10), |
|
3386 ('mb', 2 ** 20), |
|
3387 ('gb', 2 ** 30), |
|
3388 ('b', 1), |
|
3389 ) |
|
3390 |
|
3094 |
3391 |
3095 def sizetoint(s): |
3392 def sizetoint(s): |
3096 '''Convert a space specifier to a byte count. |
3393 '''Convert a space specifier to a byte count. |
3097 |
3394 |
3098 >>> sizetoint(b'30') |
3395 >>> sizetoint(b'30') |
3104 ''' |
3401 ''' |
3105 t = s.strip().lower() |
3402 t = s.strip().lower() |
3106 try: |
3403 try: |
3107 for k, u in _sizeunits: |
3404 for k, u in _sizeunits: |
3108 if t.endswith(k): |
3405 if t.endswith(k): |
3109 return int(float(t[:-len(k)]) * u) |
3406 return int(float(t[: -len(k)]) * u) |
3110 return int(t) |
3407 return int(t) |
3111 except ValueError: |
3408 except ValueError: |
3112 raise error.ParseError(_("couldn't parse size: %s") % s) |
3409 raise error.ParseError(_("couldn't parse size: %s") % s) |
3410 |
|
3113 |
3411 |
3114 class hooks(object): |
3412 class hooks(object): |
3115 '''A collection of hook functions that can be used to extend a |
3413 '''A collection of hook functions that can be used to extend a |
3116 function's behavior. Hooks are called in lexicographic order, |
3414 function's behavior. Hooks are called in lexicographic order, |
3117 based on the names of their sources.''' |
3415 based on the names of their sources.''' |
3126 self._hooks.sort(key=lambda x: x[0]) |
3424 self._hooks.sort(key=lambda x: x[0]) |
3127 results = [] |
3425 results = [] |
3128 for source, hook in self._hooks: |
3426 for source, hook in self._hooks: |
3129 results.append(hook(*args)) |
3427 results.append(hook(*args)) |
3130 return results |
3428 return results |
3429 |
|
3131 |
3430 |
3132 def getstackframes(skip=0, line=' %-*s in %s\n', fileline='%s:%d', depth=0): |
3431 def getstackframes(skip=0, line=' %-*s in %s\n', fileline='%s:%d', depth=0): |
3133 '''Yields lines for a nicely formatted stacktrace. |
3432 '''Yields lines for a nicely formatted stacktrace. |
3134 Skips the 'skip' last entries, then return the last 'depth' entries. |
3433 Skips the 'skip' last entries, then return the last 'depth' entries. |
3135 Each file+linenumber is formatted according to fileline. |
3434 Each file+linenumber is formatted according to fileline. |
3139 filepath+linenumber, |
3438 filepath+linenumber, |
3140 function |
3439 function |
3141 |
3440 |
3142 Not be used in production code but very convenient while developing. |
3441 Not be used in production code but very convenient while developing. |
3143 ''' |
3442 ''' |
3144 entries = [(fileline % (pycompat.sysbytes(fn), ln), pycompat.sysbytes(func)) |
3443 entries = [ |
3145 for fn, ln, func, _text in traceback.extract_stack()[:-skip - 1] |
3444 (fileline % (pycompat.sysbytes(fn), ln), pycompat.sysbytes(func)) |
3146 ][-depth:] |
3445 for fn, ln, func, _text in traceback.extract_stack()[: -skip - 1] |
3446 ][-depth:] |
|
3147 if entries: |
3447 if entries: |
3148 fnmax = max(len(entry[0]) for entry in entries) |
3448 fnmax = max(len(entry[0]) for entry in entries) |
3149 for fnln, func in entries: |
3449 for fnln, func in entries: |
3150 if line is None: |
3450 if line is None: |
3151 yield (fnmax, fnln, func) |
3451 yield (fnmax, fnln, func) |
3152 else: |
3452 else: |
3153 yield line % (fnmax, fnln, func) |
3453 yield line % (fnmax, fnln, func) |
3154 |
3454 |
3155 def debugstacktrace(msg='stacktrace', skip=0, |
3455 |
3156 f=procutil.stderr, otherf=procutil.stdout, depth=0): |
3456 def debugstacktrace( |
3457 msg='stacktrace', skip=0, f=procutil.stderr, otherf=procutil.stdout, depth=0 |
|
3458 ): |
|
3157 '''Writes a message to f (stderr) with a nicely formatted stacktrace. |
3459 '''Writes a message to f (stderr) with a nicely formatted stacktrace. |
3158 Skips the 'skip' entries closest to the call, then show 'depth' entries. |
3460 Skips the 'skip' entries closest to the call, then show 'depth' entries. |
3159 By default it will flush stdout first. |
3461 By default it will flush stdout first. |
3160 It can be used everywhere and intentionally does not require an ui object. |
3462 It can be used everywhere and intentionally does not require an ui object. |
3161 Not be used in production code but very convenient while developing. |
3463 Not be used in production code but very convenient while developing. |
3165 f.write('%s at:\n' % msg.rstrip()) |
3467 f.write('%s at:\n' % msg.rstrip()) |
3166 for line in getstackframes(skip + 1, depth=depth): |
3468 for line in getstackframes(skip + 1, depth=depth): |
3167 f.write(line) |
3469 f.write(line) |
3168 f.flush() |
3470 f.flush() |
3169 |
3471 |
3472 |
|
3170 class dirs(object): |
3473 class dirs(object): |
3171 '''a multiset of directory names from a dirstate or manifest''' |
3474 '''a multiset of directory names from a dirstate or manifest''' |
3172 |
3475 |
3173 def __init__(self, map, skip=None): |
3476 def __init__(self, map, skip=None): |
3174 self._dirs = {} |
3477 self._dirs = {} |
3176 if isinstance(map, dict) and skip is not None: |
3479 if isinstance(map, dict) and skip is not None: |
3177 for f, s in map.iteritems(): |
3480 for f, s in map.iteritems(): |
3178 if s[0] != skip: |
3481 if s[0] != skip: |
3179 addpath(f) |
3482 addpath(f) |
3180 elif skip is not None: |
3483 elif skip is not None: |
3181 raise error.ProgrammingError("skip character is only supported " |
3484 raise error.ProgrammingError( |
3182 "with a dict source") |
3485 "skip character is only supported " "with a dict source" |
3486 ) |
|
3183 else: |
3487 else: |
3184 for f in map: |
3488 for f in map: |
3185 addpath(f) |
3489 addpath(f) |
3186 |
3490 |
3187 def addpath(self, path): |
3491 def addpath(self, path): |
3204 return iter(self._dirs) |
3508 return iter(self._dirs) |
3205 |
3509 |
3206 def __contains__(self, d): |
3510 def __contains__(self, d): |
3207 return d in self._dirs |
3511 return d in self._dirs |
3208 |
3512 |
3513 |
|
3209 if safehasattr(parsers, 'dirs'): |
3514 if safehasattr(parsers, 'dirs'): |
3210 dirs = parsers.dirs |
3515 dirs = parsers.dirs |
3211 |
3516 |
3212 if rustdirs is not None: |
3517 if rustdirs is not None: |
3213 dirs = rustdirs |
3518 dirs = rustdirs |
3519 |
|
3214 |
3520 |
3215 def finddirs(path): |
3521 def finddirs(path): |
3216 pos = path.rfind('/') |
3522 pos = path.rfind('/') |
3217 while pos != -1: |
3523 while pos != -1: |
3218 yield path[:pos] |
3524 yield path[:pos] |
3220 yield '' |
3526 yield '' |
3221 |
3527 |
3222 |
3528 |
3223 # convenient shortcut |
3529 # convenient shortcut |
3224 dst = debugstacktrace |
3530 dst = debugstacktrace |
3531 |
|
3225 |
3532 |
3226 def safename(f, tag, ctx, others=None): |
3533 def safename(f, tag, ctx, others=None): |
3227 """ |
3534 """ |
3228 Generate a name that it is safe to rename f to in the given context. |
3535 Generate a name that it is safe to rename f to in the given context. |
3229 |
3536 |
3244 for n in itertools.count(1): |
3551 for n in itertools.count(1): |
3245 fn = '%s~%s~%s' % (f, tag, n) |
3552 fn = '%s~%s~%s' % (f, tag, n) |
3246 if fn not in ctx and fn not in others: |
3553 if fn not in ctx and fn not in others: |
3247 return fn |
3554 return fn |
3248 |
3555 |
3556 |
|
3249 def readexactly(stream, n): |
3557 def readexactly(stream, n): |
3250 '''read n bytes from stream.read and abort if less was available''' |
3558 '''read n bytes from stream.read and abort if less was available''' |
3251 s = stream.read(n) |
3559 s = stream.read(n) |
3252 if len(s) < n: |
3560 if len(s) < n: |
3253 raise error.Abort(_("stream ended unexpectedly" |
3561 raise error.Abort( |
3254 " (got %d bytes, expected %d)") |
3562 _("stream ended unexpectedly" " (got %d bytes, expected %d)") |
3255 % (len(s), n)) |
3563 % (len(s), n) |
3564 ) |
|
3256 return s |
3565 return s |
3566 |
|
3257 |
3567 |
3258 def uvarintencode(value): |
3568 def uvarintencode(value): |
3259 """Encode an unsigned integer value to a varint. |
3569 """Encode an unsigned integer value to a varint. |
3260 |
3570 |
3261 A varint is a variable length integer of 1 or more bytes. Each byte |
3571 A varint is a variable length integer of 1 or more bytes. Each byte |
3277 Traceback (most recent call last): |
3587 Traceback (most recent call last): |
3278 ... |
3588 ... |
3279 ProgrammingError: negative value for uvarint: -1 |
3589 ProgrammingError: negative value for uvarint: -1 |
3280 """ |
3590 """ |
3281 if value < 0: |
3591 if value < 0: |
3282 raise error.ProgrammingError('negative value for uvarint: %d' |
3592 raise error.ProgrammingError('negative value for uvarint: %d' % value) |
3283 % value) |
3593 bits = value & 0x7F |
3284 bits = value & 0x7f |
|
3285 value >>= 7 |
3594 value >>= 7 |
3286 bytes = [] |
3595 bytes = [] |
3287 while value: |
3596 while value: |
3288 bytes.append(pycompat.bytechr(0x80 | bits)) |
3597 bytes.append(pycompat.bytechr(0x80 | bits)) |
3289 bits = value & 0x7f |
3598 bits = value & 0x7F |
3290 value >>= 7 |
3599 value >>= 7 |
3291 bytes.append(pycompat.bytechr(bits)) |
3600 bytes.append(pycompat.bytechr(bits)) |
3292 |
3601 |
3293 return ''.join(bytes) |
3602 return ''.join(bytes) |
3603 |
|
3294 |
3604 |
3295 def uvarintdecodestream(fh): |
3605 def uvarintdecodestream(fh): |
3296 """Decode an unsigned variable length integer from a stream. |
3606 """Decode an unsigned variable length integer from a stream. |
3297 |
3607 |
3298 The passed argument is anything that has a ``.read(N)`` method. |
3608 The passed argument is anything that has a ``.read(N)`` method. |
3318 """ |
3628 """ |
3319 result = 0 |
3629 result = 0 |
3320 shift = 0 |
3630 shift = 0 |
3321 while True: |
3631 while True: |
3322 byte = ord(readexactly(fh, 1)) |
3632 byte = ord(readexactly(fh, 1)) |
3323 result |= ((byte & 0x7f) << shift) |
3633 result |= (byte & 0x7F) << shift |
3324 if not (byte & 0x80): |
3634 if not (byte & 0x80): |
3325 return result |
3635 return result |
3326 shift += 7 |
3636 shift += 7 |