2👍
If you’re okay with the terms of the GPL the easiest way to do this is definitely just to invoke hgweb from your django views. hgweb is a full-function, multi-repository python wsgi application, of the sort django loves to work with.
It’s entirely possible your entire django view could be:
from mercurial.hgweb import hgweb
def hg_view(request):
""" relay a WSGI Request to HG """
hgweb(config).run_wsgi(request)
and you’d route to that with a normal django view. You’d probably have to modify the request to remove the leading path element, but the larger point is if the goal is to have a wsgi application (which django is) call another wsgi application (which hgweb is) it’s definitely easiest to avoid TCP sockets and local pipes and instead keep things all in the family.
0👍
This is the solution I actually went with, based on Ry4an’s accepted answer. It’s entirely possible that a django HttpRequest
object can serve as a wsgirequest
that can be passed directly to hgweb_mod.run_wsgi
, but I couldn’t figure out what to return then. The following worked for me, it certainly doesn’t handle all the edge cases, but I’m able to push and pull my repos this way:
#!/usr/bin/python
class WsgiWrapper(object):
"""
A wrapper object that works reasonably well (at least in the cases in which it is currently being used)
to turn a django request into a Wsgi thingy.
"""
def __init__(self, request):
self.request = request
self.status = None
self.headers = None
self.exc_info = None
def __call__(self, status=None, response_headers=None, exc_info=None):
if status is not None:
self.status = status
self.headers = response_headers
self.exc_info = exc_info
self.outputbuffer = cStringIO.StringIO()
return self.outputbuffer.write
def serve_hg(request, repopath):
"""
Delegates to mercurial's built-in hgweb WSGI application to serve up access
to mercurial HTTPCommand Protocol.
"""
#Put together a wsgi request from the django request.
wrapper = WsgiWrapper(request)
env = {}
env.update(request.META)
env['SCRIPT_NAME'] = '/'
env['PATH_INFO'] = '/'
req = hgweb.request.wsgirequest(env, wrapper)
#Invoke hgweb as a wsgi application.
hgw = hgweb.hgweb(repopath)
hgw.repo.baseui.setconfig('web', 'allow_push', '*')
hgw.repo.baseui.setconfig('web', 'push_ssl', 'false')
gen = hgw.run_wsgi(req)
data = wrapper.outputbuffer.getvalue()
if len(data):
gen = [data]
wrapper.outputbuffer.close()
#Now build up the django response object from that.
resp = HttpResponse("".join(gen))
stat = wrapper.status
if stat is None:
resp.status_code = 200
else:
pair = stat.split(' ', 1)
resp.status_code = pair[0]
if len(pair) > 1:
resp.reason_phrase = pair[1]
hopbyhop = (
'Connection',
'Keep-Alive',
'Proxy-Authenticate',
'Proxy-Authorization',
'TE',
'Trailers',
'Transfer-Encoding',
'Upgrade',
)
if wrapper.headers is not None:
for k, v in wrapper.headers:
if k not in hopbyhop:
resp[k] = v
return resp