Mercurial > public > mercurial-scm > hg
comparison mercurial/util.py @ 43077:687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Done with
python3.7 contrib/byteify-strings.py -i $(hg files 'set:mercurial/**.py - mercurial/thirdparty/** + hgext/**.py - hgext/fsmonitor/pywatchman/** - mercurial/__init__.py')
black -l 80 -t py33 -S $(hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**" - hgext/fsmonitor/pywatchman/**')
# skip-blame mass-reformatting only
Differential Revision: https://phab.mercurial-scm.org/D6972
author | Augie Fackler <augie@google.com> |
---|---|
date | Sun, 06 Oct 2019 09:48:39 -0400 |
parents | 2372284d9457 |
children | eef9a2d67051 |
comparison
equal
deleted
inserted
replaced
43076:2372284d9457 | 43077:687b865b95ad |
---|---|
149 | 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(b'HGEMITWARNINGS')) |
155 if _dowarn: | 155 if _dowarn: |
156 # explicitly unfilter our warning for python 2.7 | 156 # explicitly unfilter our warning for python 2.7 |
157 # | 157 # |
158 # The option of setting PYTHONWARNINGS in the test runner was investigated. | 158 # The option of setting PYTHONWARNINGS in the test runner was investigated. |
159 # However, module name set through PYTHONWARNINGS was exactly matched, so | 159 # However, module name set through PYTHONWARNINGS was exactly matched, so |
184 | 184 |
185 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. |
186 """ | 186 """ |
187 if _dowarn: | 187 if _dowarn: |
188 msg += ( | 188 msg += ( |
189 "\n(compatibility will be dropped after Mercurial-%s," | 189 b"\n(compatibility will be dropped after Mercurial-%s," |
190 " update your code.)" | 190 b" update your code.)" |
191 ) % version | 191 ) % version |
192 warnings.warn(pycompat.sysstr(msg), DeprecationWarning, stacklevel + 1) | 192 warnings.warn(pycompat.sysstr(msg), DeprecationWarning, stacklevel + 1) |
193 | 193 |
194 | 194 |
195 DIGESTS = { | 195 DIGESTS = { |
196 'md5': hashlib.md5, | 196 b'md5': hashlib.md5, |
197 'sha1': hashlib.sha1, | 197 b'sha1': hashlib.sha1, |
198 'sha512': hashlib.sha512, | 198 b'sha512': hashlib.sha512, |
199 } | 199 } |
200 # List of digest types from strongest to weakest | 200 # List of digest types from strongest to weakest |
201 DIGESTS_BY_STRENGTH = ['sha512', 'sha1', 'md5'] | 201 DIGESTS_BY_STRENGTH = [b'sha512', b'sha1', b'md5'] |
202 | 202 |
203 for k in DIGESTS_BY_STRENGTH: | 203 for k in DIGESTS_BY_STRENGTH: |
204 assert k in DIGESTS | 204 assert k in DIGESTS |
205 | 205 |
206 | 206 |
219 '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33' | 219 '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33' |
220 >>> digester.preferred([b'md5', b'sha1']) | 220 >>> digester.preferred([b'md5', b'sha1']) |
221 'sha1' | 221 'sha1' |
222 """ | 222 """ |
223 | 223 |
224 def __init__(self, digests, s=''): | 224 def __init__(self, digests, s=b''): |
225 self._hashes = {} | 225 self._hashes = {} |
226 for k in digests: | 226 for k in digests: |
227 if k not in DIGESTS: | 227 if k not in DIGESTS: |
228 raise error.Abort(_('unknown digest type: %s') % k) | 228 raise error.Abort(_(b'unknown digest type: %s') % k) |
229 self._hashes[k] = DIGESTS[k]() | 229 self._hashes[k] = DIGESTS[k]() |
230 if s: | 230 if s: |
231 self.update(s) | 231 self.update(s) |
232 | 232 |
233 def update(self, data): | 233 def update(self, data): |
234 for h in self._hashes.values(): | 234 for h in self._hashes.values(): |
235 h.update(data) | 235 h.update(data) |
236 | 236 |
237 def __getitem__(self, key): | 237 def __getitem__(self, key): |
238 if key not in DIGESTS: | 238 if key not in DIGESTS: |
239 raise error.Abort(_('unknown digest type: %s') % k) | 239 raise error.Abort(_(b'unknown digest type: %s') % k) |
240 return nodemod.hex(self._hashes[key].digest()) | 240 return nodemod.hex(self._hashes[key].digest()) |
241 | 241 |
242 def __iter__(self): | 242 def __iter__(self): |
243 return iter(self._hashes) | 243 return iter(self._hashes) |
244 | 244 |
275 return content | 275 return content |
276 | 276 |
277 def validate(self): | 277 def validate(self): |
278 if self._size != self._got: | 278 if self._size != self._got: |
279 raise error.Abort( | 279 raise error.Abort( |
280 _('size mismatch: expected %d, got %d') | 280 _(b'size mismatch: expected %d, got %d') |
281 % (self._size, self._got) | 281 % (self._size, self._got) |
282 ) | 282 ) |
283 for k, v in self._digests.items(): | 283 for k, v in self._digests.items(): |
284 if v != self._digester[k]: | 284 if v != self._digester[k]: |
285 # i18n: first parameter is a digest name | 285 # i18n: first parameter is a digest name |
286 raise error.Abort( | 286 raise error.Abort( |
287 _('%s mismatch: expected %s, got %s') | 287 _(b'%s mismatch: expected %s, got %s') |
288 % (k, v, self._digester[k]) | 288 % (k, v, self._digester[k]) |
289 ) | 289 ) |
290 | 290 |
291 | 291 |
292 try: | 292 try: |
361 | 361 |
362 def readline(self, *args, **kwargs): | 362 def readline(self, *args, **kwargs): |
363 if len(self._buffer) > 1: | 363 if len(self._buffer) > 1: |
364 # this should not happen because both read and readline end with a | 364 # this should not happen because both read and readline end with a |
365 # _frombuffer call that collapse it. | 365 # _frombuffer call that collapse it. |
366 self._buffer = [''.join(self._buffer)] | 366 self._buffer = [b''.join(self._buffer)] |
367 self._lenbuf = len(self._buffer[0]) | 367 self._lenbuf = len(self._buffer[0]) |
368 lfi = -1 | 368 lfi = -1 |
369 if self._buffer: | 369 if self._buffer: |
370 lfi = self._buffer[-1].find('\n') | 370 lfi = self._buffer[-1].find(b'\n') |
371 while (not self._eof) and lfi < 0: | 371 while (not self._eof) and lfi < 0: |
372 self._fillbuffer() | 372 self._fillbuffer() |
373 if self._buffer: | 373 if self._buffer: |
374 lfi = self._buffer[-1].find('\n') | 374 lfi = self._buffer[-1].find(b'\n') |
375 size = lfi + 1 | 375 size = lfi + 1 |
376 if lfi < 0: # end of file | 376 if lfi < 0: # end of file |
377 size = self._lenbuf | 377 size = self._lenbuf |
378 elif len(self._buffer) > 1: | 378 elif len(self._buffer) > 1: |
379 # we need to take previous chunks into account | 379 # we need to take previous chunks into account |
383 def _frombuffer(self, size): | 383 def _frombuffer(self, size): |
384 """return at most 'size' data from the buffer | 384 """return at most 'size' data from the buffer |
385 | 385 |
386 The data are removed from the buffer.""" | 386 The data are removed from the buffer.""" |
387 if size == 0 or not self._buffer: | 387 if size == 0 or not self._buffer: |
388 return '' | 388 return b'' |
389 buf = self._buffer[0] | 389 buf = self._buffer[0] |
390 if len(self._buffer) > 1: | 390 if len(self._buffer) > 1: |
391 buf = ''.join(self._buffer) | 391 buf = b''.join(self._buffer) |
392 | 392 |
393 data = buf[:size] | 393 data = buf[:size] |
394 buf = buf[len(data) :] | 394 buf = buf[len(data) :] |
395 if buf: | 395 if buf: |
396 self._buffer = [buf] | 396 self._buffer = [buf] |
418 return mmap.mmap(fd, 0, access=mmap.ACCESS_READ) | 418 return mmap.mmap(fd, 0, access=mmap.ACCESS_READ) |
419 except ValueError: | 419 except ValueError: |
420 # Empty files cannot be mmapped, but mmapread should still work. Check | 420 # Empty files cannot be mmapped, but mmapread should still work. Check |
421 # 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. |
422 if os.fstat(fd).st_size == 0: | 422 if os.fstat(fd).st_size == 0: |
423 return '' | 423 return b'' |
424 raise | 424 raise |
425 | 425 |
426 | 426 |
427 class fileobjectproxy(object): | 427 class fileobjectproxy(object): |
428 """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. |
785 | 785 |
786 class baseproxyobserver(object): | 786 class baseproxyobserver(object): |
787 def _writedata(self, data): | 787 def _writedata(self, data): |
788 if not self.logdata: | 788 if not self.logdata: |
789 if self.logdataapis: | 789 if self.logdataapis: |
790 self.fh.write('\n') | 790 self.fh.write(b'\n') |
791 self.fh.flush() | 791 self.fh.flush() |
792 return | 792 return |
793 | 793 |
794 # Simple case writes all data on a single line. | 794 # Simple case writes all data on a single line. |
795 if b'\n' not in data: | 795 if b'\n' not in data: |
796 if self.logdataapis: | 796 if self.logdataapis: |
797 self.fh.write(': %s\n' % stringutil.escapestr(data)) | 797 self.fh.write(b': %s\n' % stringutil.escapestr(data)) |
798 else: | 798 else: |
799 self.fh.write( | 799 self.fh.write( |
800 '%s> %s\n' % (self.name, stringutil.escapestr(data)) | 800 b'%s> %s\n' % (self.name, stringutil.escapestr(data)) |
801 ) | 801 ) |
802 self.fh.flush() | 802 self.fh.flush() |
803 return | 803 return |
804 | 804 |
805 # Data with newlines is written to multiple lines. | 805 # Data with newlines is written to multiple lines. |
806 if self.logdataapis: | 806 if self.logdataapis: |
807 self.fh.write(':\n') | 807 self.fh.write(b':\n') |
808 | 808 |
809 lines = data.splitlines(True) | 809 lines = data.splitlines(True) |
810 for line in lines: | 810 for line in lines: |
811 self.fh.write( | 811 self.fh.write( |
812 '%s> %s\n' % (self.name, stringutil.escapestr(line)) | 812 b'%s> %s\n' % (self.name, stringutil.escapestr(line)) |
813 ) | 813 ) |
814 self.fh.flush() | 814 self.fh.flush() |
815 | 815 |
816 | 816 |
817 class fileobjectobserver(baseproxyobserver): | 817 class fileobjectobserver(baseproxyobserver): |
830 def read(self, res, size=-1): | 830 def read(self, res, size=-1): |
831 if not self.reads: | 831 if not self.reads: |
832 return | 832 return |
833 # Python 3 can return None from reads at EOF instead of empty strings. | 833 # Python 3 can return None from reads at EOF instead of empty strings. |
834 if res is None: | 834 if res is None: |
835 res = '' | 835 res = b'' |
836 | 836 |
837 if size == -1 and res == '': | 837 if size == -1 and res == b'': |
838 # Suppress pointless read(-1) calls that return | 838 # Suppress pointless read(-1) calls that return |
839 # nothing. These happen _a lot_ on Python 3, and there | 839 # nothing. These happen _a lot_ on Python 3, and there |
840 # doesn't seem to be a better workaround to have matching | 840 # doesn't seem to be a better workaround to have matching |
841 # Python 2 and 3 behavior. :( | 841 # Python 2 and 3 behavior. :( |
842 return | 842 return |
843 | 843 |
844 if self.logdataapis: | 844 if self.logdataapis: |
845 self.fh.write('%s> read(%d) -> %d' % (self.name, size, len(res))) | 845 self.fh.write(b'%s> read(%d) -> %d' % (self.name, size, len(res))) |
846 | 846 |
847 self._writedata(res) | 847 self._writedata(res) |
848 | 848 |
849 def readline(self, res, limit=-1): | 849 def readline(self, res, limit=-1): |
850 if not self.reads: | 850 if not self.reads: |
851 return | 851 return |
852 | 852 |
853 if self.logdataapis: | 853 if self.logdataapis: |
854 self.fh.write('%s> readline() -> %d' % (self.name, len(res))) | 854 self.fh.write(b'%s> readline() -> %d' % (self.name, len(res))) |
855 | 855 |
856 self._writedata(res) | 856 self._writedata(res) |
857 | 857 |
858 def readinto(self, res, dest): | 858 def readinto(self, res, dest): |
859 if not self.reads: | 859 if not self.reads: |
860 return | 860 return |
861 | 861 |
862 if self.logdataapis: | 862 if self.logdataapis: |
863 self.fh.write( | 863 self.fh.write( |
864 '%s> readinto(%d) -> %r' % (self.name, len(dest), res) | 864 b'%s> readinto(%d) -> %r' % (self.name, len(dest), res) |
865 ) | 865 ) |
866 | 866 |
867 data = dest[0:res] if res is not None else b'' | 867 data = dest[0:res] if res is not None else b'' |
868 | 868 |
869 # _writedata() uses "in" operator and is confused by memoryview because | 869 # _writedata() uses "in" operator and is confused by memoryview because |
881 # returns the integer bytes written. | 881 # returns the integer bytes written. |
882 if res is None and data: | 882 if res is None and data: |
883 res = len(data) | 883 res = len(data) |
884 | 884 |
885 if self.logdataapis: | 885 if self.logdataapis: |
886 self.fh.write('%s> write(%d) -> %r' % (self.name, len(data), res)) | 886 self.fh.write(b'%s> write(%d) -> %r' % (self.name, len(data), res)) |
887 | 887 |
888 self._writedata(data) | 888 self._writedata(data) |
889 | 889 |
890 def flush(self, res): | 890 def flush(self, res): |
891 if not self.writes: | 891 if not self.writes: |
892 return | 892 return |
893 | 893 |
894 self.fh.write('%s> flush() -> %r\n' % (self.name, res)) | 894 self.fh.write(b'%s> flush() -> %r\n' % (self.name, res)) |
895 | 895 |
896 # For observedbufferedinputpipe. | 896 # For observedbufferedinputpipe. |
897 def bufferedread(self, res, size): | 897 def bufferedread(self, res, size): |
898 if not self.reads: | 898 if not self.reads: |
899 return | 899 return |
900 | 900 |
901 if self.logdataapis: | 901 if self.logdataapis: |
902 self.fh.write( | 902 self.fh.write( |
903 '%s> bufferedread(%d) -> %d' % (self.name, size, len(res)) | 903 b'%s> bufferedread(%d) -> %d' % (self.name, size, len(res)) |
904 ) | 904 ) |
905 | 905 |
906 self._writedata(res) | 906 self._writedata(res) |
907 | 907 |
908 def bufferedreadline(self, res): | 908 def bufferedreadline(self, res): |
909 if not self.reads: | 909 if not self.reads: |
910 return | 910 return |
911 | 911 |
912 if self.logdataapis: | 912 if self.logdataapis: |
913 self.fh.write( | 913 self.fh.write( |
914 '%s> bufferedreadline() -> %d' % (self.name, len(res)) | 914 b'%s> bufferedreadline() -> %d' % (self.name, len(res)) |
915 ) | 915 ) |
916 | 916 |
917 self._writedata(res) | 917 self._writedata(res) |
918 | 918 |
919 | 919 |
956 | 956 |
957 def makefile(self, res, mode=None, bufsize=None): | 957 def makefile(self, res, mode=None, bufsize=None): |
958 if not self.states: | 958 if not self.states: |
959 return | 959 return |
960 | 960 |
961 self.fh.write('%s> makefile(%r, %r)\n' % (self.name, mode, bufsize)) | 961 self.fh.write(b'%s> makefile(%r, %r)\n' % (self.name, mode, bufsize)) |
962 | 962 |
963 def recv(self, res, size, flags=0): | 963 def recv(self, res, size, flags=0): |
964 if not self.reads: | 964 if not self.reads: |
965 return | 965 return |
966 | 966 |
967 if self.logdataapis: | 967 if self.logdataapis: |
968 self.fh.write( | 968 self.fh.write( |
969 '%s> recv(%d, %d) -> %d' % (self.name, size, flags, len(res)) | 969 b'%s> recv(%d, %d) -> %d' % (self.name, size, flags, len(res)) |
970 ) | 970 ) |
971 self._writedata(res) | 971 self._writedata(res) |
972 | 972 |
973 def recvfrom(self, res, size, flags=0): | 973 def recvfrom(self, res, size, flags=0): |
974 if not self.reads: | 974 if not self.reads: |
975 return | 975 return |
976 | 976 |
977 if self.logdataapis: | 977 if self.logdataapis: |
978 self.fh.write( | 978 self.fh.write( |
979 '%s> recvfrom(%d, %d) -> %d' | 979 b'%s> recvfrom(%d, %d) -> %d' |
980 % (self.name, size, flags, len(res[0])) | 980 % (self.name, size, flags, len(res[0])) |
981 ) | 981 ) |
982 | 982 |
983 self._writedata(res[0]) | 983 self._writedata(res[0]) |
984 | 984 |
986 if not self.reads: | 986 if not self.reads: |
987 return | 987 return |
988 | 988 |
989 if self.logdataapis: | 989 if self.logdataapis: |
990 self.fh.write( | 990 self.fh.write( |
991 '%s> recvfrom_into(%d, %d) -> %d' | 991 b'%s> recvfrom_into(%d, %d) -> %d' |
992 % (self.name, size, flags, res[0]) | 992 % (self.name, size, flags, res[0]) |
993 ) | 993 ) |
994 | 994 |
995 self._writedata(buf[0 : res[0]]) | 995 self._writedata(buf[0 : res[0]]) |
996 | 996 |
998 if not self.reads: | 998 if not self.reads: |
999 return | 999 return |
1000 | 1000 |
1001 if self.logdataapis: | 1001 if self.logdataapis: |
1002 self.fh.write( | 1002 self.fh.write( |
1003 '%s> recv_into(%d, %d) -> %d' % (self.name, size, flags, res) | 1003 b'%s> recv_into(%d, %d) -> %d' % (self.name, size, flags, res) |
1004 ) | 1004 ) |
1005 | 1005 |
1006 self._writedata(buf[0:res]) | 1006 self._writedata(buf[0:res]) |
1007 | 1007 |
1008 def send(self, res, data, flags=0): | 1008 def send(self, res, data, flags=0): |
1009 if not self.writes: | 1009 if not self.writes: |
1010 return | 1010 return |
1011 | 1011 |
1012 self.fh.write( | 1012 self.fh.write( |
1013 '%s> send(%d, %d) -> %d' % (self.name, len(data), flags, len(res)) | 1013 b'%s> send(%d, %d) -> %d' % (self.name, len(data), flags, len(res)) |
1014 ) | 1014 ) |
1015 self._writedata(data) | 1015 self._writedata(data) |
1016 | 1016 |
1017 def sendall(self, res, data, flags=0): | 1017 def sendall(self, res, data, flags=0): |
1018 if not self.writes: | 1018 if not self.writes: |
1019 return | 1019 return |
1020 | 1020 |
1021 if self.logdataapis: | 1021 if self.logdataapis: |
1022 # Returns None on success. So don't bother reporting return value. | 1022 # Returns None on success. So don't bother reporting return value. |
1023 self.fh.write('%s> sendall(%d, %d)' % (self.name, len(data), flags)) | 1023 self.fh.write( |
1024 b'%s> sendall(%d, %d)' % (self.name, len(data), flags) | |
1025 ) | |
1024 | 1026 |
1025 self._writedata(data) | 1027 self._writedata(data) |
1026 | 1028 |
1027 def sendto(self, res, data, flagsoraddress, address=None): | 1029 def sendto(self, res, data, flagsoraddress, address=None): |
1028 if not self.writes: | 1030 if not self.writes: |
1033 else: | 1035 else: |
1034 flags = 0 | 1036 flags = 0 |
1035 | 1037 |
1036 if self.logdataapis: | 1038 if self.logdataapis: |
1037 self.fh.write( | 1039 self.fh.write( |
1038 '%s> sendto(%d, %d, %r) -> %d' | 1040 b'%s> sendto(%d, %d, %r) -> %d' |
1039 % (self.name, len(data), flags, address, res) | 1041 % (self.name, len(data), flags, address, res) |
1040 ) | 1042 ) |
1041 | 1043 |
1042 self._writedata(data) | 1044 self._writedata(data) |
1043 | 1045 |
1044 def setblocking(self, res, flag): | 1046 def setblocking(self, res, flag): |
1045 if not self.states: | 1047 if not self.states: |
1046 return | 1048 return |
1047 | 1049 |
1048 self.fh.write('%s> setblocking(%r)\n' % (self.name, flag)) | 1050 self.fh.write(b'%s> setblocking(%r)\n' % (self.name, flag)) |
1049 | 1051 |
1050 def settimeout(self, res, value): | 1052 def settimeout(self, res, value): |
1051 if not self.states: | 1053 if not self.states: |
1052 return | 1054 return |
1053 | 1055 |
1054 self.fh.write('%s> settimeout(%r)\n' % (self.name, value)) | 1056 self.fh.write(b'%s> settimeout(%r)\n' % (self.name, value)) |
1055 | 1057 |
1056 def gettimeout(self, res): | 1058 def gettimeout(self, res): |
1057 if not self.states: | 1059 if not self.states: |
1058 return | 1060 return |
1059 | 1061 |
1060 self.fh.write('%s> gettimeout() -> %f\n' % (self.name, res)) | 1062 self.fh.write(b'%s> gettimeout() -> %f\n' % (self.name, res)) |
1061 | 1063 |
1062 def setsockopt(self, res, level, optname, value): | 1064 def setsockopt(self, res, level, optname, value): |
1063 if not self.states: | 1065 if not self.states: |
1064 return | 1066 return |
1065 | 1067 |
1066 self.fh.write( | 1068 self.fh.write( |
1067 '%s> setsockopt(%r, %r, %r) -> %r\n' | 1069 b'%s> setsockopt(%r, %r, %r) -> %r\n' |
1068 % (self.name, level, optname, value, res) | 1070 % (self.name, level, optname, value, res) |
1069 ) | 1071 ) |
1070 | 1072 |
1071 | 1073 |
1072 def makeloggingsocket( | 1074 def makeloggingsocket( |
1098 try: | 1100 try: |
1099 from . import __version__ | 1101 from . import __version__ |
1100 | 1102 |
1101 return __version__.version | 1103 return __version__.version |
1102 except ImportError: | 1104 except ImportError: |
1103 return 'unknown' | 1105 return b'unknown' |
1104 | 1106 |
1105 | 1107 |
1106 def versiontuple(v=None, n=4): | 1108 def versiontuple(v=None, n=4): |
1107 """Parses a Mercurial version string into an N-tuple. | 1109 """Parses a Mercurial version string into an N-tuple. |
1108 | 1110 |
1160 """ | 1162 """ |
1161 if not v: | 1163 if not v: |
1162 v = version() | 1164 v = version() |
1163 m = remod.match(br'(\d+(?:\.\d+){,2})[\+-]?(.*)', v) | 1165 m = remod.match(br'(\d+(?:\.\d+){,2})[\+-]?(.*)', v) |
1164 if not m: | 1166 if not m: |
1165 vparts, extra = '', v | 1167 vparts, extra = b'', v |
1166 elif m.group(2): | 1168 elif m.group(2): |
1167 vparts, extra = m.groups() | 1169 vparts, extra = m.groups() |
1168 else: | 1170 else: |
1169 vparts, extra = m.group(1), None | 1171 vparts, extra = m.group(1), None |
1170 | 1172 |
1171 vints = [] | 1173 vints = [] |
1172 for i in vparts.split('.'): | 1174 for i in vparts.split(b'.'): |
1173 try: | 1175 try: |
1174 vints.append(int(i)) | 1176 vints.append(int(i)) |
1175 except ValueError: | 1177 except ValueError: |
1176 break | 1178 break |
1177 # (3, 6) -> (3, 6, None) | 1179 # (3, 6) -> (3, 6, None) |
1742 nmin = 1 << log2(blen) | 1744 nmin = 1 << log2(blen) |
1743 if nmin > min: | 1745 if nmin > min: |
1744 min = nmin | 1746 min = nmin |
1745 if min > max: | 1747 if min > max: |
1746 min = max | 1748 min = max |
1747 yield ''.join(buf) | 1749 yield b''.join(buf) |
1748 blen = 0 | 1750 blen = 0 |
1749 buf = [] | 1751 buf = [] |
1750 if buf: | 1752 if buf: |
1751 yield ''.join(buf) | 1753 yield b''.join(buf) |
1752 | 1754 |
1753 | 1755 |
1754 def always(fn): | 1756 def always(fn): |
1755 return True | 1757 return True |
1756 | 1758 |
1804 if not n1: | 1806 if not n1: |
1805 return localpath(n2) | 1807 return localpath(n2) |
1806 if os.path.isabs(n1): | 1808 if os.path.isabs(n1): |
1807 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]: | 1809 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]: |
1808 return os.path.join(root, localpath(n2)) | 1810 return os.path.join(root, localpath(n2)) |
1809 n2 = '/'.join((pconvert(root), n2)) | 1811 n2 = b'/'.join((pconvert(root), n2)) |
1810 a, b = splitpath(n1), n2.split('/') | 1812 a, b = splitpath(n1), n2.split(b'/') |
1811 a.reverse() | 1813 a.reverse() |
1812 b.reverse() | 1814 b.reverse() |
1813 while a and b and a[-1] == b[-1]: | 1815 while a and b and a[-1] == b[-1]: |
1814 a.pop() | 1816 a.pop() |
1815 b.pop() | 1817 b.pop() |
1816 b.reverse() | 1818 b.reverse() |
1817 return pycompat.ossep.join((['..'] * len(a)) + b) or '.' | 1819 return pycompat.ossep.join(([b'..'] * len(a)) + b) or b'.' |
1818 | 1820 |
1819 | 1821 |
1820 # the location of data files matching the source code | 1822 # the location of data files matching the source code |
1821 if procutil.mainfrozen() and getattr(sys, 'frozen', None) != 'macosx_app': | 1823 if procutil.mainfrozen() and getattr(sys, 'frozen', None) != b'macosx_app': |
1822 # executable version (py2exe) doesn't support __file__ | 1824 # executable version (py2exe) doesn't support __file__ |
1823 datapath = os.path.dirname(pycompat.sysexecutable) | 1825 datapath = os.path.dirname(pycompat.sysexecutable) |
1824 else: | 1826 else: |
1825 datapath = os.path.dirname(pycompat.fsencode(__file__)) | 1827 datapath = os.path.dirname(pycompat.fsencode(__file__)) |
1826 | 1828 |
1841 return check | 1843 return check |
1842 | 1844 |
1843 | 1845 |
1844 # a whilelist of known filesystems where hardlink works reliably | 1846 # a whilelist of known filesystems where hardlink works reliably |
1845 _hardlinkfswhitelist = { | 1847 _hardlinkfswhitelist = { |
1846 'apfs', | 1848 b'apfs', |
1847 'btrfs', | 1849 b'btrfs', |
1848 'ext2', | 1850 b'ext2', |
1849 'ext3', | 1851 b'ext3', |
1850 'ext4', | 1852 b'ext4', |
1851 'hfs', | 1853 b'hfs', |
1852 'jfs', | 1854 b'jfs', |
1853 'NTFS', | 1855 b'NTFS', |
1854 'reiserfs', | 1856 b'reiserfs', |
1855 'tmpfs', | 1857 b'tmpfs', |
1856 'ufs', | 1858 b'ufs', |
1857 'xfs', | 1859 b'xfs', |
1858 'zfs', | 1860 b'zfs', |
1859 } | 1861 } |
1860 | 1862 |
1861 | 1863 |
1862 def copyfile(src, dest, hardlink=False, copystat=False, checkambig=False): | 1864 def copyfile(src, dest, hardlink=False, copystat=False, checkambig=False): |
1863 '''copy a file, preserving mode and optionally other stat info like | 1865 '''copy a file, preserving mode and optionally other stat info like |
1918 """Copy a directory tree using hardlinks if possible.""" | 1920 """Copy a directory tree using hardlinks if possible.""" |
1919 num = 0 | 1921 num = 0 |
1920 | 1922 |
1921 def settopic(): | 1923 def settopic(): |
1922 if progress: | 1924 if progress: |
1923 progress.topic = _('linking') if hardlink else _('copying') | 1925 progress.topic = _(b'linking') if hardlink else _(b'copying') |
1924 | 1926 |
1925 if os.path.isdir(src): | 1927 if os.path.isdir(src): |
1926 if hardlink is None: | 1928 if hardlink is None: |
1927 hardlink = ( | 1929 hardlink = ( |
1928 os.stat(src).st_dev == os.stat(os.path.dirname(dst)).st_dev | 1930 os.stat(src).st_dev == os.stat(os.path.dirname(dst)).st_dev |
1956 | 1958 |
1957 return hardlink, num | 1959 return hardlink, num |
1958 | 1960 |
1959 | 1961 |
1960 _winreservednames = { | 1962 _winreservednames = { |
1961 'con', | 1963 b'con', |
1962 'prn', | 1964 b'prn', |
1963 'aux', | 1965 b'aux', |
1964 'nul', | 1966 b'nul', |
1965 'com1', | 1967 b'com1', |
1966 'com2', | 1968 b'com2', |
1967 'com3', | 1969 b'com3', |
1968 'com4', | 1970 b'com4', |
1969 'com5', | 1971 b'com5', |
1970 'com6', | 1972 b'com6', |
1971 'com7', | 1973 b'com7', |
1972 'com8', | 1974 b'com8', |
1973 'com9', | 1975 b'com9', |
1974 'lpt1', | 1976 b'lpt1', |
1975 'lpt2', | 1977 b'lpt2', |
1976 'lpt3', | 1978 b'lpt3', |
1977 'lpt4', | 1979 b'lpt4', |
1978 'lpt5', | 1980 b'lpt5', |
1979 'lpt6', | 1981 b'lpt6', |
1980 'lpt7', | 1982 b'lpt7', |
1981 'lpt8', | 1983 b'lpt8', |
1982 'lpt9', | 1984 b'lpt9', |
1983 } | 1985 } |
1984 _winreservedchars = ':*?"<>|' | 1986 _winreservedchars = b':*?"<>|' |
1985 | 1987 |
1986 | 1988 |
1987 def checkwinfilename(path): | 1989 def checkwinfilename(path): |
1988 r'''Check that the base-relative path is a valid filename on Windows. | 1990 r'''Check that the base-relative path is a valid filename on Windows. |
1989 Returns None if the path is ok, or a UI string describing the problem. | 1991 Returns None if the path is ok, or a UI string describing the problem. |
2006 >>> checkwinfilename(b"foo\\") | 2008 >>> checkwinfilename(b"foo\\") |
2007 "filename ends with '\\', which is invalid on Windows" | 2009 "filename ends with '\\', which is invalid on Windows" |
2008 >>> checkwinfilename(b"foo\\/bar") | 2010 >>> checkwinfilename(b"foo\\/bar") |
2009 "directory name ends with '\\', which is invalid on Windows" | 2011 "directory name ends with '\\', which is invalid on Windows" |
2010 ''' | 2012 ''' |
2011 if path.endswith('\\'): | 2013 if path.endswith(b'\\'): |
2012 return _("filename ends with '\\', which is invalid on Windows") | 2014 return _(b"filename ends with '\\', which is invalid on Windows") |
2013 if '\\/' in path: | 2015 if b'\\/' in path: |
2014 return _("directory name ends with '\\', which is invalid on Windows") | 2016 return _(b"directory name ends with '\\', which is invalid on Windows") |
2015 for n in path.replace('\\', '/').split('/'): | 2017 for n in path.replace(b'\\', b'/').split(b'/'): |
2016 if not n: | 2018 if not n: |
2017 continue | 2019 continue |
2018 for c in _filenamebytestr(n): | 2020 for c in _filenamebytestr(n): |
2019 if c in _winreservedchars: | 2021 if c in _winreservedchars: |
2020 return ( | 2022 return ( |
2021 _("filename contains '%s', which is reserved " "on Windows") | 2023 _( |
2024 b"filename contains '%s', which is reserved " | |
2025 b"on Windows" | |
2026 ) | |
2022 % c | 2027 % c |
2023 ) | 2028 ) |
2024 if ord(c) <= 31: | 2029 if ord(c) <= 31: |
2025 return _( | 2030 return _( |
2026 "filename contains '%s', which is invalid " "on Windows" | 2031 b"filename contains '%s', which is invalid " b"on Windows" |
2027 ) % stringutil.escapestr(c) | 2032 ) % stringutil.escapestr(c) |
2028 base = n.split('.')[0] | 2033 base = n.split(b'.')[0] |
2029 if base and base.lower() in _winreservednames: | 2034 if base and base.lower() in _winreservednames: |
2030 return ( | 2035 return ( |
2031 _("filename contains '%s', which is reserved " "on Windows") | 2036 _(b"filename contains '%s', which is reserved " b"on Windows") |
2032 % base | 2037 % base |
2033 ) | 2038 ) |
2034 t = n[-1:] | 2039 t = n[-1:] |
2035 if t in '. ' and n not in '..': | 2040 if t in b'. ' and n not in b'..': |
2036 return ( | 2041 return ( |
2037 _("filename ends with '%s', which is not allowed " "on Windows") | 2042 _( |
2043 b"filename ends with '%s', which is not allowed " | |
2044 b"on Windows" | |
2045 ) | |
2038 % t | 2046 % t |
2039 ) | 2047 ) |
2040 | 2048 |
2041 | 2049 |
2042 if pycompat.iswindows: | 2050 if pycompat.iswindows: |
2076 except OSError as why: | 2084 except OSError as why: |
2077 if why.errno not in (errno.EINVAL, errno.ENOSYS): | 2085 if why.errno not in (errno.EINVAL, errno.ENOSYS): |
2078 raise | 2086 raise |
2079 except AttributeError: # no symlink in os | 2087 except AttributeError: # no symlink in os |
2080 pass | 2088 pass |
2081 with posixfile(pathname, 'rb') as fp: | 2089 with posixfile(pathname, b'rb') as fp: |
2082 return fp.read() | 2090 return fp.read() |
2083 | 2091 |
2084 | 2092 |
2085 def fstat(fp): | 2093 def fstat(fp): |
2086 '''stat file object that may not have fileno method.''' | 2094 '''stat file object that may not have fileno method.''' |
2128 class _re(object): | 2136 class _re(object): |
2129 def _checkre2(self): | 2137 def _checkre2(self): |
2130 global _re2 | 2138 global _re2 |
2131 try: | 2139 try: |
2132 # check if match works, see issue3964 | 2140 # check if match works, see issue3964 |
2133 _re2 = bool(re2.match(r'\[([^\[]+)\]', '[ui]')) | 2141 _re2 = bool(re2.match(r'\[([^\[]+)\]', b'[ui]')) |
2134 except ImportError: | 2142 except ImportError: |
2135 _re2 = False | 2143 _re2 = False |
2136 | 2144 |
2137 def compile(self, pat, flags=0): | 2145 def compile(self, pat, flags=0): |
2138 '''Compile a regular expression, using re2 if possible | 2146 '''Compile a regular expression, using re2 if possible |
2142 IGNORECASE and MULTILINE.''' | 2150 IGNORECASE and MULTILINE.''' |
2143 if _re2 is None: | 2151 if _re2 is None: |
2144 self._checkre2() | 2152 self._checkre2() |
2145 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0: | 2153 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0: |
2146 if flags & remod.IGNORECASE: | 2154 if flags & remod.IGNORECASE: |
2147 pat = '(?i)' + pat | 2155 pat = b'(?i)' + pat |
2148 if flags & remod.MULTILINE: | 2156 if flags & remod.MULTILINE: |
2149 pat = '(?m)' + pat | 2157 pat = b'(?m)' + pat |
2150 try: | 2158 try: |
2151 return re2.compile(pat) | 2159 return re2.compile(pat) |
2152 except re2.error: | 2160 except re2.error: |
2153 pass | 2161 pass |
2154 return remod.compile(pat, flags) | 2162 return remod.compile(pat, flags) |
2190 | 2198 |
2191 seps = pycompat.ossep | 2199 seps = pycompat.ossep |
2192 if pycompat.osaltsep: | 2200 if pycompat.osaltsep: |
2193 seps = seps + pycompat.osaltsep | 2201 seps = seps + pycompat.osaltsep |
2194 # Protect backslashes. This gets silly very quickly. | 2202 # Protect backslashes. This gets silly very quickly. |
2195 seps.replace('\\', '\\\\') | 2203 seps.replace(b'\\', b'\\\\') |
2196 pattern = remod.compile(br'([^%s]+)|([%s]+)' % (seps, seps)) | 2204 pattern = remod.compile(br'([^%s]+)|([%s]+)' % (seps, seps)) |
2197 dir = os.path.normpath(root) | 2205 dir = os.path.normpath(root) |
2198 result = [] | 2206 result = [] |
2199 for part, sep in pattern.findall(name): | 2207 for part, sep in pattern.findall(name): |
2200 if sep: | 2208 if sep: |
2213 found = contents.get(part) | 2221 found = contents.get(part) |
2214 | 2222 |
2215 result.append(found or part) | 2223 result.append(found or part) |
2216 dir = os.path.join(dir, part) | 2224 dir = os.path.join(dir, part) |
2217 | 2225 |
2218 return ''.join(result) | 2226 return b''.join(result) |
2219 | 2227 |
2220 | 2228 |
2221 def checknlink(testfile): | 2229 def checknlink(testfile): |
2222 '''check whether hardlink count reporting works properly''' | 2230 '''check whether hardlink count reporting works properly''' |
2223 | 2231 |
2224 # testfile may be open, so we need a separate file for checking to | 2232 # testfile may be open, so we need a separate file for checking to |
2225 # work around issue2543 (or testfile may get lost on Samba shares) | 2233 # work around issue2543 (or testfile may get lost on Samba shares) |
2226 f1, f2, fp = None, None, None | 2234 f1, f2, fp = None, None, None |
2227 try: | 2235 try: |
2228 fd, f1 = pycompat.mkstemp( | 2236 fd, f1 = pycompat.mkstemp( |
2229 prefix='.%s-' % os.path.basename(testfile), | 2237 prefix=b'.%s-' % os.path.basename(testfile), |
2230 suffix='1~', | 2238 suffix=b'1~', |
2231 dir=os.path.dirname(testfile), | 2239 dir=os.path.dirname(testfile), |
2232 ) | 2240 ) |
2233 os.close(fd) | 2241 os.close(fd) |
2234 f2 = '%s2~' % f1[:-2] | 2242 f2 = b'%s2~' % f1[:-2] |
2235 | 2243 |
2236 oslink(f1, f2) | 2244 oslink(f1, f2) |
2237 # nlinks() may behave differently for files on Windows shares if | 2245 # nlinks() may behave differently for files on Windows shares if |
2238 # the file is open. | 2246 # the file is open. |
2239 fp = posixfile(f2) | 2247 fp = posixfile(f2) |
2278 can use emptyok=True as an optimization. | 2286 can use emptyok=True as an optimization. |
2279 | 2287 |
2280 Returns the name of the temporary file. | 2288 Returns the name of the temporary file. |
2281 """ | 2289 """ |
2282 d, fn = os.path.split(name) | 2290 d, fn = os.path.split(name) |
2283 fd, temp = pycompat.mkstemp(prefix='.%s-' % fn, suffix='~', dir=d) | 2291 fd, temp = pycompat.mkstemp(prefix=b'.%s-' % fn, suffix=b'~', dir=d) |
2284 os.close(fd) | 2292 os.close(fd) |
2285 # Temporary files are created with mode 0600, which is usually not | 2293 # Temporary files are created with mode 0600, which is usually not |
2286 # what we want. If the original file already exists, just copy | 2294 # what we want. If the original file already exists, just copy |
2287 # its mode. Otherwise, manually obey umask. | 2295 # its mode. Otherwise, manually obey umask. |
2288 copymode(name, temp, createmode, enforcewritable) | 2296 copymode(name, temp, createmode, enforcewritable) |
2289 | 2297 |
2290 if emptyok: | 2298 if emptyok: |
2291 return temp | 2299 return temp |
2292 try: | 2300 try: |
2293 try: | 2301 try: |
2294 ifp = posixfile(name, "rb") | 2302 ifp = posixfile(name, b"rb") |
2295 except IOError as inst: | 2303 except IOError as inst: |
2296 if inst.errno == errno.ENOENT: | 2304 if inst.errno == errno.ENOENT: |
2297 return temp | 2305 return temp |
2298 if not getattr(inst, 'filename', None): | 2306 if not getattr(inst, 'filename', None): |
2299 inst.filename = name | 2307 inst.filename = name |
2300 raise | 2308 raise |
2301 ofp = posixfile(temp, "wb") | 2309 ofp = posixfile(temp, b"wb") |
2302 for chunk in filechunkiter(ifp): | 2310 for chunk in filechunkiter(ifp): |
2303 ofp.write(chunk) | 2311 ofp.write(chunk) |
2304 ifp.close() | 2312 ifp.close() |
2305 ofp.close() | 2313 ofp.close() |
2306 except: # re-raises | 2314 except: # re-raises |
2430 checkambig argument of constructor is used with filestat, and is | 2438 checkambig argument of constructor is used with filestat, and is |
2431 useful only if target file is guarded by any lock (e.g. repo.lock | 2439 useful only if target file is guarded by any lock (e.g. repo.lock |
2432 or repo.wlock). | 2440 or repo.wlock). |
2433 ''' | 2441 ''' |
2434 | 2442 |
2435 def __init__(self, name, mode='w+b', createmode=None, checkambig=False): | 2443 def __init__(self, name, mode=b'w+b', createmode=None, checkambig=False): |
2436 self.__name = name # permanent name | 2444 self.__name = name # permanent name |
2437 self._tempname = mktempcopy( | 2445 self._tempname = mktempcopy( |
2438 name, | 2446 name, |
2439 emptyok=('w' in mode), | 2447 emptyok=(b'w' in mode), |
2440 createmode=createmode, | 2448 createmode=createmode, |
2441 enforcewritable=('w' in mode), | 2449 enforcewritable=(b'w' in mode), |
2442 ) | 2450 ) |
2443 | 2451 |
2444 self._fp = posixfile(self._tempname, mode) | 2452 self._fp = posixfile(self._tempname, mode) |
2445 self._checkambig = checkambig | 2453 self._checkambig = checkambig |
2446 | 2454 |
2539 if mode is not None: | 2547 if mode is not None: |
2540 os.chmod(name, mode) | 2548 os.chmod(name, mode) |
2541 | 2549 |
2542 | 2550 |
2543 def readfile(path): | 2551 def readfile(path): |
2544 with open(path, 'rb') as fp: | 2552 with open(path, b'rb') as fp: |
2545 return fp.read() | 2553 return fp.read() |
2546 | 2554 |
2547 | 2555 |
2548 def writefile(path, text): | 2556 def writefile(path, text): |
2549 with open(path, 'wb') as fp: | 2557 with open(path, b'wb') as fp: |
2550 fp.write(text) | 2558 fp.write(text) |
2551 | 2559 |
2552 | 2560 |
2553 def appendfile(path, text): | 2561 def appendfile(path, text): |
2554 with open(path, 'ab') as fp: | 2562 with open(path, b'ab') as fp: |
2555 fp.write(text) | 2563 fp.write(text) |
2556 | 2564 |
2557 | 2565 |
2558 class chunkbuffer(object): | 2566 class chunkbuffer(object): |
2559 """Allow arbitrary sized chunks of data to be efficiently read from an | 2567 """Allow arbitrary sized chunks of data to be efficiently read from an |
2581 """Read L bytes of data from the iterator of chunks of data. | 2589 """Read L bytes of data from the iterator of chunks of data. |
2582 Returns less than L bytes if the iterator runs dry. | 2590 Returns less than L bytes if the iterator runs dry. |
2583 | 2591 |
2584 If size parameter is omitted, read everything""" | 2592 If size parameter is omitted, read everything""" |
2585 if l is None: | 2593 if l is None: |
2586 return ''.join(self.iter) | 2594 return b''.join(self.iter) |
2587 | 2595 |
2588 left = l | 2596 left = l |
2589 buf = [] | 2597 buf = [] |
2590 queue = self._queue | 2598 queue = self._queue |
2591 while left > 0: | 2599 while left > 0: |
2633 else: | 2641 else: |
2634 buf.append(chunk[offset : offset + left]) | 2642 buf.append(chunk[offset : offset + left]) |
2635 self._chunkoffset += left | 2643 self._chunkoffset += left |
2636 left -= chunkremaining | 2644 left -= chunkremaining |
2637 | 2645 |
2638 return ''.join(buf) | 2646 return b''.join(buf) |
2639 | 2647 |
2640 | 2648 |
2641 def filechunkiter(f, size=131072, limit=None): | 2649 def filechunkiter(f, size=131072, limit=None): |
2642 """Create a generator that produces the data in the file size | 2650 """Create a generator that produces the data in the file size |
2643 (default 131072) bytes at a time, up to optional limit (default is | 2651 (default 131072) bytes at a time, up to optional limit (default is |
2725 Traceback (most recent call last): | 2733 Traceback (most recent call last): |
2726 ... | 2734 ... |
2727 ParseError: fromline must be strictly positive | 2735 ParseError: fromline must be strictly positive |
2728 """ | 2736 """ |
2729 if toline - fromline < 0: | 2737 if toline - fromline < 0: |
2730 raise error.ParseError(_("line range must be positive")) | 2738 raise error.ParseError(_(b"line range must be positive")) |
2731 if fromline < 1: | 2739 if fromline < 1: |
2732 raise error.ParseError(_("fromline must be strictly positive")) | 2740 raise error.ParseError(_(b"fromline must be strictly positive")) |
2733 return fromline - 1, toline | 2741 return fromline - 1, toline |
2734 | 2742 |
2735 | 2743 |
2736 bytecount = unitcountfn( | 2744 bytecount = unitcountfn( |
2737 (100, 1 << 30, _('%.0f GB')), | 2745 (100, 1 << 30, _(b'%.0f GB')), |
2738 (10, 1 << 30, _('%.1f GB')), | 2746 (10, 1 << 30, _(b'%.1f GB')), |
2739 (1, 1 << 30, _('%.2f GB')), | 2747 (1, 1 << 30, _(b'%.2f GB')), |
2740 (100, 1 << 20, _('%.0f MB')), | 2748 (100, 1 << 20, _(b'%.0f MB')), |
2741 (10, 1 << 20, _('%.1f MB')), | 2749 (10, 1 << 20, _(b'%.1f MB')), |
2742 (1, 1 << 20, _('%.2f MB')), | 2750 (1, 1 << 20, _(b'%.2f MB')), |
2743 (100, 1 << 10, _('%.0f KB')), | 2751 (100, 1 << 10, _(b'%.0f KB')), |
2744 (10, 1 << 10, _('%.1f KB')), | 2752 (10, 1 << 10, _(b'%.1f KB')), |
2745 (1, 1 << 10, _('%.2f KB')), | 2753 (1, 1 << 10, _(b'%.2f KB')), |
2746 (1, 1, _('%.0f bytes')), | 2754 (1, 1, _(b'%.0f bytes')), |
2747 ) | 2755 ) |
2748 | 2756 |
2749 | 2757 |
2750 class transformingwriter(object): | 2758 class transformingwriter(object): |
2751 """Writable file wrapper to transform data by function""" | 2759 """Writable file wrapper to transform data by function""" |
2769 # stray CR is an error. | 2777 # stray CR is an error. |
2770 _eolre = remod.compile(br'\r*\n') | 2778 _eolre = remod.compile(br'\r*\n') |
2771 | 2779 |
2772 | 2780 |
2773 def tolf(s): | 2781 def tolf(s): |
2774 return _eolre.sub('\n', s) | 2782 return _eolre.sub(b'\n', s) |
2775 | 2783 |
2776 | 2784 |
2777 def tocrlf(s): | 2785 def tocrlf(s): |
2778 return _eolre.sub('\r\n', s) | 2786 return _eolre.sub(b'\r\n', s) |
2779 | 2787 |
2780 | 2788 |
2781 def _crlfwriter(fp): | 2789 def _crlfwriter(fp): |
2782 return transformingwriter(fp, tocrlf) | 2790 return transformingwriter(fp, tocrlf) |
2783 | 2791 |
2784 | 2792 |
2785 if pycompat.oslinesep == '\r\n': | 2793 if pycompat.oslinesep == b'\r\n': |
2786 tonativeeol = tocrlf | 2794 tonativeeol = tocrlf |
2787 fromnativeeol = tolf | 2795 fromnativeeol = tolf |
2788 nativeeolwriter = _crlfwriter | 2796 nativeeolwriter = _crlfwriter |
2789 else: | 2797 else: |
2790 tonativeeol = pycompat.identity | 2798 tonativeeol = pycompat.identity |
2791 fromnativeeol = pycompat.identity | 2799 fromnativeeol = pycompat.identity |
2792 nativeeolwriter = pycompat.identity | 2800 nativeeolwriter = pycompat.identity |
2793 | 2801 |
2794 if pyplatform.python_implementation() == 'CPython' and sys.version_info < ( | 2802 if pyplatform.python_implementation() == b'CPython' and sys.version_info < ( |
2795 3, | 2803 3, |
2796 0, | 2804 0, |
2797 ): | 2805 ): |
2798 # There is an issue in CPython that some IO methods do not handle EINTR | 2806 # There is an issue in CPython that some IO methods do not handle EINTR |
2799 # correctly. The following table shows what CPython version (and functions) | 2807 # correctly. The following table shows what CPython version (and functions) |
2820 # files approximately as "fast" files and use the fast (unsafe) code path, | 2828 # files approximately as "fast" files and use the fast (unsafe) code path, |
2821 # to minimize the performance impact. | 2829 # to minimize the performance impact. |
2822 if sys.version_info >= (2, 7, 4): | 2830 if sys.version_info >= (2, 7, 4): |
2823 # fp.readline deals with EINTR correctly, use it as a workaround. | 2831 # fp.readline deals with EINTR correctly, use it as a workaround. |
2824 def _safeiterfile(fp): | 2832 def _safeiterfile(fp): |
2825 return iter(fp.readline, '') | 2833 return iter(fp.readline, b'') |
2826 | 2834 |
2827 else: | 2835 else: |
2828 # fp.read* are broken too, manually deal with EINTR in a stupid way. | 2836 # fp.read* are broken too, manually deal with EINTR in a stupid way. |
2829 # note: this may block longer than necessary because of bufsize. | 2837 # note: this may block longer than necessary because of bufsize. |
2830 def _safeiterfile(fp, bufsize=4096): | 2838 def _safeiterfile(fp, bufsize=4096): |
2831 fd = fp.fileno() | 2839 fd = fp.fileno() |
2832 line = '' | 2840 line = b'' |
2833 while True: | 2841 while True: |
2834 try: | 2842 try: |
2835 buf = os.read(fd, bufsize) | 2843 buf = os.read(fd, bufsize) |
2836 except OSError as ex: | 2844 except OSError as ex: |
2837 # os.read only raises EINTR before any data is read | 2845 # os.read only raises EINTR before any data is read |
2838 if ex.errno == errno.EINTR: | 2846 if ex.errno == errno.EINTR: |
2839 continue | 2847 continue |
2840 else: | 2848 else: |
2841 raise | 2849 raise |
2842 line += buf | 2850 line += buf |
2843 if '\n' in buf: | 2851 if b'\n' in buf: |
2844 splitted = line.splitlines(True) | 2852 splitted = line.splitlines(True) |
2845 line = '' | 2853 line = b'' |
2846 for l in splitted: | 2854 for l in splitted: |
2847 if l[-1] == '\n': | 2855 if l[-1] == b'\n': |
2848 yield l | 2856 yield l |
2849 else: | 2857 else: |
2850 line = l | 2858 line = l |
2851 if not buf: | 2859 if not buf: |
2852 break | 2860 break |
2891 | 2899 |
2892 escape_prefix is an optional flag that allows using doubled prefix for | 2900 escape_prefix is an optional flag that allows using doubled prefix for |
2893 its escaping. | 2901 its escaping. |
2894 """ | 2902 """ |
2895 fn = fn or (lambda s: s) | 2903 fn = fn or (lambda s: s) |
2896 patterns = '|'.join(mapping.keys()) | 2904 patterns = b'|'.join(mapping.keys()) |
2897 if escape_prefix: | 2905 if escape_prefix: |
2898 patterns += '|' + prefix | 2906 patterns += b'|' + prefix |
2899 if len(prefix) > 1: | 2907 if len(prefix) > 1: |
2900 prefix_char = prefix[1:] | 2908 prefix_char = prefix[1:] |
2901 else: | 2909 else: |
2902 prefix_char = prefix | 2910 prefix_char = prefix |
2903 mapping[prefix_char] = prefix_char | 2911 mapping[prefix_char] = prefix_char |
2919 | 2927 |
2920 try: | 2928 try: |
2921 return socket.getservbyname(pycompat.sysstr(port)) | 2929 return socket.getservbyname(pycompat.sysstr(port)) |
2922 except socket.error: | 2930 except socket.error: |
2923 raise error.Abort( | 2931 raise error.Abort( |
2924 _("no port number associated with service '%s'") % port | 2932 _(b"no port number associated with service '%s'") % port |
2925 ) | 2933 ) |
2926 | 2934 |
2927 | 2935 |
2928 class url(object): | 2936 class url(object): |
2929 r"""Reliable URL parser. | 2937 r"""Reliable URL parser. |
2997 | 3005 |
2998 >>> url(b'http:') | 3006 >>> url(b'http:') |
2999 <url scheme: 'http'> | 3007 <url scheme: 'http'> |
3000 """ | 3008 """ |
3001 | 3009 |
3002 _safechars = "!~*'()+" | 3010 _safechars = b"!~*'()+" |
3003 _safepchars = "/!~*'()+:\\" | 3011 _safepchars = b"/!~*'()+:\\" |
3004 _matchscheme = remod.compile('^[a-zA-Z0-9+.\\-]+:').match | 3012 _matchscheme = remod.compile(b'^[a-zA-Z0-9+.\\-]+:').match |
3005 | 3013 |
3006 def __init__(self, path, parsequery=True, parsefragment=True): | 3014 def __init__(self, path, parsequery=True, parsefragment=True): |
3007 # We slowly chomp away at path until we have only the path left | 3015 # We slowly chomp away at path until we have only the path left |
3008 self.scheme = self.user = self.passwd = self.host = None | 3016 self.scheme = self.user = self.passwd = self.host = None |
3009 self.port = self.path = self.query = self.fragment = None | 3017 self.port = self.path = self.query = self.fragment = None |
3010 self._localpath = True | 3018 self._localpath = True |
3011 self._hostport = '' | 3019 self._hostport = b'' |
3012 self._origpath = path | 3020 self._origpath = path |
3013 | 3021 |
3014 if parsefragment and '#' in path: | 3022 if parsefragment and b'#' in path: |
3015 path, self.fragment = path.split('#', 1) | 3023 path, self.fragment = path.split(b'#', 1) |
3016 | 3024 |
3017 # special case for Windows drive letters and UNC paths | 3025 # special case for Windows drive letters and UNC paths |
3018 if hasdriveletter(path) or path.startswith('\\\\'): | 3026 if hasdriveletter(path) or path.startswith(b'\\\\'): |
3019 self.path = path | 3027 self.path = path |
3020 return | 3028 return |
3021 | 3029 |
3022 # For compatibility reasons, we can't handle bundle paths as | 3030 # For compatibility reasons, we can't handle bundle paths as |
3023 # normal URLS | 3031 # normal URLS |
3024 if path.startswith('bundle:'): | 3032 if path.startswith(b'bundle:'): |
3025 self.scheme = 'bundle' | 3033 self.scheme = b'bundle' |
3026 path = path[7:] | 3034 path = path[7:] |
3027 if path.startswith('//'): | 3035 if path.startswith(b'//'): |
3028 path = path[2:] | 3036 path = path[2:] |
3029 self.path = path | 3037 self.path = path |
3030 return | 3038 return |
3031 | 3039 |
3032 if self._matchscheme(path): | 3040 if self._matchscheme(path): |
3033 parts = path.split(':', 1) | 3041 parts = path.split(b':', 1) |
3034 if parts[0]: | 3042 if parts[0]: |
3035 self.scheme, path = parts | 3043 self.scheme, path = parts |
3036 self._localpath = False | 3044 self._localpath = False |
3037 | 3045 |
3038 if not path: | 3046 if not path: |
3039 path = None | 3047 path = None |
3040 if self._localpath: | 3048 if self._localpath: |
3041 self.path = '' | 3049 self.path = b'' |
3042 return | 3050 return |
3043 else: | 3051 else: |
3044 if self._localpath: | 3052 if self._localpath: |
3045 self.path = path | 3053 self.path = path |
3046 return | 3054 return |
3047 | 3055 |
3048 if parsequery and '?' in path: | 3056 if parsequery and b'?' in path: |
3049 path, self.query = path.split('?', 1) | 3057 path, self.query = path.split(b'?', 1) |
3050 if not path: | 3058 if not path: |
3051 path = None | 3059 path = None |
3052 if not self.query: | 3060 if not self.query: |
3053 self.query = None | 3061 self.query = None |
3054 | 3062 |
3055 # // is required to specify a host/authority | 3063 # // is required to specify a host/authority |
3056 if path and path.startswith('//'): | 3064 if path and path.startswith(b'//'): |
3057 parts = path[2:].split('/', 1) | 3065 parts = path[2:].split(b'/', 1) |
3058 if len(parts) > 1: | 3066 if len(parts) > 1: |
3059 self.host, path = parts | 3067 self.host, path = parts |
3060 else: | 3068 else: |
3061 self.host = parts[0] | 3069 self.host = parts[0] |
3062 path = None | 3070 path = None |
3063 if not self.host: | 3071 if not self.host: |
3064 self.host = None | 3072 self.host = None |
3065 # path of file:///d is /d | 3073 # path of file:///d is /d |
3066 # path of file:///d:/ is d:/, not /d:/ | 3074 # path of file:///d:/ is d:/, not /d:/ |
3067 if path and not hasdriveletter(path): | 3075 if path and not hasdriveletter(path): |
3068 path = '/' + path | 3076 path = b'/' + path |
3069 | 3077 |
3070 if self.host and '@' in self.host: | 3078 if self.host and b'@' in self.host: |
3071 self.user, self.host = self.host.rsplit('@', 1) | 3079 self.user, self.host = self.host.rsplit(b'@', 1) |
3072 if ':' in self.user: | 3080 if b':' in self.user: |
3073 self.user, self.passwd = self.user.split(':', 1) | 3081 self.user, self.passwd = self.user.split(b':', 1) |
3074 if not self.host: | 3082 if not self.host: |
3075 self.host = None | 3083 self.host = None |
3076 | 3084 |
3077 # Don't split on colons in IPv6 addresses without ports | 3085 # Don't split on colons in IPv6 addresses without ports |
3078 if ( | 3086 if ( |
3079 self.host | 3087 self.host |
3080 and ':' in self.host | 3088 and b':' in self.host |
3081 and not (self.host.startswith('[') and self.host.endswith(']')) | 3089 and not ( |
3090 self.host.startswith(b'[') and self.host.endswith(b']') | |
3091 ) | |
3082 ): | 3092 ): |
3083 self._hostport = self.host | 3093 self._hostport = self.host |
3084 self.host, self.port = self.host.rsplit(':', 1) | 3094 self.host, self.port = self.host.rsplit(b':', 1) |
3085 if not self.host: | 3095 if not self.host: |
3086 self.host = None | 3096 self.host = None |
3087 | 3097 |
3088 if ( | 3098 if ( |
3089 self.host | 3099 self.host |
3090 and self.scheme == 'file' | 3100 and self.scheme == b'file' |
3091 and self.host not in ('localhost', '127.0.0.1', '[::1]') | 3101 and self.host not in (b'localhost', b'127.0.0.1', b'[::1]') |
3092 ): | 3102 ): |
3093 raise error.Abort(_('file:// URLs can only refer to localhost')) | 3103 raise error.Abort( |
3104 _(b'file:// URLs can only refer to localhost') | |
3105 ) | |
3094 | 3106 |
3095 self.path = path | 3107 self.path = path |
3096 | 3108 |
3097 # leave the query string escaped | 3109 # leave the query string escaped |
3098 for a in ('user', 'passwd', 'host', 'port', 'path', 'fragment'): | 3110 for a in (b'user', b'passwd', b'host', b'port', b'path', b'fragment'): |
3099 v = getattr(self, a) | 3111 v = getattr(self, a) |
3100 if v is not None: | 3112 if v is not None: |
3101 setattr(self, a, urlreq.unquote(v)) | 3113 setattr(self, a, urlreq.unquote(v)) |
3102 | 3114 |
3103 @encoding.strmethod | 3115 @encoding.strmethod |
3104 def __repr__(self): | 3116 def __repr__(self): |
3105 attrs = [] | 3117 attrs = [] |
3106 for a in ( | 3118 for a in ( |
3107 'scheme', | 3119 b'scheme', |
3108 'user', | 3120 b'user', |
3109 'passwd', | 3121 b'passwd', |
3110 'host', | 3122 b'host', |
3111 'port', | 3123 b'port', |
3112 'path', | 3124 b'path', |
3113 'query', | 3125 b'query', |
3114 'fragment', | 3126 b'fragment', |
3115 ): | 3127 ): |
3116 v = getattr(self, a) | 3128 v = getattr(self, a) |
3117 if v is not None: | 3129 if v is not None: |
3118 attrs.append('%s: %r' % (a, pycompat.bytestr(v))) | 3130 attrs.append(b'%s: %r' % (a, pycompat.bytestr(v))) |
3119 return '<url %s>' % ', '.join(attrs) | 3131 return b'<url %s>' % b', '.join(attrs) |
3120 | 3132 |
3121 def __bytes__(self): | 3133 def __bytes__(self): |
3122 r"""Join the URL's components back into a URL string. | 3134 r"""Join the URL's components back into a URL string. |
3123 | 3135 |
3124 Examples: | 3136 Examples: |
3152 >>> print(url(br'file:///D:\data\hg')) | 3164 >>> print(url(br'file:///D:\data\hg')) |
3153 file:///D:\data\hg | 3165 file:///D:\data\hg |
3154 """ | 3166 """ |
3155 if self._localpath: | 3167 if self._localpath: |
3156 s = self.path | 3168 s = self.path |
3157 if self.scheme == 'bundle': | 3169 if self.scheme == b'bundle': |
3158 s = 'bundle:' + s | 3170 s = b'bundle:' + s |
3159 if self.fragment: | 3171 if self.fragment: |
3160 s += '#' + self.fragment | 3172 s += b'#' + self.fragment |
3161 return s | 3173 return s |
3162 | 3174 |
3163 s = self.scheme + ':' | 3175 s = self.scheme + b':' |
3164 if self.user or self.passwd or self.host: | 3176 if self.user or self.passwd or self.host: |
3165 s += '//' | 3177 s += b'//' |
3166 elif self.scheme and ( | 3178 elif self.scheme and ( |
3167 not self.path | 3179 not self.path |
3168 or self.path.startswith('/') | 3180 or self.path.startswith(b'/') |
3169 or hasdriveletter(self.path) | 3181 or hasdriveletter(self.path) |
3170 ): | 3182 ): |
3171 s += '//' | 3183 s += b'//' |
3172 if hasdriveletter(self.path): | 3184 if hasdriveletter(self.path): |
3173 s += '/' | 3185 s += b'/' |
3174 if self.user: | 3186 if self.user: |
3175 s += urlreq.quote(self.user, safe=self._safechars) | 3187 s += urlreq.quote(self.user, safe=self._safechars) |
3176 if self.passwd: | 3188 if self.passwd: |
3177 s += ':' + urlreq.quote(self.passwd, safe=self._safechars) | 3189 s += b':' + urlreq.quote(self.passwd, safe=self._safechars) |
3178 if self.user or self.passwd: | 3190 if self.user or self.passwd: |
3179 s += '@' | 3191 s += b'@' |
3180 if self.host: | 3192 if self.host: |
3181 if not (self.host.startswith('[') and self.host.endswith(']')): | 3193 if not (self.host.startswith(b'[') and self.host.endswith(b']')): |
3182 s += urlreq.quote(self.host) | 3194 s += urlreq.quote(self.host) |
3183 else: | 3195 else: |
3184 s += self.host | 3196 s += self.host |
3185 if self.port: | 3197 if self.port: |
3186 s += ':' + urlreq.quote(self.port) | 3198 s += b':' + urlreq.quote(self.port) |
3187 if self.host: | 3199 if self.host: |
3188 s += '/' | 3200 s += b'/' |
3189 if self.path: | 3201 if self.path: |
3190 # TODO: similar to the query string, we should not unescape the | 3202 # TODO: similar to the query string, we should not unescape the |
3191 # path when we store it, the path might contain '%2f' = '/', | 3203 # path when we store it, the path might contain '%2f' = '/', |
3192 # which we should *not* escape. | 3204 # which we should *not* escape. |
3193 s += urlreq.quote(self.path, safe=self._safepchars) | 3205 s += urlreq.quote(self.path, safe=self._safepchars) |
3194 if self.query: | 3206 if self.query: |
3195 # we store the query in escaped form. | 3207 # we store the query in escaped form. |
3196 s += '?' + self.query | 3208 s += b'?' + self.query |
3197 if self.fragment is not None: | 3209 if self.fragment is not None: |
3198 s += '#' + urlreq.quote(self.fragment, safe=self._safepchars) | 3210 s += b'#' + urlreq.quote(self.fragment, safe=self._safepchars) |
3199 return s | 3211 return s |
3200 | 3212 |
3201 __str__ = encoding.strmethod(__bytes__) | 3213 __str__ = encoding.strmethod(__bytes__) |
3202 | 3214 |
3203 def authinfo(self): | 3215 def authinfo(self): |
3211 return (s, None) | 3223 return (s, None) |
3212 # authinfo[1] is passed to urllib2 password manager, and its | 3224 # authinfo[1] is passed to urllib2 password manager, and its |
3213 # URIs must not contain credentials. The host is passed in the | 3225 # URIs must not contain credentials. The host is passed in the |
3214 # URIs list because Python < 2.4.3 uses only that to search for | 3226 # URIs list because Python < 2.4.3 uses only that to search for |
3215 # a password. | 3227 # a password. |
3216 return (s, (None, (s, self.host), self.user, self.passwd or '')) | 3228 return (s, (None, (s, self.host), self.user, self.passwd or b'')) |
3217 | 3229 |
3218 def isabs(self): | 3230 def isabs(self): |
3219 if self.scheme and self.scheme != 'file': | 3231 if self.scheme and self.scheme != b'file': |
3220 return True # remote URL | 3232 return True # remote URL |
3221 if hasdriveletter(self.path): | 3233 if hasdriveletter(self.path): |
3222 return True # absolute for our purposes - can't be joined() | 3234 return True # absolute for our purposes - can't be joined() |
3223 if self.path.startswith(br'\\'): | 3235 if self.path.startswith(br'\\'): |
3224 return True # Windows UNC path | 3236 return True # Windows UNC path |
3225 if self.path.startswith('/'): | 3237 if self.path.startswith(b'/'): |
3226 return True # POSIX-style | 3238 return True # POSIX-style |
3227 return False | 3239 return False |
3228 | 3240 |
3229 def localpath(self): | 3241 def localpath(self): |
3230 if self.scheme == 'file' or self.scheme == 'bundle': | 3242 if self.scheme == b'file' or self.scheme == b'bundle': |
3231 path = self.path or '/' | 3243 path = self.path or b'/' |
3232 # For Windows, we need to promote hosts containing drive | 3244 # For Windows, we need to promote hosts containing drive |
3233 # letters to paths with drive letters. | 3245 # letters to paths with drive letters. |
3234 if hasdriveletter(self._hostport): | 3246 if hasdriveletter(self._hostport): |
3235 path = self._hostport + '/' + self.path | 3247 path = self._hostport + b'/' + self.path |
3236 elif ( | 3248 elif ( |
3237 self.host is not None and self.path and not hasdriveletter(path) | 3249 self.host is not None and self.path and not hasdriveletter(path) |
3238 ): | 3250 ): |
3239 path = '/' + path | 3251 path = b'/' + path |
3240 return path | 3252 return path |
3241 return self._origpath | 3253 return self._origpath |
3242 | 3254 |
3243 def islocal(self): | 3255 def islocal(self): |
3244 '''whether localpath will return something that posixfile can open''' | 3256 '''whether localpath will return something that posixfile can open''' |
3245 return ( | 3257 return ( |
3246 not self.scheme or self.scheme == 'file' or self.scheme == 'bundle' | 3258 not self.scheme |
3259 or self.scheme == b'file' | |
3260 or self.scheme == b'bundle' | |
3247 ) | 3261 ) |
3248 | 3262 |
3249 | 3263 |
3250 def hasscheme(path): | 3264 def hasscheme(path): |
3251 return bool(url(path).scheme) | 3265 return bool(url(path).scheme) |
3252 | 3266 |
3253 | 3267 |
3254 def hasdriveletter(path): | 3268 def hasdriveletter(path): |
3255 return path and path[1:2] == ':' and path[0:1].isalpha() | 3269 return path and path[1:2] == b':' and path[0:1].isalpha() |
3256 | 3270 |
3257 | 3271 |
3258 def urllocalpath(path): | 3272 def urllocalpath(path): |
3259 return url(path, parsequery=False, parsefragment=False).localpath() | 3273 return url(path, parsequery=False, parsefragment=False).localpath() |
3260 | 3274 |
3268 user. | 3282 user. |
3269 | 3283 |
3270 Raises an error.Abort when the url is unsafe. | 3284 Raises an error.Abort when the url is unsafe. |
3271 """ | 3285 """ |
3272 path = urlreq.unquote(path) | 3286 path = urlreq.unquote(path) |
3273 if path.startswith('ssh://-') or path.startswith('svn+ssh://-'): | 3287 if path.startswith(b'ssh://-') or path.startswith(b'svn+ssh://-'): |
3274 raise error.Abort( | 3288 raise error.Abort( |
3275 _('potentially unsafe url: %r') % (pycompat.bytestr(path),) | 3289 _(b'potentially unsafe url: %r') % (pycompat.bytestr(path),) |
3276 ) | 3290 ) |
3277 | 3291 |
3278 | 3292 |
3279 def hidepassword(u): | 3293 def hidepassword(u): |
3280 '''hide user credential in a url string''' | 3294 '''hide user credential in a url string''' |
3281 u = url(u) | 3295 u = url(u) |
3282 if u.passwd: | 3296 if u.passwd: |
3283 u.passwd = '***' | 3297 u.passwd = b'***' |
3284 return bytes(u) | 3298 return bytes(u) |
3285 | 3299 |
3286 | 3300 |
3287 def removeauth(u): | 3301 def removeauth(u): |
3288 '''remove all authentication information from a url string''' | 3302 '''remove all authentication information from a url string''' |
3290 u.user = u.passwd = None | 3304 u.user = u.passwd = None |
3291 return bytes(u) | 3305 return bytes(u) |
3292 | 3306 |
3293 | 3307 |
3294 timecount = unitcountfn( | 3308 timecount = unitcountfn( |
3295 (1, 1e3, _('%.0f s')), | 3309 (1, 1e3, _(b'%.0f s')), |
3296 (100, 1, _('%.1f s')), | 3310 (100, 1, _(b'%.1f s')), |
3297 (10, 1, _('%.2f s')), | 3311 (10, 1, _(b'%.2f s')), |
3298 (1, 1, _('%.3f s')), | 3312 (1, 1, _(b'%.3f s')), |
3299 (100, 0.001, _('%.1f ms')), | 3313 (100, 0.001, _(b'%.1f ms')), |
3300 (10, 0.001, _('%.2f ms')), | 3314 (10, 0.001, _(b'%.2f ms')), |
3301 (1, 0.001, _('%.3f ms')), | 3315 (1, 0.001, _(b'%.3f ms')), |
3302 (100, 0.000001, _('%.1f us')), | 3316 (100, 0.000001, _(b'%.1f us')), |
3303 (10, 0.000001, _('%.2f us')), | 3317 (10, 0.000001, _(b'%.2f us')), |
3304 (1, 0.000001, _('%.3f us')), | 3318 (1, 0.000001, _(b'%.3f us')), |
3305 (100, 0.000000001, _('%.1f ns')), | 3319 (100, 0.000000001, _(b'%.1f ns')), |
3306 (10, 0.000000001, _('%.2f ns')), | 3320 (10, 0.000000001, _(b'%.2f ns')), |
3307 (1, 0.000000001, _('%.3f ns')), | 3321 (1, 0.000000001, _(b'%.3f ns')), |
3308 ) | 3322 ) |
3309 | 3323 |
3310 | 3324 |
3311 @attr.s | 3325 @attr.s |
3312 class timedcmstats(object): | 3326 class timedcmstats(object): |
3320 elapsed = attr.ib(default=0) | 3334 elapsed = attr.ib(default=0) |
3321 # the number of nested timedcm context managers. | 3335 # the number of nested timedcm context managers. |
3322 level = attr.ib(default=1) | 3336 level = attr.ib(default=1) |
3323 | 3337 |
3324 def __bytes__(self): | 3338 def __bytes__(self): |
3325 return timecount(self.elapsed) if self.elapsed else '<unknown>' | 3339 return timecount(self.elapsed) if self.elapsed else b'<unknown>' |
3326 | 3340 |
3327 __str__ = encoding.strmethod(__bytes__) | 3341 __str__ = encoding.strmethod(__bytes__) |
3328 | 3342 |
3329 | 3343 |
3330 @contextlib.contextmanager | 3344 @contextlib.contextmanager |
3364 def wrapper(*args, **kwargs): | 3378 def wrapper(*args, **kwargs): |
3365 with timedcm(pycompat.bytestr(func.__name__)) as time_stats: | 3379 with timedcm(pycompat.bytestr(func.__name__)) as time_stats: |
3366 result = func(*args, **kwargs) | 3380 result = func(*args, **kwargs) |
3367 stderr = procutil.stderr | 3381 stderr = procutil.stderr |
3368 stderr.write( | 3382 stderr.write( |
3369 '%s%s: %s\n' | 3383 b'%s%s: %s\n' |
3370 % ( | 3384 % ( |
3371 ' ' * time_stats.level * 2, | 3385 b' ' * time_stats.level * 2, |
3372 pycompat.bytestr(func.__name__), | 3386 pycompat.bytestr(func.__name__), |
3373 time_stats, | 3387 time_stats, |
3374 ) | 3388 ) |
3375 ) | 3389 ) |
3376 return result | 3390 return result |
3377 | 3391 |
3378 return wrapper | 3392 return wrapper |
3379 | 3393 |
3380 | 3394 |
3381 _sizeunits = ( | 3395 _sizeunits = ( |
3382 ('m', 2 ** 20), | 3396 (b'm', 2 ** 20), |
3383 ('k', 2 ** 10), | 3397 (b'k', 2 ** 10), |
3384 ('g', 2 ** 30), | 3398 (b'g', 2 ** 30), |
3385 ('kb', 2 ** 10), | 3399 (b'kb', 2 ** 10), |
3386 ('mb', 2 ** 20), | 3400 (b'mb', 2 ** 20), |
3387 ('gb', 2 ** 30), | 3401 (b'gb', 2 ** 30), |
3388 ('b', 1), | 3402 (b'b', 1), |
3389 ) | 3403 ) |
3390 | 3404 |
3391 | 3405 |
3392 def sizetoint(s): | 3406 def sizetoint(s): |
3393 '''Convert a space specifier to a byte count. | 3407 '''Convert a space specifier to a byte count. |
3404 for k, u in _sizeunits: | 3418 for k, u in _sizeunits: |
3405 if t.endswith(k): | 3419 if t.endswith(k): |
3406 return int(float(t[: -len(k)]) * u) | 3420 return int(float(t[: -len(k)]) * u) |
3407 return int(t) | 3421 return int(t) |
3408 except ValueError: | 3422 except ValueError: |
3409 raise error.ParseError(_("couldn't parse size: %s") % s) | 3423 raise error.ParseError(_(b"couldn't parse size: %s") % s) |
3410 | 3424 |
3411 | 3425 |
3412 class hooks(object): | 3426 class hooks(object): |
3413 '''A collection of hook functions that can be used to extend a | 3427 '''A collection of hook functions that can be used to extend a |
3414 function's behavior. Hooks are called in lexicographic order, | 3428 function's behavior. Hooks are called in lexicographic order, |
3426 for source, hook in self._hooks: | 3440 for source, hook in self._hooks: |
3427 results.append(hook(*args)) | 3441 results.append(hook(*args)) |
3428 return results | 3442 return results |
3429 | 3443 |
3430 | 3444 |
3431 def getstackframes(skip=0, line=' %-*s in %s\n', fileline='%s:%d', depth=0): | 3445 def getstackframes(skip=0, line=b' %-*s in %s\n', fileline=b'%s:%d', depth=0): |
3432 '''Yields lines for a nicely formatted stacktrace. | 3446 '''Yields lines for a nicely formatted stacktrace. |
3433 Skips the 'skip' last entries, then return the last 'depth' entries. | 3447 Skips the 'skip' last entries, then return the last 'depth' entries. |
3434 Each file+linenumber is formatted according to fileline. | 3448 Each file+linenumber is formatted according to fileline. |
3435 Each line is formatted according to line. | 3449 Each line is formatted according to line. |
3436 If line is None, it yields: | 3450 If line is None, it yields: |
3452 else: | 3466 else: |
3453 yield line % (fnmax, fnln, func) | 3467 yield line % (fnmax, fnln, func) |
3454 | 3468 |
3455 | 3469 |
3456 def debugstacktrace( | 3470 def debugstacktrace( |
3457 msg='stacktrace', skip=0, f=procutil.stderr, otherf=procutil.stdout, depth=0 | 3471 msg=b'stacktrace', |
3472 skip=0, | |
3473 f=procutil.stderr, | |
3474 otherf=procutil.stdout, | |
3475 depth=0, | |
3458 ): | 3476 ): |
3459 '''Writes a message to f (stderr) with a nicely formatted stacktrace. | 3477 '''Writes a message to f (stderr) with a nicely formatted stacktrace. |
3460 Skips the 'skip' entries closest to the call, then show 'depth' entries. | 3478 Skips the 'skip' entries closest to the call, then show 'depth' entries. |
3461 By default it will flush stdout first. | 3479 By default it will flush stdout first. |
3462 It can be used everywhere and intentionally does not require an ui object. | 3480 It can be used everywhere and intentionally does not require an ui object. |
3463 Not be used in production code but very convenient while developing. | 3481 Not be used in production code but very convenient while developing. |
3464 ''' | 3482 ''' |
3465 if otherf: | 3483 if otherf: |
3466 otherf.flush() | 3484 otherf.flush() |
3467 f.write('%s at:\n' % msg.rstrip()) | 3485 f.write(b'%s at:\n' % msg.rstrip()) |
3468 for line in getstackframes(skip + 1, depth=depth): | 3486 for line in getstackframes(skip + 1, depth=depth): |
3469 f.write(line) | 3487 f.write(line) |
3470 f.flush() | 3488 f.flush() |
3471 | 3489 |
3472 | 3490 |
3480 for f, s in map.iteritems(): | 3498 for f, s in map.iteritems(): |
3481 if s[0] != skip: | 3499 if s[0] != skip: |
3482 addpath(f) | 3500 addpath(f) |
3483 elif skip is not None: | 3501 elif skip is not None: |
3484 raise error.ProgrammingError( | 3502 raise error.ProgrammingError( |
3485 "skip character is only supported " "with a dict source" | 3503 b"skip character is only supported " b"with a dict source" |
3486 ) | 3504 ) |
3487 else: | 3505 else: |
3488 for f in map: | 3506 for f in map: |
3489 addpath(f) | 3507 addpath(f) |
3490 | 3508 |
3517 if rustdirs is not None: | 3535 if rustdirs is not None: |
3518 dirs = rustdirs | 3536 dirs = rustdirs |
3519 | 3537 |
3520 | 3538 |
3521 def finddirs(path): | 3539 def finddirs(path): |
3522 pos = path.rfind('/') | 3540 pos = path.rfind(b'/') |
3523 while pos != -1: | 3541 while pos != -1: |
3524 yield path[:pos] | 3542 yield path[:pos] |
3525 pos = path.rfind('/', 0, pos) | 3543 pos = path.rfind(b'/', 0, pos) |
3526 yield '' | 3544 yield b'' |
3527 | 3545 |
3528 | 3546 |
3529 # convenient shortcut | 3547 # convenient shortcut |
3530 dst = debugstacktrace | 3548 dst = debugstacktrace |
3531 | 3549 |
3543 in the provided context and is not in the set of other names. | 3561 in the provided context and is not in the set of other names. |
3544 """ | 3562 """ |
3545 if others is None: | 3563 if others is None: |
3546 others = set() | 3564 others = set() |
3547 | 3565 |
3548 fn = '%s~%s' % (f, tag) | 3566 fn = b'%s~%s' % (f, tag) |
3549 if fn not in ctx and fn not in others: | 3567 if fn not in ctx and fn not in others: |
3550 return fn | 3568 return fn |
3551 for n in itertools.count(1): | 3569 for n in itertools.count(1): |
3552 fn = '%s~%s~%s' % (f, tag, n) | 3570 fn = b'%s~%s~%s' % (f, tag, n) |
3553 if fn not in ctx and fn not in others: | 3571 if fn not in ctx and fn not in others: |
3554 return fn | 3572 return fn |
3555 | 3573 |
3556 | 3574 |
3557 def readexactly(stream, n): | 3575 def readexactly(stream, n): |
3558 '''read n bytes from stream.read and abort if less was available''' | 3576 '''read n bytes from stream.read and abort if less was available''' |
3559 s = stream.read(n) | 3577 s = stream.read(n) |
3560 if len(s) < n: | 3578 if len(s) < n: |
3561 raise error.Abort( | 3579 raise error.Abort( |
3562 _("stream ended unexpectedly" " (got %d bytes, expected %d)") | 3580 _(b"stream ended unexpectedly" b" (got %d bytes, expected %d)") |
3563 % (len(s), n) | 3581 % (len(s), n) |
3564 ) | 3582 ) |
3565 return s | 3583 return s |
3566 | 3584 |
3567 | 3585 |
3587 Traceback (most recent call last): | 3605 Traceback (most recent call last): |
3588 ... | 3606 ... |
3589 ProgrammingError: negative value for uvarint: -1 | 3607 ProgrammingError: negative value for uvarint: -1 |
3590 """ | 3608 """ |
3591 if value < 0: | 3609 if value < 0: |
3592 raise error.ProgrammingError('negative value for uvarint: %d' % value) | 3610 raise error.ProgrammingError(b'negative value for uvarint: %d' % value) |
3593 bits = value & 0x7F | 3611 bits = value & 0x7F |
3594 value >>= 7 | 3612 value >>= 7 |
3595 bytes = [] | 3613 bytes = [] |
3596 while value: | 3614 while value: |
3597 bytes.append(pycompat.bytechr(0x80 | bits)) | 3615 bytes.append(pycompat.bytechr(0x80 | bits)) |
3598 bits = value & 0x7F | 3616 bits = value & 0x7F |
3599 value >>= 7 | 3617 value >>= 7 |
3600 bytes.append(pycompat.bytechr(bits)) | 3618 bytes.append(pycompat.bytechr(bits)) |
3601 | 3619 |
3602 return ''.join(bytes) | 3620 return b''.join(bytes) |
3603 | 3621 |
3604 | 3622 |
3605 def uvarintdecodestream(fh): | 3623 def uvarintdecodestream(fh): |
3606 """Decode an unsigned variable length integer from a stream. | 3624 """Decode an unsigned variable length integer from a stream. |
3607 | 3625 |