Mercurial > hg
view tests/dumbhttp.py @ 46667:93e9f448273c
rhg: Add support for automatic fallback to Python
`rhg` is a command-line application that can do a small subset of what
`hg` can. It is written entirely in Rust, which avoids the cost of starting
a Python interpreter and importing many Python modules.
In a script that runs many `hg` commands, this cost can add up.
However making users decide when to use `rhg` instead of `hg` is
not practical as we want the subset of supported functionality
to grow over time.
Instead we introduce "fallback" behavior where, when `rhg` encounters
something (a sub-command, a repository format, …) that is not implemented
in Rust-only, it does nothing but silently start a subprocess of
Python-based `hg` running the same command.
That way `rhg` becomes a drop-in replacement for `hg` that sometimes
goes faster. Whether Python is used should be an implementation detail
not apparent to users (other than through speed).
A new `fallback` value is added to the previously introduced
`rhg.on-unsupported` configuration key. When in this mode, the new
`rhg.fallback-executable` config is determine what command to use
to run a Python-based `hg`.
The previous `rhg.on-unsupported = abort-silent` configuration was designed
to let a wrapper script call `rhg` and then fall back to `hg` based on the
exit code. This is still available, but having fallback behavior built-in
in rhg might be easier for users instead of leaving that script "as an
exercise for the reader".
Using a subprocess like this is not idea, especially when `rhg` is to be
installed in `$PATH` as `hg`, since the other `hg.py` executable needs
to still be available… somewhere. Eventually this could be replaced
by using PyOxidizer to a have a single executable that embeds a Python
interpreter, but only starts it when needed.
Differential Revision: https://phab.mercurial-scm.org/D10093
author | Simon Sapin <simon.sapin@octobus.net> |
---|---|
date | Mon, 01 Mar 2021 20:36:06 +0100 |
parents | c102b704edb5 |
children | 23f5ed6dbcb1 |
line wrap: on
line source
#!/usr/bin/env python3 from __future__ import absolute_import """ Small and dumb HTTP server for use in tests. """ import optparse import os import signal import socket import sys from mercurial import ( encoding, pycompat, server, util, ) httpserver = util.httpserver OptionParser = optparse.OptionParser if os.environ.get('HGIPV6', '0') == '1': class simplehttpserver(httpserver.httpserver): address_family = socket.AF_INET6 else: simplehttpserver = httpserver.httpserver class _httprequesthandler(httpserver.simplehttprequesthandler): def log_message(self, format, *args): httpserver.simplehttprequesthandler.log_message(self, format, *args) sys.stderr.flush() class simplehttpservice(object): def __init__(self, host, port): self.address = (host, port) def init(self): self.httpd = simplehttpserver(self.address, _httprequesthandler) def run(self): self.httpd.serve_forever() if __name__ == '__main__': parser = OptionParser() parser.add_option( '-p', '--port', dest='port', type='int', default=8000, help='TCP port to listen on', metavar='PORT', ) parser.add_option( '-H', '--host', dest='host', default='localhost', help='hostname or IP to listen on', metavar='HOST', ) parser.add_option('--logfile', help='file name of access/error log') parser.add_option( '--pid', dest='pid', help='file name where the PID of the server is stored', ) parser.add_option( '-f', '--foreground', dest='foreground', action='store_true', help='do not start the HTTP server in the background', ) parser.add_option('--daemon-postexec', action='append') (options, args) = parser.parse_args() signal.signal(signal.SIGTERM, lambda x, y: sys.exit(0)) if options.foreground and options.logfile: parser.error( "options --logfile and --foreground are mutually " "exclusive" ) if options.foreground and options.pid: parser.error("options --pid and --foreground are mutually exclusive") opts = { b'pid_file': options.pid, b'daemon': not options.foreground, b'daemon_postexec': pycompat.rapply( encoding.strtolocal, options.daemon_postexec ), } service = simplehttpservice(options.host, options.port) runargs = [sys.executable, __file__] + sys.argv[1:] runargs = [pycompat.fsencode(a) for a in runargs] server.runservice( opts, initfn=service.init, runfn=service.run, logfile=options.logfile, runargs=runargs, )