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 |
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 |
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 |
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( |
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 |
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: |
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 |
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 |
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 |
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): |
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 |