Python 2 is gone.
Details
Details
Diff Detail
Diff Detail
- Repository
- rHG Mercurial
- Branch
- default
- Lint
No Linters Available - Unit
No Unit Test Coverage
( )
Python 2 is gone.
No Linters Available |
No Unit Test Coverage |
Path | Packages | |||
---|---|---|---|---|
M | mercurial/httppeer.py (5 lines) | |||
M | mercurial/localrepo.py (3 lines) | |||
M | mercurial/pycompat.py (3 lines) | |||
M | mercurial/wireprotov1peer.py (11 lines) |
Commit | Parents | Author | Summary | Date |
---|---|---|---|---|
2f68c4d045af | 650ba17c9d45 | Augie Fackler | Mar 2 2022, 10:24 AM |
# httppeer.py - HTTP repository proxy classes for mercurial | # httppeer.py - HTTP repository proxy classes for mercurial | ||||
# | # | ||||
# Copyright 2005, 2006 Olivia Mackall <olivia@selenic.com> | # Copyright 2005, 2006 Olivia Mackall <olivia@selenic.com> | ||||
# Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com> | # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com> | ||||
# | # | ||||
# This software may be used and distributed according to the terms of the | # This software may be used and distributed according to the terms of the | ||||
# GNU General Public License version 2 or any later version. | # GNU General Public License version 2 or any later version. | ||||
from __future__ import absolute_import | from __future__ import absolute_import | ||||
import errno | import errno | ||||
import io | import io | ||||
import os | import os | ||||
import socket | import socket | ||||
import struct | import struct | ||||
from concurrent import futures | |||||
from .i18n import _ | from .i18n import _ | ||||
from .pycompat import getattr | from .pycompat import getattr | ||||
from . import ( | from . import ( | ||||
bundle2, | bundle2, | ||||
error, | error, | ||||
httpconnection, | httpconnection, | ||||
pycompat, | pycompat, | ||||
statichttprepo, | statichttprepo, | ||||
def _callcompressable(self, cmd, **args): | def _callcompressable(self, cmd, **args): | ||||
return self._callstream(cmd, _compressible=True, **args) | return self._callstream(cmd, _compressible=True, **args) | ||||
def _abort(self, exception): | def _abort(self, exception): | ||||
raise exception | raise exception | ||||
class queuedcommandfuture(pycompat.futures.Future): | class queuedcommandfuture(futures.Future): | ||||
"""Wraps result() on command futures to trigger submission on call.""" | """Wraps result() on command futures to trigger submission on call.""" | ||||
def result(self, timeout=None): | def result(self, timeout=None): | ||||
if self.done(): | if self.done(): | ||||
return pycompat.futures.Future.result(self, timeout) | return futures.Future.result(self, timeout) | ||||
self._peerexecutor.sendcommands() | self._peerexecutor.sendcommands() | ||||
# sendcommands() will restore the original __class__ and self.result | # sendcommands() will restore the original __class__ and self.result | ||||
# will resolve to Future.result. | # will resolve to Future.result. | ||||
return self.result(timeout) | return self.result(timeout) | ||||
import errno | import errno | ||||
import functools | import functools | ||||
import os | import os | ||||
import random | import random | ||||
import sys | import sys | ||||
import time | import time | ||||
import weakref | import weakref | ||||
from concurrent import futures | |||||
from .i18n import _ | from .i18n import _ | ||||
from .node import ( | from .node import ( | ||||
bin, | bin, | ||||
hex, | hex, | ||||
nullrev, | nullrev, | ||||
sha1nodeconstants, | sha1nodeconstants, | ||||
short, | short, | ||||
) | ) | ||||
raise error.ProgrammingError( | raise error.ProgrammingError( | ||||
b'callcommand() cannot be used after close()' | b'callcommand() cannot be used after close()' | ||||
) | ) | ||||
# We don't need to support anything fancy. Just call the named | # We don't need to support anything fancy. Just call the named | ||||
# method on the peer and return a resolved future. | # method on the peer and return a resolved future. | ||||
fn = getattr(self._peer, pycompat.sysstr(command)) | fn = getattr(self._peer, pycompat.sysstr(command)) | ||||
f = pycompat.futures.Future() | f = futures.Future() | ||||
try: | try: | ||||
result = fn(**pycompat.strkwargs(args)) | result = fn(**pycompat.strkwargs(args)) | ||||
except Exception: | except Exception: | ||||
pycompat.future_set_exception_info(f, sys.exc_info()[1:]) | pycompat.future_set_exception_info(f, sys.exc_info()[1:]) | ||||
else: | else: | ||||
f.set_result(result) | f.set_result(result) | ||||
# wireprotov1peer.py - Client-side functionality for wire protocol version 1. | # wireprotov1peer.py - Client-side functionality for wire protocol version 1. | ||||
# | # | ||||
# Copyright 2005-2010 Olivia Mackall <olivia@selenic.com> | # Copyright 2005-2010 Olivia Mackall <olivia@selenic.com> | ||||
# | # | ||||
# This software may be used and distributed according to the terms of the | # This software may be used and distributed according to the terms of the | ||||
# GNU General Public License version 2 or any later version. | # GNU General Public License version 2 or any later version. | ||||
from __future__ import absolute_import | from __future__ import absolute_import | ||||
import sys | import sys | ||||
import weakref | import weakref | ||||
from concurrent import futures | |||||
from .i18n import _ | from .i18n import _ | ||||
from .node import bin | from .node import bin | ||||
from .pycompat import ( | from .pycompat import ( | ||||
getattr, | getattr, | ||||
setattr, | setattr, | ||||
) | ) | ||||
from . import ( | from . import ( | ||||
bundle2, | bundle2, | ||||
b'%s=%s' % (escapearg(k), escapearg(v)) | b'%s=%s' % (escapearg(k), escapearg(v)) | ||||
for k, v in pycompat.iteritems(argsdict) | for k, v in pycompat.iteritems(argsdict) | ||||
) | ) | ||||
cmds.append(b'%s %s' % (op, args)) | cmds.append(b'%s %s' % (op, args)) | ||||
return b';'.join(cmds) | return b';'.join(cmds) | ||||
class unsentfuture(pycompat.futures.Future): | class unsentfuture(futures.Future): | ||||
"""A Future variation to represent an unsent command. | """A Future variation to represent an unsent command. | ||||
Because we buffer commands and don't submit them immediately, calling | Because we buffer commands and don't submit them immediately, calling | ||||
``result()`` on an unsent future could deadlock. Futures for buffered | ``result()`` on an unsent future could deadlock. Futures for buffered | ||||
commands are represented by this type, which wraps ``result()`` to | commands are represented by this type, which wraps ``result()`` to | ||||
call ``sendcommands()``. | call ``sendcommands()``. | ||||
""" | """ | ||||
def result(self, timeout=None): | def result(self, timeout=None): | ||||
if self.done(): | if self.done(): | ||||
return pycompat.futures.Future.result(self, timeout) | return futures.Future.result(self, timeout) | ||||
self._peerexecutor.sendcommands() | self._peerexecutor.sendcommands() | ||||
# This looks like it will infinitely recurse. However, | # This looks like it will infinitely recurse. However, | ||||
# sendcommands() should modify __class__. This call serves as a check | # sendcommands() should modify __class__. This call serves as a check | ||||
# on that. | # on that. | ||||
return self.result(timeout) | return self.result(timeout) | ||||
# Commands are either batchable or they aren't. If a command | # Commands are either batchable or they aren't. If a command | ||||
# isn't batchable, we send it immediately because the executor | # isn't batchable, we send it immediately because the executor | ||||
# can no longer accept new commands after a non-batchable command. | # can no longer accept new commands after a non-batchable command. | ||||
# If a command is batchable, we queue it for later. But we have | # If a command is batchable, we queue it for later. But we have | ||||
# to account for the case of a non-batchable command arriving after | # to account for the case of a non-batchable command arriving after | ||||
# a batchable one and refuse to service it. | # a batchable one and refuse to service it. | ||||
def addcall(): | def addcall(): | ||||
f = pycompat.futures.Future() | f = futures.Future() | ||||
self._futures.add(f) | self._futures.add(f) | ||||
self._calls.append((command, args, fn, f)) | self._calls.append((command, args, fn, f)) | ||||
return f | return f | ||||
if getattr(fn, 'batchable', False): | if getattr(fn, 'batchable', False): | ||||
f = addcall() | f = addcall() | ||||
# But since we don't issue it immediately, we wrap its result() | # But since we don't issue it immediately, we wrap its result() | ||||
return | return | ||||
self._sent = True | self._sent = True | ||||
# Unhack any future types so caller seens a clean type and to break | # Unhack any future types so caller seens a clean type and to break | ||||
# cycle between us and futures. | # cycle between us and futures. | ||||
for f in self._futures: | for f in self._futures: | ||||
if isinstance(f, unsentfuture): | if isinstance(f, unsentfuture): | ||||
f.__class__ = pycompat.futures.Future | f.__class__ = futures.Future | ||||
f._peerexecutor = None | f._peerexecutor = None | ||||
calls = self._calls | calls = self._calls | ||||
# Mainly to destroy references to futures. | # Mainly to destroy references to futures. | ||||
self._calls = None | self._calls = None | ||||
# Simple case of a single command. We call it synchronously. | # Simple case of a single command. We call it synchronously. | ||||
if len(calls) == 1: | if len(calls) == 1: | ||||
# This will emit responses in order they were executed. | # This will emit responses in order they were executed. | ||||
wireresults = self._peer._submitbatch(requests) | wireresults = self._peer._submitbatch(requests) | ||||
# The use of a thread pool executor here is a bit weird for something | # The use of a thread pool executor here is a bit weird for something | ||||
# that only spins up a single thread. However, thread management is | # that only spins up a single thread. However, thread management is | ||||
# hard and it is easy to encounter race conditions, deadlocks, etc. | # hard and it is easy to encounter race conditions, deadlocks, etc. | ||||
# concurrent.futures already solves these problems and its thread pool | # concurrent.futures already solves these problems and its thread pool | ||||
# executor has minimal overhead. So we use it. | # executor has minimal overhead. So we use it. | ||||
self._responseexecutor = pycompat.futures.ThreadPoolExecutor(1) | self._responseexecutor = futures.ThreadPoolExecutor(1) | ||||
self._responsef = self._responseexecutor.submit( | self._responsef = self._responseexecutor.submit( | ||||
self._readbatchresponse, states, wireresults | self._readbatchresponse, states, wireresults | ||||
) | ) | ||||
def close(self): | def close(self): | ||||
self.sendcommands() | self.sendcommands() | ||||
if self._closed: | if self._closed: |
In hindsight, we shouldn't have removed this, as the pycompat.futures symbol is part of the pycompat API, which we want to preserve to help extensions.
I'll restore this in D12247.