mercurial/util.py
branchstable
changeset 49366 288de6f5d724
parent 49308 d2adebe35635
child 49572 c4f07a011714
--- a/mercurial/util.py	Thu Jun 16 15:15:03 2022 +0200
+++ b/mercurial/util.py	Thu Jun 16 15:28:54 2022 +0200
@@ -13,7 +13,6 @@
 hide platform-specific details from the core.
 """
 
-from __future__ import absolute_import, print_function
 
 import abc
 import collections
@@ -21,11 +20,12 @@
 import errno
 import gc
 import hashlib
+import io
 import itertools
 import locale
 import mmap
 import os
-import platform as pyplatform
+import pickle  # provides util.pickle symbol
 import re as remod
 import shutil
 import stat
@@ -42,7 +42,6 @@
     open,
     setattr,
 )
-from .node import hex
 from hgdemandimport import tracing
 from . import (
     encoding,
@@ -76,10 +75,9 @@
 
 cookielib = pycompat.cookielib
 httplib = pycompat.httplib
-pickle = pycompat.pickle
 safehasattr = pycompat.safehasattr
 socketserver = pycompat.socketserver
-bytesio = pycompat.bytesio
+bytesio = io.BytesIO
 # TODO deprecate stringio name, as it is a lie on Python 3.
 stringio = bytesio
 xmlrpclib = pycompat.xmlrpclib
@@ -158,11 +156,6 @@
 SERVERROLE = compression.SERVERROLE
 CLIENTROLE = compression.CLIENTROLE
 
-try:
-    recvfds = osutil.recvfds
-except AttributeError:
-    pass
-
 # Python compatibility
 
 _notset = object()
@@ -189,7 +182,7 @@
     warnings.filterwarnings('default', '', DeprecationWarning, 'mercurial')
     warnings.filterwarnings('default', '', DeprecationWarning, 'hgext')
     warnings.filterwarnings('default', '', DeprecationWarning, 'hgext3rd')
-if _dowarn and pycompat.ispy3:
+if _dowarn:
     # silence warning emitted by passing user string to re.sub()
     warnings.filterwarnings(
         'ignore', 'bad escape', DeprecationWarning, 'mercurial'
@@ -233,7 +226,7 @@
     assert k in DIGESTS
 
 
-class digester(object):
+class digester:
     """helper to compute digests.
 
     This helper can be used to compute one or more digests given their name.
@@ -281,7 +274,7 @@
         return None
 
 
-class digestchecker(object):
+class digestchecker:
     """file handle wrapper that additionally checks content against a given
     size and digests.
 
@@ -331,7 +324,7 @@
 _chunksize = 4096
 
 
-class bufferedinputpipe(object):
+class bufferedinputpipe:
     """a manually buffered input pipe
 
     Python will not let us use buffered IO and lazy reading with 'polling' at
@@ -459,7 +452,7 @@
         raise
 
 
-class fileobjectproxy(object):
+class fileobjectproxy:
     """A proxy around file objects that tells a watcher when events occur.
 
     This type is intended to only be used for testing purposes. Think hard
@@ -695,7 +688,7 @@
 }
 
 
-class socketproxy(object):
+class socketproxy:
     """A proxy around a socket that tells a watcher when events occur.
 
     This is like ``fileobjectproxy`` except for sockets.
@@ -818,7 +811,7 @@
         )
 
 
-class baseproxyobserver(object):
+class baseproxyobserver:
     def __init__(self, fh, name, logdata, logdataapis):
         self.fh = fh
         self.name = name
@@ -1258,7 +1251,7 @@
     return f
 
 
-class cow(object):
+class cow:
     """helper class to make copy-on-write easier
 
     Call preparewrite before doing any writes.
@@ -1302,7 +1295,7 @@
         # __setitem__() isn't called as of PyPy 5.8.0
         def update(self, src, **f):
             if isinstance(src, dict):
-                src = pycompat.iteritems(src)
+                src = src.items()
             for k, v in src:
                 self[k] = v
             for k in f:
@@ -1351,7 +1344,7 @@
     """
 
 
-class transactional(object):  # pytype: disable=ignored-metaclass
+class transactional:  # pytype: disable=ignored-metaclass
     """Base class for making a transactional type into a context manager."""
 
     __metaclass__ = abc.ABCMeta
@@ -1402,7 +1395,7 @@
     yield enter_result
 
 
-class _lrucachenode(object):
+class _lrucachenode:
     """A node in a doubly linked list.
 
     Holds a reference to nodes on either side as well as a key-value
@@ -1426,7 +1419,7 @@
         self.cost = 0
 
 
-class lrucachedict(object):
+class lrucachedict:
     """Dict that caches most recent accesses and sets.
 
     The dict consists of an actual backing dict - indexed by original
@@ -1757,7 +1750,7 @@
     return f
 
 
-class propertycache(object):
+class propertycache:
     def __init__(self, func):
         self.func = func
         self.name = func.__name__
@@ -2216,7 +2209,7 @@
     _re2 = False
 
 
-class _re(object):
+class _re:
     def _checkre2(self):
         global _re2
         global _re2_input
@@ -2418,7 +2411,7 @@
     return temp
 
 
