##// END OF EJS Templates
procutil: make stream detection in make_line_buffered more correct and strict...
procutil: make stream detection in make_line_buffered more correct and strict In make_line_buffered(), we don’t want to wrap the stream if we know that lines get flushed to the underlying raw stream already. Previously, the heuristic was too optimistic. It assumed that any stream which is not an instance of io.BufferedIOBase doesn’t need wrapping. However, there are buffered streams that aren’t instances of io.BufferedIOBase, like Mercurial’s own winstdout. The new logic is different in two ways: First, only for the check, if unwraps any combination of WriteAllWrapper and winstdout. Second, it skips wrapping the stream only if it is an instance of io.RawIOBase (or already wrapped). If it is an instance of io.BufferedIOBase, it gets wrapped. In any other case, the function raises an exception. This ensures that, if an unknown stream is passed or we add another wrapper in the future, we don’t wrap the stream if it’s already line buffered or not wrap the stream if it’s not line buffered. In fact, this was already helpful during development of this change. Without it, I possibly would have forgot that WriteAllWrapper needs to be ignored for the check, leading to unnecessary wrapping if stdout is unbuffered. The alternative would have been to always wrap unknown streams. However, I don’t think that anyone would benefit from being less strict. We can expect streams from the standard library to be subclassing either io.RawIOBase or io.BufferedIOBase, so running Mercurial in the standard way should not regress by this change. Py2exe might replace sys.stdout and sys.stderr, but that currently breaks Mercurial anyway and also these streams don’t claim to be interactive, so this function is not called for them.

File last commit:

