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