comparison mercurial/hgweb/hgweb_mod.py @ 43076:2372284d9457

formatting: blacken the codebase This is using my patch to black (https://github.com/psf/black/pull/826) so we don't un-wrap collection literals. Done with: hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S # skip-blame mass-reformatting only # no-check-commit reformats foo_bar functions Differential Revision: https://phab.mercurial-scm.org/D6971
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:45:02 -0400
parents c93d046d4300
children 687b865b95ad
comparison
equal deleted inserted replaced
43075:57875cf423c9 43076:2372284d9457
43 webcommands, 43 webcommands,
44 webutil, 44 webutil,
45 wsgicgi, 45 wsgicgi,
46 ) 46 )
47 47
48
48 def getstyle(req, configfn, templatepath): 49 def getstyle(req, configfn, templatepath):
49 styles = ( 50 styles = (
50 req.qsparams.get('style', None), 51 req.qsparams.get('style', None),
51 configfn('web', 'style'), 52 configfn('web', 'style'),
52 'paper', 53 'paper',
53 ) 54 )
54 return styles, templater.stylemap(styles, templatepath) 55 return styles, templater.stylemap(styles, templatepath)
56
55 57
56 def makebreadcrumb(url, prefix=''): 58 def makebreadcrumb(url, prefix=''):
57 '''Return a 'URL breadcrumb' list 59 '''Return a 'URL breadcrumb' list
58 60
59 A 'URL breadcrumb' is a list of URL-name pairs, 61 A 'URL breadcrumb' is a list of URL-name pairs,
76 break 78 break
77 breadcrumb.append({'url': urlel, 'name': pathel}) 79 breadcrumb.append({'url': urlel, 'name': pathel})
78 urlel = os.path.dirname(urlel) 80 urlel = os.path.dirname(urlel)
79 return templateutil.mappinglist(reversed(breadcrumb)) 81 return templateutil.mappinglist(reversed(breadcrumb))
80 82
83
81 class requestcontext(object): 84 class requestcontext(object):
82 """Holds state/context for an individual request. 85 """Holds state/context for an individual request.
83 86
84 Servers can be multi-threaded. Holding state on the WSGI application 87 Servers can be multi-threaded. Holding state on the WSGI application
85 is prone to race conditions. Instances of this class exist to hold 88 is prone to race conditions. Instances of this class exist to hold
86 mutable and race-free state for requests. 89 mutable and race-free state for requests.
87 """ 90 """
91
88 def __init__(self, app, repo, req, res): 92 def __init__(self, app, repo, req, res):
89 self.repo = repo 93 self.repo = repo
90 self.reponame = app.reponame 94 self.reponame = app.reponame
91 self.req = req 95 self.req = req
92 self.res = res 96 self.res = res
111 115
112 self.csp, self.nonce = cspvalues(self.repo.ui) 116 self.csp, self.nonce = cspvalues(self.repo.ui)
113 117
114 # Trust the settings from the .hg/hgrc files by default. 118 # Trust the settings from the .hg/hgrc files by default.
115 def config(self, section, name, default=uimod._unset, untrusted=True): 119 def config(self, section, name, default=uimod._unset, untrusted=True):
116 return self.repo.ui.config(section, name, default, 120 return self.repo.ui.config(section, name, default, untrusted=untrusted)
117 untrusted=untrusted)
118 121
119 def configbool(self, section, name, default=uimod._unset, untrusted=True): 122 def configbool(self, section, name, default=uimod._unset, untrusted=True):
120 return self.repo.ui.configbool(section, name, default, 123 return self.repo.ui.configbool(
121 untrusted=untrusted) 124 section, name, default, untrusted=untrusted
125 )
122 126
123 def configint(self, section, name, default=uimod._unset, untrusted=True): 127 def configint(self, section, name, default=uimod._unset, untrusted=True):
124 return self.repo.ui.configint(section, name, default, 128 return self.repo.ui.configint(
125 untrusted=untrusted) 129 section, name, default, untrusted=untrusted
130 )
126 131
127 def configlist(self, section, name, default=uimod._unset, untrusted=True): 132 def configlist(self, section, name, default=uimod._unset, untrusted=True):
128 return self.repo.ui.configlist(section, name, default, 133 return self.repo.ui.configlist(
129 untrusted=untrusted) 134 section, name, default, untrusted=untrusted
135 )
130 136
131 def archivelist(self, nodeid): 137 def archivelist(self, nodeid):
132 return webutil.archivelist(self.repo.ui, nodeid) 138 return webutil.archivelist(self.repo.ui, nodeid)
133 139
134 def templater(self, req): 140 def templater(self, req):
135 # determine scheme, port and server name 141 # determine scheme, port and server name
136 # this is needed to create absolute urls 142 # this is needed to create absolute urls
137 logourl = self.config('web', 'logourl') 143 logourl = self.config('web', 'logourl')
138 logoimg = self.config('web', 'logoimg') 144 logoimg = self.config('web', 'logoimg')
139 staticurl = (self.config('web', 'staticurl') 145 staticurl = (
140 or req.apppath.rstrip('/') + '/static/') 146 self.config('web', 'staticurl')
147 or req.apppath.rstrip('/') + '/static/'
148 )
141 if not staticurl.endswith('/'): 149 if not staticurl.endswith('/'):
142 staticurl += '/' 150 staticurl += '/'
143 151
144 # figure out which style to use 152 # figure out which style to use
145 153
146 vars = {} 154 vars = {}
147 styles, (style, mapfile) = getstyle(req, self.config, 155 styles, (style, mapfile) = getstyle(req, self.config, self.templatepath)
148 self.templatepath)
149 if style == styles[0]: 156 if style == styles[0]:
150 vars['style'] = style 157 vars['style'] = style
151 158
152 sessionvars = webutil.sessionvars(vars, '?') 159 sessionvars = webutil.sessionvars(vars, '?')
153 160
154 if not self.reponame: 161 if not self.reponame:
155 self.reponame = (self.config('web', 'name', '') 162 self.reponame = (
156 or req.reponame 163 self.config('web', 'name', '')
157 or req.apppath 164 or req.reponame
158 or self.repo.root) 165 or req.apppath
166 or self.repo.root
167 )
159 168
160 filters = {} 169 filters = {}
161 templatefilter = registrar.templatefilter(filters) 170 templatefilter = registrar.templatefilter(filters)
171
162 @templatefilter('websub', intype=bytes) 172 @templatefilter('websub', intype=bytes)
163 def websubfilter(text): 173 def websubfilter(text):
164 return templatefilters.websub(text, self.websubtable) 174 return templatefilters.websub(text, self.websubtable)
165 175
166 # create the templater 176 # create the templater
177 'pathdef': makebreadcrumb(req.apppath), 187 'pathdef': makebreadcrumb(req.apppath),
178 'style': style, 188 'style': style,
179 'nonce': self.nonce, 189 'nonce': self.nonce,
180 } 190 }
181 templatekeyword = registrar.templatekeyword(defaults) 191 templatekeyword = registrar.templatekeyword(defaults)
192
182 @templatekeyword('motd', requires=()) 193 @templatekeyword('motd', requires=())
183 def motd(context, mapping): 194 def motd(context, mapping):
184 yield self.config('web', 'motd') 195 yield self.config('web', 'motd')
185 196
186 tres = formatter.templateresources(self.repo.ui, self.repo) 197 tres = formatter.templateresources(self.repo.ui, self.repo)
187 tmpl = templater.templater.frommapfile(mapfile, 198 tmpl = templater.templater.frommapfile(
188 filters=filters, 199 mapfile, filters=filters, defaults=defaults, resources=tres
189 defaults=defaults, 200 )
190 resources=tres)
191 return tmpl 201 return tmpl
192 202
193 def sendtemplate(self, name, **kwargs): 203 def sendtemplate(self, name, **kwargs):
194 """Helper function to send a response generated from a template.""" 204 """Helper function to send a response generated from a template."""
195 kwargs = pycompat.byteskwargs(kwargs) 205 kwargs = pycompat.byteskwargs(kwargs)
196 self.res.setbodygen(self.tmpl.generate(name, kwargs)) 206 self.res.setbodygen(self.tmpl.generate(name, kwargs))
197 return self.res.sendresponse() 207 return self.res.sendresponse()
198 208
209
199 class hgweb(object): 210 class hgweb(object):
200 """HTTP server for individual repositories. 211 """HTTP server for individual repositories.
201 212
202 Instances of this class serve HTTP responses for a particular 213 Instances of this class serve HTTP responses for a particular
203 repository. 214 repository.
205 Instances are typically used as WSGI applications. 216 Instances are typically used as WSGI applications.
206 217
207 Some servers are multi-threaded. On these servers, there may 218 Some servers are multi-threaded. On these servers, there may
208 be multiple active threads inside __call__. 219 be multiple active threads inside __call__.
209 """ 220 """
221
210 def __init__(self, repo, name=None, baseui=None): 222 def __init__(self, repo, name=None, baseui=None):
211 if isinstance(repo, bytes): 223 if isinstance(repo, bytes):
212 if baseui: 224 if baseui:
213 u = baseui.copy() 225 u = baseui.copy()
214 else: 226 else:
280 """Start a server from CGI environment. 292 """Start a server from CGI environment.
281 293
282 Modern servers should be using WSGI and should avoid this 294 Modern servers should be using WSGI and should avoid this
283 method, if possible. 295 method, if possible.
284 """ 296 """
285 if not encoding.environ.get('GATEWAY_INTERFACE', 297 if not encoding.environ.get('GATEWAY_INTERFACE', '').startswith(
286 '').startswith("CGI/1."): 298 "CGI/1."
287 raise RuntimeError("This function is only intended to be " 299 ):
288 "called while running as a CGI script.") 300 raise RuntimeError(
301 "This function is only intended to be "
302 "called while running as a CGI script."
303 )
289 wsgicgi.launch(self) 304 wsgicgi.launch(self)
290 305
291 def __call__(self, env, respond): 306 def __call__(self, env, respond):
292 """Run the WSGI application. 307 """Run the WSGI application.
293 308
326 # accordingly. But URL paths can conflict with subrepos and virtual 341 # accordingly. But URL paths can conflict with subrepos and virtual
327 # repos in hgwebdir. So until we have a workaround for this, only 342 # repos in hgwebdir. So until we have a workaround for this, only
328 # expose the URLs if the feature is enabled. 343 # expose the URLs if the feature is enabled.
329 apienabled = rctx.repo.ui.configbool('experimental', 'web.apiserver') 344 apienabled = rctx.repo.ui.configbool('experimental', 'web.apiserver')
330 if apienabled and req.dispatchparts and req.dispatchparts[0] == b'api': 345 if apienabled and req.dispatchparts and req.dispatchparts[0] == b'api':
331 wireprotoserver.handlewsgiapirequest(rctx, req, res, 346 wireprotoserver.handlewsgiapirequest(
332 self.check_perm) 347 rctx, req, res, self.check_perm
348 )
333 return res.sendresponse() 349 return res.sendresponse()
334 350
335 handled = wireprotoserver.handlewsgirequest( 351 handled = wireprotoserver.handlewsgirequest(
336 rctx, req, res, self.check_perm) 352 rctx, req, res, self.check_perm
353 )
337 if handled: 354 if handled:
338 return res.sendresponse() 355 return res.sendresponse()
339 356
340 # Old implementations of hgweb supported dispatching the request via 357 # Old implementations of hgweb supported dispatching the request via
341 # the initial query string parameter instead of using PATH_INFO. 358 # the initial query string parameter instead of using PATH_INFO.
352 if 'cmd' not in req.qsparams and args and args[0]: 369 if 'cmd' not in req.qsparams and args and args[0]:
353 cmd = args.pop(0) 370 cmd = args.pop(0)
354 style = cmd.rfind('-') 371 style = cmd.rfind('-')
355 if style != -1: 372 if style != -1:
356 req.qsparams['style'] = cmd[:style] 373 req.qsparams['style'] = cmd[:style]
357 cmd = cmd[style + 1:] 374 cmd = cmd[style + 1 :]
358 375
359 # avoid accepting e.g. style parameter as command 376 # avoid accepting e.g. style parameter as command
360 if util.safehasattr(webcommands, cmd): 377 if util.safehasattr(webcommands, cmd):
361 req.qsparams['cmd'] = cmd 378 req.qsparams['cmd'] = cmd
362 379
379 if cmd == 'archive': 396 if cmd == 'archive':
380 fn = req.qsparams['node'] 397 fn = req.qsparams['node']
381 for type_, spec in webutil.archivespecs.iteritems(): 398 for type_, spec in webutil.archivespecs.iteritems():
382 ext = spec[2] 399 ext = spec[2]
383 if fn.endswith(ext): 400 if fn.endswith(ext):
384 req.qsparams['node'] = fn[:-len(ext)] 401 req.qsparams['node'] = fn[: -len(ext)]
385 req.qsparams['type'] = type_ 402 req.qsparams['type'] = type_
386 else: 403 else:
387 cmd = req.qsparams.get('cmd', '') 404 cmd = req.qsparams.get('cmd', '')
388 405
389 # process the web interface request 406 # process the web interface request
390 407
391 try: 408 try:
392 rctx.tmpl = rctx.templater(req) 409 rctx.tmpl = rctx.templater(req)
393 ctype = rctx.tmpl.render('mimetype', 410 ctype = rctx.tmpl.render(
394 {'encoding': encoding.encoding}) 411 'mimetype', {'encoding': encoding.encoding}
412 )
395 413
396 # check read permissions non-static content 414 # check read permissions non-static content
397 if cmd != 'static': 415 if cmd != 'static':
398 self.check_perm(rctx, req, None) 416 self.check_perm(rctx, req, None)
399 417
429 res.headers['Content-Type'] = ctype 447 res.headers['Content-Type'] = ctype
430 return getattr(webcommands, cmd)(rctx) 448 return getattr(webcommands, cmd)(rctx)
431 449
432 except (error.LookupError, error.RepoLookupError) as err: 450 except (error.LookupError, error.RepoLookupError) as err:
433 msg = pycompat.bytestr(err) 451 msg = pycompat.bytestr(err)
434 if (util.safehasattr(err, 'name') and 452 if util.safehasattr(err, 'name') and not isinstance(
435 not isinstance(err, error.ManifestLookupError)): 453 err, error.ManifestLookupError
454 ):
436 msg = 'revision not found: %s' % err.name 455 msg = 'revision not found: %s' % err.name
437 456
438 res.status = '404 Not Found' 457 res.status = '404 Not Found'
439 res.headers['Content-Type'] = ctype 458 res.headers['Content-Type'] = ctype
440 return rctx.sendtemplate('error', error=msg) 459 return rctx.sendtemplate('error', error=msg)
455 474
456 def check_perm(self, rctx, req, op): 475 def check_perm(self, rctx, req, op):
457 for permhook in permhooks: 476 for permhook in permhooks:
458 permhook(rctx, req, op) 477 permhook(rctx, req, op)
459 478
479
460 def getwebview(repo): 480 def getwebview(repo):
461 """The 'web.view' config controls changeset filter to hgweb. Possible 481 """The 'web.view' config controls changeset filter to hgweb. Possible
462 values are ``served``, ``visible`` and ``all``. Default is ``served``. 482 values are ``served``, ``visible`` and ``all``. Default is ``served``.
463 The ``served`` filter only shows changesets that can be pulled from the 483 The ``served`` filter only shows changesets that can be pulled from the
464 hgweb instance. The``visible`` filter includes secret changesets but 484 hgweb instance. The``visible`` filter includes secret changesets but