Mercurial > public > mercurial-scm > hg
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 |