##// END OF EJS Templates
help: restore use of callable() since it was readded in Python 3.2
Augie Fackler -
r21796:8225bb1f default
parent child Browse files
Show More
@@ -1,516 +1,516 b''
1 1 # help.py - help data for mercurial
2 2 #
3 3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from i18n import gettext, _
9 9 import itertools, sys, os
10 10 import error
11 11 import extensions, revset, fileset, templatekw, templatefilters, filemerge
12 12 import encoding, util, minirst
13 13 import cmdutil
14 14
15 15 def listexts(header, exts, indent=1, showdeprecated=False):
16 16 '''return a text listing of the given extensions'''
17 17 rst = []
18 18 if exts:
19 19 rst.append('\n%s\n\n' % header)
20 20 for name, desc in sorted(exts.iteritems()):
21 21 if '(DEPRECATED)' in desc and not showdeprecated:
22 22 continue
23 23 rst.append('%s:%s: %s\n' % (' ' * indent, name, desc))
24 24 return rst
25 25
26 26 def extshelp():
27 27 rst = loaddoc('extensions')().splitlines(True)
28 28 rst.extend(listexts(
29 29 _('enabled extensions:'), extensions.enabled(), showdeprecated=True))
30 30 rst.extend(listexts(_('disabled extensions:'), extensions.disabled()))
31 31 doc = ''.join(rst)
32 32 return doc
33 33
34 34 def optrst(options, verbose):
35 35 data = []
36 36 multioccur = False
37 37 for option in options:
38 38 if len(option) == 5:
39 39 shortopt, longopt, default, desc, optlabel = option
40 40 else:
41 41 shortopt, longopt, default, desc = option
42 42 optlabel = _("VALUE") # default label
43 43
44 44 if not verbose and ("DEPRECATED" in desc or _("DEPRECATED") in desc):
45 45 continue
46 46
47 47 so = ''
48 48 if shortopt:
49 49 so = '-' + shortopt
50 50 lo = '--' + longopt
51 51 if default:
52 52 desc += _(" (default: %s)") % default
53 53
54 54 if isinstance(default, list):
55 55 lo += " %s [+]" % optlabel
56 56 multioccur = True
57 57 elif (default is not None) and not isinstance(default, bool):
58 58 lo += " %s" % optlabel
59 59
60 60 data.append((so, lo, desc))
61 61
62 62 rst = minirst.maketable(data, 1)
63 63
64 64 if multioccur:
65 65 rst.append(_("\n[+] marked option can be specified multiple times\n"))
66 66
67 67 return ''.join(rst)
68 68
69 69 def indicateomitted(rst, omitted, notomitted=None):
70 70 rst.append('\n\n.. container:: omitted\n\n %s\n\n' % omitted)
71 71 if notomitted:
72 72 rst.append('\n\n.. container:: notomitted\n\n %s\n\n' % notomitted)
73 73
74 74 def topicmatch(kw):
75 75 """Return help topics matching kw.
76 76
77 77 Returns {'section': [(name, summary), ...], ...} where section is
78 78 one of topics, commands, extensions, or extensioncommands.
79 79 """
80 80 kw = encoding.lower(kw)
81 81 def lowercontains(container):
82 82 return kw in encoding.lower(container) # translated in helptable
83 83 results = {'topics': [],
84 84 'commands': [],
85 85 'extensions': [],
86 86 'extensioncommands': [],
87 87 }
88 88 for names, header, doc in helptable:
89 89 if (sum(map(lowercontains, names))
90 90 or lowercontains(header)
91 91 or lowercontains(doc())):
92 92 results['topics'].append((names[0], header))
93 93 import commands # avoid cycle
94 94 for cmd, entry in commands.table.iteritems():
95 95 if len(entry) == 3:
96 96 summary = entry[2]
97 97 else:
98 98 summary = ''
99 99 # translate docs *before* searching there
100 100 docs = _(getattr(entry[0], '__doc__', None)) or ''
101 101 if kw in cmd or lowercontains(summary) or lowercontains(docs):
102 102 doclines = docs.splitlines()
103 103 if doclines:
104 104 summary = doclines[0]
105 105 cmdname = cmd.split('|')[0].lstrip('^')
106 106 results['commands'].append((cmdname, summary))
107 107 for name, docs in itertools.chain(
108 108 extensions.enabled(False).iteritems(),
109 109 extensions.disabled().iteritems()):
110 110 # extensions.load ignores the UI argument
111 111 mod = extensions.load(None, name, '')
112 112 name = name.split('.')[-1]
113 113 if lowercontains(name) or lowercontains(docs):
114 114 # extension docs are already translated
115 115 results['extensions'].append((name, docs.splitlines()[0]))
116 116 for cmd, entry in getattr(mod, 'cmdtable', {}).iteritems():
117 117 if kw in cmd or (len(entry) > 2 and lowercontains(entry[2])):
118 118 cmdname = cmd.split('|')[0].lstrip('^')
119 119 if entry[0].__doc__:
120 120 cmddoc = gettext(entry[0].__doc__).splitlines()[0]
121 121 else:
122 122 cmddoc = _('(no help text available)')
123 123 results['extensioncommands'].append((cmdname, cmddoc))
124 124 return results
125 125
126 126 def loaddoc(topic):
127 127 """Return a delayed loader for help/topic.txt."""
128 128
129 129 def loader():
130 130 if util.mainfrozen():
131 131 module = sys.executable
132 132 else:
133 133 module = __file__
134 134 base = os.path.dirname(module)
135 135
136 136 for dir in ('.', '..'):
137 137 docdir = os.path.join(base, dir, 'help')
138 138 if os.path.isdir(docdir):
139 139 break
140 140
141 141 path = os.path.join(docdir, topic + ".txt")
142 142 doc = gettext(util.readfile(path))
143 143 for rewriter in helphooks.get(topic, []):
144 144 doc = rewriter(topic, doc)
145 145 return doc
146 146
147 147 return loader
148 148
149 149 helptable = sorted([
150 150 (["config", "hgrc"], _("Configuration Files"), loaddoc('config')),
151 151 (["dates"], _("Date Formats"), loaddoc('dates')),
152 152 (["patterns"], _("File Name Patterns"), loaddoc('patterns')),
153 153 (['environment', 'env'], _('Environment Variables'),
154 154 loaddoc('environment')),
155 155 (['revisions', 'revs'], _('Specifying Single Revisions'),
156 156 loaddoc('revisions')),
157 157 (['multirevs', 'mrevs'], _('Specifying Multiple Revisions'),
158 158 loaddoc('multirevs')),
159 159 (['revsets', 'revset'], _("Specifying Revision Sets"), loaddoc('revsets')),
160 160 (['filesets', 'fileset'], _("Specifying File Sets"), loaddoc('filesets')),
161 161 (['diffs'], _('Diff Formats'), loaddoc('diffs')),
162 162 (['merge-tools', 'mergetools'], _('Merge Tools'), loaddoc('merge-tools')),
163 163 (['templating', 'templates', 'template', 'style'], _('Template Usage'),
164 164 loaddoc('templates')),
165 165 (['urls'], _('URL Paths'), loaddoc('urls')),
166 166 (["extensions"], _("Using Additional Features"), extshelp),
167 167 (["subrepos", "subrepo"], _("Subrepositories"), loaddoc('subrepos')),
168 168 (["hgweb"], _("Configuring hgweb"), loaddoc('hgweb')),
169 169 (["glossary"], _("Glossary"), loaddoc('glossary')),
170 170 (["hgignore", "ignore"], _("Syntax for Mercurial Ignore Files"),
171 171 loaddoc('hgignore')),
172 172 (["phases"], _("Working with Phases"), loaddoc('phases')),
173 173 ])
174 174
175 175 # Map topics to lists of callable taking the current topic help and
176 176 # returning the updated version
177 177 helphooks = {}
178 178
179 179 def addtopichook(topic, rewriter):
180 180 helphooks.setdefault(topic, []).append(rewriter)
181 181
182 182 def makeitemsdoc(topic, doc, marker, items):
183 183 """Extract docstring from the items key to function mapping, build a
184 184 .single documentation block and use it to overwrite the marker in doc
185 185 """
186 186 entries = []
187 187 for name in sorted(items):
188 188 text = (items[name].__doc__ or '').rstrip()
189 189 if not text:
190 190 continue
191 191 text = gettext(text)
192 192 lines = text.splitlines()
193 193 doclines = [(lines[0])]
194 194 for l in lines[1:]:
195 195 # Stop once we find some Python doctest
196 196 if l.strip().startswith('>>>'):
197 197 break
198 198 doclines.append(' ' + l.strip())
199 199 entries.append('\n'.join(doclines))
200 200 entries = '\n\n'.join(entries)
201 201 return doc.replace(marker, entries)
202 202
203 203 def addtopicsymbols(topic, marker, symbols):
204 204 def add(topic, doc):
205 205 return makeitemsdoc(topic, doc, marker, symbols)
206 206 addtopichook(topic, add)
207 207
208 208 addtopicsymbols('filesets', '.. predicatesmarker', fileset.symbols)
209 209 addtopicsymbols('merge-tools', '.. internaltoolsmarker', filemerge.internals)
210 210 addtopicsymbols('revsets', '.. predicatesmarker', revset.symbols)
211 211 addtopicsymbols('templates', '.. keywordsmarker', templatekw.dockeywords)
212 212 addtopicsymbols('templates', '.. filtersmarker', templatefilters.filters)
213 213
214 214 def help_(ui, name, unknowncmd=False, full=True, **opts):
215 215 '''
216 216 Generate the help for 'name' as unformatted restructured text. If
217 217 'name' is None, describe the commands available.
218 218 '''
219 219
220 220 import commands # avoid cycle
221 221
222 222 def helpcmd(name):
223 223 try:
224 224 aliases, entry = cmdutil.findcmd(name, commands.table,
225 225 strict=unknowncmd)
226 226 except error.AmbiguousCommand, inst:
227 227 # py3k fix: except vars can't be used outside the scope of the
228 228 # except block, nor can be used inside a lambda. python issue4617
229 229 prefix = inst.args[0]
230 230 select = lambda c: c.lstrip('^').startswith(prefix)
231 231 rst = helplist(select)
232 232 return rst
233 233
234 234 rst = []
235 235
236 236 # check if it's an invalid alias and display its error if it is
237 237 if getattr(entry[0], 'badalias', False):
238 238 if not unknowncmd:
239 239 ui.pushbuffer()
240 240 entry[0](ui)
241 241 rst.append(ui.popbuffer())
242 242 return rst
243 243
244 244 # synopsis
245 245 if len(entry) > 2:
246 246 if entry[2].startswith('hg'):
247 247 rst.append("%s\n" % entry[2])
248 248 else:
249 249 rst.append('hg %s %s\n' % (aliases[0], entry[2]))
250 250 else:
251 251 rst.append('hg %s\n' % aliases[0])
252 252 # aliases
253 253 if full and not ui.quiet and len(aliases) > 1:
254 254 rst.append(_("\naliases: %s\n") % ', '.join(aliases[1:]))
255 255 rst.append('\n')
256 256
257 257 # description
258 258 doc = gettext(entry[0].__doc__)
259 259 if not doc:
260 260 doc = _("(no help text available)")
261 261 if util.safehasattr(entry[0], 'definition'): # aliased command
262 262 if entry[0].definition.startswith('!'): # shell alias
263 263 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
264 264 else:
265 265 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
266 266 doc = doc.splitlines(True)
267 267 if ui.quiet or not full:
268 268 rst.append(doc[0])
269 269 else:
270 270 rst.extend(doc)
271 271 rst.append('\n')
272 272
273 273 # check if this command shadows a non-trivial (multi-line)
274 274 # extension help text
275 275 try:
276 276 mod = extensions.find(name)
277 277 doc = gettext(mod.__doc__) or ''
278 278 if '\n' in doc.strip():
279 279 msg = _('use "hg help -e %s" to show help for '
280 280 'the %s extension') % (name, name)
281 281 rst.append('\n%s\n' % msg)
282 282 except KeyError:
283 283 pass
284 284
285 285 # options
286 286 if not ui.quiet and entry[1]:
287 287 rst.append('\n%s\n\n' % _("options:"))
288 288 rst.append(optrst(entry[1], ui.verbose))
289 289
290 290 if ui.verbose:
291 291 rst.append('\n%s\n\n' % _("global options:"))
292 292 rst.append(optrst(commands.globalopts, ui.verbose))
293 293
294 294 if not ui.verbose:
295 295 if not full:
296 296 rst.append(_('\nuse "hg help %s" to show the full help text\n')
297 297 % name)
298 298 elif not ui.quiet:
299 299 omitted = _('use "hg -v help %s" to show more complete'
300 300 ' help and the global options') % name
301 301 notomitted = _('use "hg -v help %s" to show'
302 302 ' the global options') % name
303 303 indicateomitted(rst, omitted, notomitted)
304 304
305 305 return rst
306 306
307 307
308 308 def helplist(select=None):
309 309 # list of commands
310 310 if name == "shortlist":
311 311 header = _('basic commands:\n\n')
312 312 elif name == "debug":
313 313 header = _('debug commands (internal and unsupported):\n\n')
314 314 else:
315 315 header = _('list of commands:\n\n')
316 316
317 317 h = {}
318 318 cmds = {}
319 319 for c, e in commands.table.iteritems():
320 320 f = c.split("|", 1)[0]
321 321 if select and not select(f):
322 322 continue
323 323 if (not select and name != 'shortlist' and
324 324 e[0].__module__ != commands.__name__):
325 325 continue
326 326 if name == "shortlist" and not f.startswith("^"):
327 327 continue
328 328 f = f.lstrip("^")
329 329 if not ui.debugflag and f.startswith("debug") and name != "debug":
330 330 continue
331 331 doc = e[0].__doc__
332 332 if doc and 'DEPRECATED' in doc and not ui.verbose:
333 333 continue
334 334 doc = gettext(doc)
335 335 if not doc:
336 336 doc = _("(no help text available)")
337 337 h[f] = doc.splitlines()[0].rstrip()
338 338 cmds[f] = c.lstrip("^")
339 339
340 340 rst = []
341 341 if not h:
342 342 if not ui.quiet:
343 343 rst.append(_('no commands defined\n'))
344 344 return rst
345 345
346 346 if not ui.quiet:
347 347 rst.append(header)
348 348 fns = sorted(h)
349 349 for f in fns:
350 350 if ui.verbose:
351 351 commacmds = cmds[f].replace("|",", ")
352 352 rst.append(" :%s: %s\n" % (commacmds, h[f]))
353 353 else:
354 354 rst.append(' :%s: %s\n' % (f, h[f]))
355 355
356 356 if not name:
357 357 exts = listexts(_('enabled extensions:'), extensions.enabled())
358 358 if exts:
359 359 rst.append('\n')
360 360 rst.extend(exts)
361 361
362 362 rst.append(_("\nadditional help topics:\n\n"))
363 363 topics = []
364 364 for names, header, doc in helptable:
365 365 topics.append((names[0], header))
366 366 for t, desc in topics:
367 367 rst.append(" :%s: %s\n" % (t, desc))
368 368
369 369 optlist = []
370 370 if not ui.quiet:
371 371 if ui.verbose:
372 372 optlist.append((_("global options:"), commands.globalopts))
373 373 if name == 'shortlist':
374 374 optlist.append((_('use "hg help" for the full list '
375 375 'of commands'), ()))
376 376 else:
377 377 if name == 'shortlist':
378 378 msg = _('use "hg help" for the full list of commands '
379 379 'or "hg -v" for details')
380 380 elif name and not full:
381 381 msg = _('use "hg help %s" to show the full help '
382 382 'text') % name
383 383 else:
384 384 msg = _('use "hg -v help%s" to show builtin aliases and '
385 385 'global options') % (name and " " + name or "")
386 386 optlist.append((msg, ()))
387 387
388 388 if optlist:
389 389 for title, options in optlist:
390 390 rst.append('\n%s\n' % title)
391 391 if options:
392 392 rst.append('\n%s\n' % optrst(options, ui.verbose))
393 393 return rst
394 394
395 395 def helptopic(name):
396 396 for names, header, doc in helptable:
397 397 if name in names:
398 398 break
399 399 else:
400 400 raise error.UnknownCommand(name)
401 401
402 402 rst = [minirst.section(header)]
403 403
404 404 # description
405 405 if not doc:
406 406 rst.append(" %s\n" % _("(no help text available)"))
407 if util.safehasattr(doc, '__call__'):
407 if callable(doc):
408 408 rst += [" %s\n" % l for l in doc().splitlines()]
409 409
410 410 if not ui.verbose:
411 411 omitted = (_('use "hg help -v %s" to show more complete help') %
412 412 name)
413 413 indicateomitted(rst, omitted)
414 414
415 415 try:
416 416 cmdutil.findcmd(name, commands.table)
417 417 rst.append(_('\nuse "hg help -c %s" to see help for '
418 418 'the %s command\n') % (name, name))
419 419 except error.UnknownCommand:
420 420 pass
421 421 return rst
422 422
423 423 def helpext(name):
424 424 try:
425 425 mod = extensions.find(name)
426 426 doc = gettext(mod.__doc__) or _('no help text available')
427 427 except KeyError:
428 428 mod = None
429 429 doc = extensions.disabledext(name)
430 430 if not doc:
431 431 raise error.UnknownCommand(name)
432 432
433 433 if '\n' not in doc:
434 434 head, tail = doc, ""
435 435 else:
436 436 head, tail = doc.split('\n', 1)
437 437 rst = [_('%s extension - %s\n\n') % (name.split('.')[-1], head)]
438 438 if tail:
439 439 rst.extend(tail.splitlines(True))
440 440 rst.append('\n')
441 441
442 442 if not ui.verbose:
443 443 omitted = (_('use "hg help -v %s" to show more complete help') %
444 444 name)
445 445 indicateomitted(rst, omitted)
446 446
447 447 if mod:
448 448 try:
449 449 ct = mod.cmdtable
450 450 except AttributeError:
451 451 ct = {}
452 452 modcmds = set([c.split('|', 1)[0] for c in ct])
453 453 rst.extend(helplist(modcmds.__contains__))
454 454 else:
455 455 rst.append(_('use "hg help extensions" for information on enabling '
456 456 'extensions\n'))
457 457 return rst
458 458
459 459 def helpextcmd(name):
460 460 cmd, ext, mod = extensions.disabledcmd(ui, name,
461 461 ui.configbool('ui', 'strict'))
462 462 doc = gettext(mod.__doc__).splitlines()[0]
463 463
464 464 rst = listexts(_("'%s' is provided by the following "
465 465 "extension:") % cmd, {ext: doc}, indent=4)
466 466 rst.append('\n')
467 467 rst.append(_('use "hg help extensions" for information on enabling '
468 468 'extensions\n'))
469 469 return rst
470 470
471 471
472 472 rst = []
473 473 kw = opts.get('keyword')
474 474 if kw:
475 475 matches = topicmatch(kw)
476 476 for t, title in (('topics', _('Topics')),
477 477 ('commands', _('Commands')),
478 478 ('extensions', _('Extensions')),
479 479 ('extensioncommands', _('Extension Commands'))):
480 480 if matches[t]:
481 481 rst.append('%s:\n\n' % title)
482 482 rst.extend(minirst.maketable(sorted(matches[t]), 1))
483 483 rst.append('\n')
484 484 if not rst:
485 485 msg = _('no matches')
486 486 hint = _('try "hg help" for a list of topics')
487 487 raise util.Abort(msg, hint=hint)
488 488 elif name and name != 'shortlist':
489 489 if unknowncmd:
490 490 queries = (helpextcmd,)
491 491 elif opts.get('extension'):
492 492 queries = (helpext,)
493 493 elif opts.get('command'):
494 494 queries = (helpcmd,)
495 495 else:
496 496 queries = (helptopic, helpcmd, helpext, helpextcmd)
497 497 for f in queries:
498 498 try:
499 499 rst = f(name)
500 500 break
501 501 except error.UnknownCommand:
502 502 pass
503 503 else:
504 504 if unknowncmd:
505 505 raise error.UnknownCommand(name)
506 506 else:
507 507 msg = _('no such help topic: %s') % name
508 508 hint = _('try "hg help --keyword %s"') % name
509 509 raise util.Abort(msg, hint=hint)
510 510 else:
511 511 # program name
512 512 if not ui.quiet:
513 513 rst = [_("Mercurial Distributed SCM\n"), '\n']
514 514 rst.extend(helplist())
515 515
516 516 return ''.join(rst)
General Comments 0
You need to be logged in to leave comments. Login now