-class filestat(object):
+class filestat:
     """help to exactly detect change of a file
 
     'stat' attribute is result of 'os.stat()' if specified 'path'
@@ -2433,9 +2426,7 @@
     def frompath(cls, path):
         try:
             stat = os.stat(path)
-        except OSError as err:
-            if err.errno != errno.ENOENT:
-                raise
+        except FileNotFoundError:
             stat = None
         return cls(stat)
 
@@ -2512,19 +2503,17 @@
         advanced = (old.stat[stat.ST_MTIME] + 1) & 0x7FFFFFFF
         try:
             os.utime(path, (advanced, advanced))
-        except OSError as inst:
-            if inst.errno == errno.EPERM:
-                # utime() on the file created by another user causes EPERM,
-                # if a process doesn't have appropriate privileges
-                return False
-            raise
+        except PermissionError:
+            # utime() on the file created by another user causes EPERM,
+            # if a process doesn't have appropriate privileges
+            return False
         return True
 
     def __ne__(self, other):
         return not self == other
 
 
-class atomictempfile(object):
+class atomictempfile:
     """writable file object that atomically updates a file
 
     All writes will go to a temporary copy of the original file. Call
@@ -2594,6 +2583,14 @@
             self.close()
 
 
+def tryrmdir(f):
+    try:
+        removedirs(f)
+    except OSError as e:
+        if e.errno != errno.ENOENT and e.errno != errno.ENOTEMPTY:
+            raise
+
+
 def unlinkpath(f, ignoremissing=False, rmdir=True):
     # type: (bytes, bool, bool) -> None
     """unlink and remove the directory if it is empty"""
@@ -2611,12 +2608,11 @@
 
 def tryunlink(f):
     # type: (bytes) -> None
-    """Attempt to remove a file, ignoring ENOENT errors."""
+    """Attempt to remove a file, ignoring FileNotFoundError."""
     try:
         unlink(f)
-    except OSError as e:
-        if e.errno != errno.ENOENT:
-            raise
+    except FileNotFoundError:
+        pass
 
 
 def makedirs(name, mode=None, notindexed=False):
@@ -2667,7 +2663,7 @@
         fp.write(text)
 
 
-class chunkbuffer(object):
+class chunkbuffer:
     """Allow arbitrary sized chunks of data to be efficiently read from an
     iterator over chunks of arbitrary size."""
 
@@ -2772,7 +2768,7 @@
         yield s
 
 
-class cappedreader(object):
+class cappedreader:
     """A file object proxy that allows reading up to N bytes.
 
     Given a source file object, instances of this type allow reading up to
@@ -2860,7 +2856,7 @@
 )
 
 
-class transformingwriter(object):
+class transformingwriter:
     """Writable file wrapper to transform data by function"""
 
     def __init__(self, fp, encode):
@@ -2906,50 +2902,10 @@
     fromnativeeol = pycompat.identity
     nativeeolwriter = pycompat.identity
 
-if pyplatform.python_implementation() == b'CPython' and sys.version_info < (
-    3,
-    0,
-):
-    # There is an issue in CPython that some IO methods do not handle EINTR
-    # correctly. The following table shows what CPython version (and functions)
-    # are affected (buggy: has the EINTR bug, okay: otherwise):
-    #
-    #                | < 2.7.4 | 2.7.4 to 2.7.12 | >= 3.0
-    #   --------------------------------------------------
-    #    fp.__iter__ | buggy   | buggy           | okay
-    #    fp.read*    | buggy   | okay [1]        | okay
-    #
-    # [1]: fixed by changeset 67dc99a989cd in the cpython hg repo.
-    #
-    # Here we workaround the EINTR issue for fileobj.__iter__. Other methods
-    # like "read*" work fine, as we do not support Python < 2.7.4.
-    #
-    # Although we can workaround the EINTR issue for fp.__iter__, it is slower:
-    # "for x in fp" is 4x faster than "for x in iter(fp.readline, '')" in
-    # CPython 2, because CPython 2 maintains an internal readahead buffer for
-    # fp.__iter__ but not other fp.read* methods.
-    #
-    # On modern systems like Linux, the "read" syscall cannot be interrupted
-    # when reading "fast" files like on-disk files. So the EINTR issue only
-    # affects things like pipes, sockets, ttys etc. We treat "normal" (S_ISREG)
-    # files approximately as "fast" files and use the fast (unsafe) code path,
-    # to minimize the performance impact.
-
-    def iterfile(fp):
-        fastpath = True
-        if type(fp) is file:
-            fastpath = stat.S_ISREG(os.fstat(fp.fileno()).st_mode)
-        if fastpath:
-            return fp
-        else:
-            # fp.readline deals with EINTR correctly, use it as a workaround.
-            return iter(fp.readline, b'')
-
-
-else:
-    # PyPy and CPython 3 do not have the EINTR issue thus no workaround needed.
-    def iterfile(fp):
-        return fp
+
+# TODO delete since workaround variant for Python 2 no longer needed.
+def iterfile(fp):
+    return fp
 
 
 def iterlines(iterator):
@@ -3008,7 +2964,7 @@
 
 
 @attr.s
-class timedcmstats(object):
+class timedcmstats:
     """Stats information produced by the timedcm context manager on entering."""
 
     # the starting value of the timer as a float (meaning and resulution is
@@ -3109,7 +3065,7 @@
         raise error.ParseError(_(b"couldn't parse size: %s") % s)
 
 
-class hooks(object):
+class hooks:
     """A collection of hook functions that can be used to extend a
     function's behavior. Hooks are called in lexicographic order,
     based on the names of their sources."""