r49801:642e31cb default
r50273:094a5fa3 6.2 stable
Show More
__init__.py
242 lines | 6.8 KiB | text/x-python | PythonLexer
Matt Mackall
zeroconf: initial implementation...
r7071 # zeroconf.py - zeroconf support for Mercurial
#
Raphaël Gomès
contributor: change mentions of mpm to olivia...
r47575 # Copyright 2005-2007 Olivia Mackall <olivia@selenic.com>
Matt Mackall
zeroconf: initial implementation...
r7071 #
Martin Geisler
updated license to be explicit about GPL version 2
r8225 # This software may be used and distributed according to the terms of the
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
Cédric Duval
extensions: improve the consistency of synopses...
r8894 '''discover and advertise repositories on the local network
David Soria Parra
zeroconf: add extension documentation
r7606
Vernon Tang
zeroconf: improve the extension's documentation...
r43806 The zeroconf extension will advertise :hg:`serve` instances over
DNS-SD so that they can be discovered using the :hg:`paths` command
without knowing the server's IP address.
David Soria Parra
zeroconf: add extension documentation
r7606
Martin Geisler
zeroconf: small fixes in docstring...
r11504 To allow other people to discover your repository using run
:hg:`serve` in your repository::
David Soria Parra
zeroconf: add extension documentation
r7606
Martin Geisler
zeroconf: use reST syntax for literal blocks
r9218 $ cd test
$ hg serve
David Soria Parra
zeroconf: add extension documentation
r7606
Martin Geisler
zeroconf: small fixes in docstring...
r11504 You can discover Zeroconf-enabled repositories by running
:hg:`paths`::
David Soria Parra
zeroconf: add extension documentation
r7606
Martin Geisler
zeroconf: use reST syntax for literal blocks
r9218 $ hg paths
zc-test = http://example.com:8000/test
David Soria Parra
zeroconf: add extension documentation
r7606 '''
timeless
zeroconf: use absolute_import
r28296 import os
import socket
import time
Renato Cunha
hgext/zeroconf/__init__.py: Separate relative and absolute imports....
r11340
timeless
zeroconf: use absolute_import
r28296 from . import Zeroconf
from mercurial import (
dispatch,
encoding,
extensions,
hg,
Gregory Szorc
zeroconf: port to Python 3...
r42752 pycompat,
config: also respect HGRCSKIPREPO in the zeroconf extension...
r44730 rcutil,
Augie Fackler
zeroconf: import ui as uimod per test-check-module-imports
r28308 ui as uimod,
timeless
zeroconf: use absolute_import
r28296 )
Augie Fackler
formatting: blacken the codebase...
r43346 from mercurial.hgweb import server as servermod
Matt Mackall
zeroconf: initial implementation...
r7071
Augie Fackler
extensions: change magic "shipped with hg" string...
r29841 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
Augie Fackler
extensions: document that `testedwith = 'internal'` is special...
r25186 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
# be specifying the version(s) of Mercurial they are tested with, or
# leave the attribute unspecified.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 testedwith = b'ships-with-hg-core'
Augie Fackler
hgext: mark all first-party extensions as such
r16743
Matt Mackall
zeroconf: initial implementation...
r7071 # publish
server = None
localip = None
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
zeroconf: initial implementation...
r7071 def getip():
# finds external-facing interface without sending any packets (Linux)
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 s.connect(('1.0.0.1', 0))
Matt Mackall
zeroconf: initial implementation...
r7071 ip = s.getsockname()[0]
return ip
Brodie Rao
cleanup: replace naked excepts with more specific ones
r16688 except socket.error:
Matt Mackall
zeroconf: initial implementation...
r7071 pass
# Generic method, sometimes gives useless results
Alexander Solovyov
zeroconf: guess ip for Zeroconf...
r8264 try:
dumbip = socket.gethostbyaddr(socket.gethostname())[2][0]
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 if ':' in dumbip:
dumbip = '127.0.0.1'
if not dumbip.startswith('127.'):
Alexander Solovyov
zeroconf: guess ip for Zeroconf...
r8264 return dumbip
Augie Fackler
zeroconf: gethostbyaddr may also fail with socket.herror
r10317 except (socket.gaierror, socket.herror):
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 dumbip = '127.0.0.1'
Matt Mackall
zeroconf: initial implementation...
r7071
# works elsewhere, but actually sends a packet
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 s.connect(('1.0.0.1', 1))
Matt Mackall
zeroconf: initial implementation...
r7071 ip = s.getsockname()[0]
return ip
Brodie Rao
cleanup: replace naked excepts with more specific ones
r16688 except socket.error:
Matt Mackall
zeroconf: initial implementation...
r7071 pass
return dumbip
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
zeroconf: initial implementation...
r7071 def publish(name, desc, path, port):
global server, localip
if not server:
Alexander Solovyov
zeroconf: guess ip for Zeroconf...
r8264 ip = getip()
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 if ip.startswith('127.'):
Augie Fackler
zeroconf: Don't break serve if no internet connection is present.
r7295 # if we have no internet connection, this can happen.
return
Matt Mackall
zeroconf: initial implementation...
r7071 localip = socket.inet_aton(ip)
Alexander Solovyov
zeroconf: guess ip for Zeroconf...
r8264 server = Zeroconf.Zeroconf(ip)
Matt Mackall
zeroconf: initial implementation...
r7071
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 hostname = socket.gethostname().split('.')[0]
host = hostname + ".local"
Augie Fackler
cleanup: remove pointless r-prefixes on double-quoted strings...
r43809 name = "%s-%s" % (hostname, name)
Matt Mackall
zeroconf: initial implementation...
r7071
# advertise to browsers
Augie Fackler
formatting: blacken the codebase...
r43346 svc = Zeroconf.ServiceInfo(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'_http._tcp.local.',
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 pycompat.bytestr(name + '._http._tcp.local.'),
Augie Fackler
formatting: blacken the codebase...
r43346 server=host,
port=port,
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 properties={b'description': desc, b'path': b"/" + path},
Augie Fackler
formatting: blacken the codebase...
r43346 address=localip,
weight=0,
priority=0,
)
Matt Mackall
zeroconf: initial implementation...
r7071 server.registerService(svc)
# advertise to Mercurial clients
Augie Fackler
formatting: blacken the codebase...
r43346 svc = Zeroconf.ServiceInfo(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'_hg._tcp.local.',
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 pycompat.bytestr(name + '._hg._tcp.local.'),
Augie Fackler
formatting: blacken the codebase...
r43346 server=host,
port=port,
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 properties={b'description': desc, b'path': b"/" + path},
Augie Fackler
formatting: blacken the codebase...
r43346 address=localip,
weight=0,
priority=0,
)
Matt Mackall
zeroconf: initial implementation...
r7071 server.registerService(svc)
Augie Fackler
formatting: blacken the codebase...
r43346
Benoit Boissinot
zeroconf: use port from server instead of picking port from config (issue3746)...
r18190 def zc_create_server(create_server, ui, app):
httpd = create_server(ui, app)
port = httpd.port
Matt Mackall
zeroconf: initial implementation...
r7071
Benoit Boissinot
zeroconf: use port from server instead of picking port from config (issue3746)...
r18190 try:
repos = app.repos
except AttributeError:
# single repo
Gregory Szorc
zeroconf: access repo on hgweb_mod properly (issue5036)...
r27910 with app._obtainrepo() as repo:
name = app.reponame or os.path.basename(repo.root)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 path = repo.ui.config(b"web", b"prefix", b"").strip(b'/')
desc = repo.ui.config(b"web", b"description")
Boris Feld
configitems: register the 'web.description' config
r34235 if not desc:
desc = name
Benoit Boissinot
zeroconf: use port from server instead of picking port from config (issue3746)...
r18190 publish(name, desc, path, port)
else:
# webdir
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 prefix = app.ui.config(b"web", b"prefix", b"").strip(b'/') + b'/'
Benoit Boissinot
zeroconf: use port from server instead of picking port from config (issue3746)...
r18190 for repo, path in repos:
u = app.ui.copy()
config: also respect HGRCSKIPREPO in the zeroconf extension...
r44730 if rcutil.use_repo_hgrc():
u.readconfig(os.path.join(path, b'.hg', b'hgrc'))
Alexander Solovyov
zeroconf: code cleanup, fixing variable names to be meaningful
r9488 name = os.path.basename(repo)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 path = (prefix + repo).strip(b'/')
desc = u.config(b'web', b'description')
Boris Feld
configitems: register the 'web.description' config
r34235 if not desc:
desc = name
Benoit Boissinot
zeroconf: use port from server instead of picking port from config (issue3746)...
r18190 publish(name, desc, path, port)
return httpd
Matt Mackall
zeroconf: initial implementation...
r7071
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
zeroconf: initial implementation...
r7071 # listen
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
py3: use class X: instead of class X(object):...
r49801 class listener:
Matt Mackall
zeroconf: initial implementation...
r7071 def __init__(self):
self.found = {}
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
zeroconf: initial implementation...
r7071 def removeService(self, server, type, name):
if repr(name) in self.found:
del self.found[repr(name)]
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
zeroconf: initial implementation...
r7071 def addService(self, server, type, name):
self.found[repr(name)] = server.getServiceInfo(type, name)
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
zeroconf: initial implementation...
r7071 def getzcpaths():
Alexander Solovyov
zeroconf: guess ip for Zeroconf...
r8264 ip = getip()
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 if ip.startswith('127.'):
Alexander Solovyov
zeroconf: guess ip for Zeroconf...
r8264 return
server = Zeroconf.Zeroconf(ip)
Matt Mackall
zeroconf: initial implementation...
r7071 l = listener()
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 Zeroconf.ServiceBrowser(server, b"_hg._tcp.local.", l)
Matt Mackall
zeroconf: initial implementation...
r7071 time.sleep(1)
server.close()
Alexander Solovyov
zeroconf: code cleanup, fixing variable names to be meaningful
r9488 for value in l.found.values():
Augie Fackler
formatting: blacken the codebase...
r43346 name = value.name[: value.name.index(b'.')]
Augie Fackler
cleanup: remove pointless r-prefixes on double-quoted strings...
r43809 url = "http://%s:%s%s" % (
Augie Fackler
formatting: blacken the codebase...
r43346 socket.inet_ntoa(value.address),
value.port,
Augie Fackler
cleanup: remove pointless r-prefixes on double-quoted strings...
r43809 value.properties.get("path", "/"),
Augie Fackler
formatting: blacken the codebase...
r43346 )
Gregory Szorc
zeroconf: port to Python 3...
r42752 yield b"zc-" + name, pycompat.bytestr(url)
Matt Mackall
zeroconf: initial implementation...
r7071
Augie Fackler
formatting: blacken the codebase...
r43346
zeroconf: blindly forward extra argument to the core config method...
r33175 def config(orig, self, section, key, *args, **kwargs):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if section == b"paths" and key.startswith(b"zc-"):
Alexander Solovyov
zeroconf: code cleanup, fixing variable names to be meaningful
r9488 for name, path in getzcpaths():
if name == key:
return path
zeroconf: blindly forward extra argument to the core config method...
r33175 return orig(self, section, key, *args, **kwargs)
Matt Mackall
zeroconf: initial implementation...
r7071
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
zeroconf: forward all arguments passed to ui.configitems() wrapper...
r28038 def configitems(orig, self, section, *args, **kwargs):
repos = orig(self, section, *args, **kwargs)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if section == b"paths":
Alexander Solovyov
zeroconf: code cleanup, fixing variable names to be meaningful
r9488 repos += getzcpaths()
return repos
Matt Mackall
zeroconf: initial implementation...
r7071
Augie Fackler
formatting: blacken the codebase...
r43346
Danek Duvall
zeroconf: fix crash in "hg paths" when zeroconf server is up...
r28250 def configsuboptions(orig, self, section, name, *args, **kwargs):
opt, sub = orig(self, section, name, *args, **kwargs)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if section == b"paths" and name.startswith(b"zc-"):
Danek Duvall
zeroconf: fix crash in "hg paths" when zeroconf server is up...
r28250 # We have to find the URL in the zeroconf paths. We can't cons up any
# suboptions, so we use any that we found in the original config.
for zcname, zcurl in getzcpaths():
if zcname == name:
return zcurl, sub
return opt, sub
Augie Fackler
formatting: blacken the codebase...
r43346
Henrik Stuart
zeroconf: override default destination folder on clone
r10342 def defaultdest(orig, source):
for name, path in getzcpaths():
if path == source:
return name.encode(encoding.encoding)
return orig(source)
Augie Fackler
formatting: blacken the codebase...
r43346
Nicolas Dumazet
zeroconf: notify the Zeroconf threads when hg exits...
r14104 def cleanupafterdispatch(orig, ui, options, cmd, cmdfunc):
try:
return orig(ui, options, cmd, cmdfunc)
finally:
# we need to call close() on the server to notify() the various
# threading Conditions and allow the background threads to exit
global server
if server:
server.close()
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 extensions.wrapfunction(dispatch, b'_runcommand', cleanupafterdispatch)
Nicolas Dumazet
zeroconf: notify the Zeroconf threads when hg exits...
r14104
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 extensions.wrapfunction(uimod.ui, b'config', config)
extensions.wrapfunction(uimod.ui, b'configitems', configitems)
extensions.wrapfunction(uimod.ui, b'configsuboptions', configsuboptions)
extensions.wrapfunction(hg, b'defaultdest', defaultdest)
extensions.wrapfunction(servermod, b'create_server', zc_create_server)