Coverage for /usr/local/lib/python3.11/dist-packages/pyrocko/squirrel/tool/common.py: 79%

232 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2024-03-07 11:54 +0000

1# http://pyrocko.org - GPLv3 

2# 

3# The Pyrocko Developers, 21st Century 

4# ---|P------/S----------~Lg---------- 

5 

6''' 

7Squirrel command line tool infrastructure and argument parsing. 

8''' 

9 

10import os 

11import sys 

12import re 

13import argparse 

14import logging 

15import textwrap 

16 

17from pyrocko import util, progress 

18from pyrocko.squirrel import error 

19 

20 

21logger = logging.getLogger('psq.tool.common') 

22 

23help_time_format = 'Format: ```YYYY-MM-DD HH:MM:SS.FFF```, truncation allowed.' 

24 

25 

26def unwrap(s): 

27 if s is None: 

28 return None 

29 s = s.strip() 

30 parts = re.split(r'\n{2,}', s) 

31 lines = [] 

32 for part in parts: 

33 plines = part.splitlines() 

34 if not any(line.startswith(' ') for line in plines): 

35 lines.append(' '.join(plines)) 

36 else: 

37 lines.extend(plines) 

38 

39 lines.append('') 

40 

41 return '\n'.join(lines) 

42 

43 

44def wrap(s): 

45 lines = [] 

46 parts = re.split(r'\n{2,}', s) 

47 for part in parts: 

48 plines = part.splitlines() 

49 if part.startswith('usage:') \ 

50 or all(line.startswith(' ') for line in plines): 

51 lines.extend(plines) 

52 else: 

53 for line in plines: 

54 if not line: 

55 lines.append(line) 

56 if not line.startswith(' '): 

57 lines.extend( 

58 textwrap.wrap(line, 79,)) 

59 else: 

60 lines.extend( 

61 textwrap.wrap(line, 79, subsequent_indent=' '*24)) 

62 

63 lines.append('') 

64 

65 return '\n'.join(lines) 

66 

67 

68def wrap_usage(s): 

69 lines = [] 

70 for line in s.splitlines(): 

71 if not line.startswith('usage:'): 

72 lines.append(line) 

73 else: 

74 lines.extend(textwrap.wrap(line, 79, subsequent_indent=' '*24)) 

75 

76 return '\n'.join(lines) 

77 

78 

79def formatter_with_width(n): 

80 class PyrockoHelpFormatter(argparse.RawDescriptionHelpFormatter): 

81 def __init__(self, *args, **kwargs): 

82 kwargs['width'] = n 

83 kwargs['max_help_position'] = 24 

84 argparse.RawDescriptionHelpFormatter.__init__( 

85 self, *args, **kwargs) 

86 

87 # fix alignment problems, with the post-processing wrapping 

88 self._action_max_length = 24 

89 

90 return PyrockoHelpFormatter 

91 

92 

93class PyrockoArgumentParser(argparse.ArgumentParser): 

94 ''' 

95 Tweaks and extends the standard argument parser to simplify the generation 

96 of the online docs. 

97 

98 We want to convert the ``--help`` outputs to ``rst`` for the html docs. 

99 Problem is that argparse's ``HelpFormatter`` to date have no public 

100 interface which we could use to achieve this. The solution here is a bit 

101 clunky but works ok for our purposes. We allow markup like 

102 :literal:`\\`\\`code\\`\\`` which is kept when producing ``rst`` (by 

103 parsing the final ``--help`` output) but stripped out when doing normal 

104 ``--help``. This leads to a problem with the internal output wrapping of 

105 argparse which it does before the stripping. To solve, we render with 

106 argparse to a very wide width and do the wrapping in post-processing. 

107 :literal:`\\`\\`code\\`\\`` is replaced with just ``code`` in normal 

108 output. :literal:`\\`\\`\\`code\\`\\`\\`` is replaced with ``'code'`` in 

109 normal output and with :literal:`\\`\\`code\\`\\`` in ``rst`` output. 

110 ``rst`` output is selected with environment variable 

111 ``PYROCKO_RST_HELP=1``. The script ``maintenance/argparse_help_to_rst.py`` 

112 extracts the ``rst`` help and generates the ``rst`` files for the docs. 

113 ''' 

114 

115 def __init__( 

116 self, prog=None, usage=None, description=None, epilog=None, 

117 **kwargs): 

118 

119 kwargs['formatter_class'] = formatter_with_width(1000000) 

120 

121 description = unwrap(description) 

122 epilog = unwrap(epilog) 

123 

124 argparse.ArgumentParser.__init__( 

125 self, prog=prog, usage=usage, description=description, 

126 epilog=epilog, **kwargs) 

127 

128 if hasattr(self, '_action_groups'): 

129 for group in self._action_groups: 

130 if group.title == 'positional arguments': 

131 group.title = 'Positional arguments' 

132 

133 elif group.title == 'optional arguments': 

134 group.title = 'Optional arguments' 

135 

136 elif group.title == 'options': 

137 group.title = 'Options' 

138 

139 self.raw_help = False 

140 

141 def format_help(self, *args, **kwargs): 

142 s = argparse.ArgumentParser.format_help(self, *args, **kwargs) 

143 

144 # replace usage with wrapped one from argparse because naive wrapping 

145 # does not look good. 

146 formatter_class = self.formatter_class 

147 self.formatter_class = formatter_with_width(79) 

148 usage = self.format_usage() 

149 self.formatter_class = formatter_class 

150 

151 lines = [] 

152 for line in s.splitlines(): 

153 if line.startswith('usage:'): 

154 lines.append(usage) 

155 else: 

156 lines.append(line) 

157 

158 s = '\n'.join(lines) 

159 

160 if os.environ.get('PYROCKO_RST_HELP', '0') == '0': 

161 s = s.replace('```', "'") 

162 s = s.replace('``', '') 

163 s = wrap(s) 

164 else: 

165 s = s.replace('```', '``') 

166 s = wrap_usage(s) 

167 

168 return s 

169 

170 

171class SquirrelArgumentParser(PyrockoArgumentParser): 

172 ''' 

173 Parser for CLI arguments with a some extras for Squirrel based apps. 

174 

175 :param command: 

176 Implementation of the command. 

177 :type command: 

178 :py:class:`SquirrelCommand` or module providing the same interface 

179 

180 :param subcommands: 

181 Implementations of subcommands. 

182 :type subcommands: 

183 :py:class:`list` of :py:class:`SquirrelCommand` or modules providing 

184 the same interface 

185 

186 :param \\*args: 

187 Handed through to base class's init. 

188 

189 :param \\*\\*kwargs: 

190 Handed through to base class's init. 

191 ''' 

192 

193 def __init__(self, *args, command=None, subcommands=[], **kwargs): 

194 

195 self._command = command 

196 self._subcommands = subcommands 

197 self._have_selection_arguments = False 

198 self._have_query_arguments = False 

199 

200 kwargs['add_help'] = False 

201 PyrockoArgumentParser.__init__(self, *args, **kwargs) 

202 add_standard_arguments(self) 

203 self._command = None 

204 self._subcommands = [] 

205 if command: 

206 self.set_command(command) 

207 

208 if subcommands: 

209 self.set_subcommands(subcommands) 

210 

211 def set_command(self, command): 

212 command.setup(self) 

213 self.set_defaults(target=command.run) 

214 

215 def set_subcommands(self, subcommands): 

216 subparsers = self.add_subparsers( 

217 metavar='SUBCOMMAND', 

218 title='Subcommands') 

219 

220 for mod in subcommands: 

221 subparser = mod.make_subparser(subparsers) 

222 if subparser is None: 

223 raise Exception( 

224 'make_subparser(subparsers) must return the created ' 

225 'parser.') 

226 

227 mod.setup(subparser) 

228 subparser.set_defaults(target=mod.run, subparser=subparser) 

229 

230 def parse_args(self, args=None, namespace=None): 

231 ''' 

232 Parse arguments given on command line. 

233 

234 Extends the functionality of 

235 :py:meth:`argparse.ArgumentParser.parse_args` to process and handle the 

236 standard options ``--loglevel``, ``--progress`` and ``--help``. 

237 ''' 

238 

239 args = PyrockoArgumentParser.parse_args( 

240 self, args=args, namespace=namespace) 

241 

242 eff_parser = args.__dict__.get('subparser', self) 

243 

244 process_standard_arguments(eff_parser, args) 

245 

246 if eff_parser._have_selection_arguments: 

247 def make_squirrel(): 

248 return squirrel_from_selection_arguments(args) 

249 

250 args.make_squirrel = make_squirrel 

251 

252 if eff_parser._have_query_arguments: 

253 try: 

254 args.squirrel_query = squirrel_query_from_arguments(args) 

255 except (error.SquirrelError, error.ToolError) as e: 

256 logger.fatal(str(e)) 

257 sys.exit(1) 

258 

259 return args 

260 

261 def dispatch(self, args): 

262 ''' 

263 Dispatch execution to selected command/subcommand. 

264 

265 :param args: 

266 Parsed arguments obtained from :py:meth:`parse_args`. 

267 

268 :returns: 

269 ``True`` if dispatching was successful, ``False`` otherwise. 

270 

271 If an exception of type 

272 :py:exc:`~pyrocko.squirrel.error.SquirrelError` or 

273 :py:exc:`~pyrocko.squirrel.error.ToolError` is caught, the error is 

274 logged and the program is terminated with exit code 1. 

275 ''' 

276 eff_parser = args.__dict__.get('subparser', self) 

277 target = args.__dict__.get('target', None) 

278 

279 if target: 

280 try: 

281 target(eff_parser, args) 

282 return True 

283 

284 except (error.SquirrelError, error.ToolError) as e: 

285 logger.fatal(str(e)) 

286 sys.exit(1) 

287 

288 return False 

289 

290 def run(self, args=None): 

291 ''' 

292 Parse arguments and dispatch to selected command/subcommand. 

293 

294 This simply calls :py:meth:`parse_args` and then :py:meth:`dispatch` 

295 with the obtained ``args``. A usage message is printed if no command is 

296 selected. 

297 ''' 

298 args = self.parse_args(args) 

299 if not self.dispatch(args): 

300 self.print_help() 

301 

302 def add_squirrel_selection_arguments(self): 

303 ''' 

304 Set up command line options commonly used to configure a 

305 :py:class:`~pyrocko.squirrel.base.Squirrel` instance. 

306 

307 This will optional arguments ``--add``, ``--include``, ``--exclude``, 

308 ``--optimistic``, ``--format``, ``--add-only``, ``--persistent``, and 

309 ``--dataset``. 

310 

311 Call ``args.make_squirrel()`` on the arguments returned from 

312 :py:meth:`parse_args` to finally instantiate and configure the 

313 :py:class:`~pyrocko.squirrel.base.Squirrel` instance. 

314 ''' 

315 add_squirrel_selection_arguments(self) 

316 self._have_selection_arguments = True 

317 

318 def add_squirrel_query_arguments(self, without=[]): 

319 ''' 

320 Set up command line options commonly used in squirrel queries. 

321 

322 This will add optional arguments ``--kinds``, ``--codes``, ``--tmin``, 

323 ``--tmax``, and ``--time``. 

324 

325 Once finished with parsing, the query arguments are available as 

326 ``args.squirrel_query`` on the arguments returned from 

327 :py:meth:`parse_args`. 

328 

329 :param without: 

330 Suppress adding given options. 

331 :type without: 

332 :py:class:`list` of :py:class:`str`, choices: ``'tmin'``, 

333 ``'tmax'``, ``'codes'``, and ``'time'``. 

334 ''' 

335 

336 add_squirrel_query_arguments(self, without=without) 

337 self._have_query_arguments = True 

338 

339 

340def csvtype(choices): 

341 def splitarg(arg): 

342 values = arg.split(',') 

343 for value in values: 

344 if value not in choices: 

345 raise argparse.ArgumentTypeError( 

346 'Invalid choice: {!r} (choose from {})' 

347 .format(value, ', '.join(map(repr, choices)))) 

348 return values 

349 return splitarg 

350 

351 

352def dq(x): 

353 return '``%s``' % x 

354 

355 

356def ldq(xs): 

357 return ', '.join(dq(x) for x in xs) 

358 

359 

360def add_standard_arguments(parser): 

361 group = parser.add_argument_group('General options') 

362 group.add_argument( 

363 '--help', '-h', 

364 action='help', 

365 help='Show this help message and exit.') 

366 

367 loglevel_choices = ['critical', 'error', 'warning', 'info', 'debug'] 

368 loglevel_default = 'info' 

369 

370 group.add_argument( 

371 '--loglevel', 

372 choices=loglevel_choices, 

373 default=loglevel_default, 

374 metavar='LEVEL', 

375 help='Set logger level. Choices: %s. Default: %s.' % ( 

376 ldq(loglevel_choices), dq(loglevel_default))) 

377 

378 progress_choices = ['terminal', 'log', 'off'] 

379 progress_default = 'terminal' 

380 

381 group.add_argument( 

382 '--progress', 

383 choices=progress_choices, 

384 default=progress_default, 

385 metavar='DEST', 

386 help='Set how progress status is reported. Choices: %s. ' 

387 'Default: %s.' % ( 

388 ldq(progress_choices), dq(progress_default))) 

389 

390 

391def process_standard_arguments(parser, args): 

392 loglevel = args.__dict__.pop('loglevel') 

393 util.setup_logging(parser.prog, loglevel) 

394 

395 pmode = args.__dict__.pop('progress') 

396 progress.set_default_viewer(pmode) 

397 

398 

399def add_squirrel_selection_arguments(parser): 

400 ''' 

401 Set up command line options commonly used to configure a 

402 :py:class:`~pyrocko.squirrel.base.Squirrel` instance. 

403 

404 This will optional arguments ``--add``, ``--include``, ``--exclude``, 

405 ``--optimistic``, ``--format``, ``--add-only``, ``--persistent``, 

406 and ``--dataset`` to a given argument parser. 

407 

408 Once finished with parsing, call 

409 :py:func:`squirrel_from_selection_arguments` to finally instantiate and 

410 configure the :py:class:`~pyrocko.squirrel.base.Squirrel` instance. 

411 

412 :param parser: 

413 The argument parser to be configured. 

414 :type parser: 

415 argparse.ArgumentParser 

416 ''' 

417 from pyrocko import squirrel as sq 

418 

419 group = parser.add_argument_group('Data collection options') 

420 

421 group.add_argument( 

422 '--add', '-a', 

423 dest='paths', 

424 metavar='PATH', 

425 nargs='+', 

426 help='Add files and directories with waveforms, metadata and events. ' 

427 'Content is indexed and added to the temporary (default) or ' 

428 'persistent (see ``--persistent``) data selection.') 

429 

430 group.add_argument( 

431 '--include', 

432 dest='include', 

433 metavar='REGEX', 

434 help='Only include files whose paths match the regular expression ' 

435 "``REGEX``. Examples: ``--include='\\.MSEED$'`` would only " 

436 'match files ending with ```.MSEED```. ' 

437 "``--include='\\.BH[EN]\\.'`` would match paths containing " 

438 "```.BHE.``` or ```.BHN.```. ``--include='/2011/'`` would " 

439 'match paths with a subdirectory ```2011``` in their path ' 

440 'hierarchy.') 

441 

442 group.add_argument( 

443 '--exclude', 

444 dest='exclude', 

445 metavar='REGEX', 

446 help='Only include files whose paths do not match the regular ' 

447 "expression ``REGEX``. Examples: ``--exclude='/\\.DS_Store/'`` " 

448 'would exclude anything inside any ```.DS_Store``` subdirectory.') 

449 

450 group.add_argument( 

451 '--optimistic', '-o', 

452 action='store_false', 

453 dest='check', 

454 default=True, 

455 help='Disable checking file modification times for faster startup.') 

456 

457 group.add_argument( 

458 '--format', '-f', 

459 dest='format', 

460 metavar='FORMAT', 

461 default='detect', 

462 choices=sq.supported_formats(), 

463 help='Assume input files are of given ``FORMAT``. Choices: %s. ' 

464 'Default: %s.' % ( 

465 ldq(sq.supported_formats()), 

466 dq('detect'))) 

467 

468 group.add_argument( 

469 '--add-only', 

470 type=csvtype(sq.supported_content_kinds()), 

471 dest='kinds_add', 

472 metavar='KINDS', 

473 help='Restrict meta-data scanning to given content kinds. ' 

474 '``KINDS`` is a comma-separated list of content kinds. ' 

475 'Choices: %s. By default, all content kinds are indexed.' 

476 % ldq(sq.supported_content_kinds())) 

477 

478 group.add_argument( 

479 '--persistent', '-p', 

480 dest='persistent', 

481 metavar='NAME', 

482 help='Create/use persistent selection with given ``NAME``. Persistent ' 

483 'selections can be used to speed up startup of Squirrel-based ' 

484 'applications.') 

485 

486 group.add_argument( 

487 '--dataset', '-d', 

488 dest='datasets', 

489 default=[], 

490 action='append', 

491 metavar='FILE', 

492 help='Add files, directories and remote sources from dataset ' 

493 'description file. This option can be repeated to add multiple ' 

494 'datasets. Run ```squirrel template``` to obtain examples of ' 

495 'dataset description files.') 

496 

497 

498def squirrel_from_selection_arguments(args): 

499 ''' 

500 Create a :py:class:`~pyrocko.squirrel.base.Squirrel` instance from command 

501 line arguments. 

502 

503 Use :py:func:`add_squirrel_selection_arguments` to configure the parser 

504 with the necessary options. 

505 

506 :param args: 

507 Parsed command line arguments, as returned by 

508 :py:meth:`argparse.ArgumentParser.parse_args`. 

509 

510 :returns: 

511 :py:class:`pyrocko.squirrel.base.Squirrel` instance with paths, 

512 datasets and remote sources added. 

513 

514 ''' 

515 from pyrocko.squirrel import base 

516 

517 squirrel = base.Squirrel(persistent=args.persistent) 

518 

519 with progress.view(): 

520 if args.paths: 

521 squirrel.add( 

522 args.paths, 

523 check=args.check, 

524 format=args.format, 

525 kinds=args.kinds_add or None, 

526 include=args.include, 

527 exclude=args.exclude) 

528 

529 with progress.task('add datasets', logger=logger) as task: 

530 for dataset_path in task(args.datasets): 

531 squirrel.add_dataset( 

532 dataset_path, check=args.check) 

533 

534 return squirrel 

535 

536 

537def add_squirrel_query_arguments(parser, without=[]): 

538 ''' 

539 Set up command line options commonly used in squirrel queries. 

540 

541 This will add optional arguments ``--kinds``, ``--codes``, ``--tmin``, 

542 ``--tmax``, and ``--time``. 

543 

544 Once finished with parsing, call 

545 :py:func:`squirrel_query_from_arguments` to get the parsed values. 

546 

547 :param parser: 

548 The argument parser to be configured. 

549 :type parser: 

550 argparse.ArgumentParser 

551 

552 :param without: 

553 Suppress adding given options. 

554 :type without: 

555 :py:class:`list` of :py:class:`str` 

556 ''' 

557 

558 from pyrocko import squirrel as sq 

559 

560 group = parser.add_argument_group('Data query options') 

561 

562 if 'kinds' not in without: 

563 group.add_argument( 

564 '--kinds', 

565 type=csvtype(sq.supported_content_kinds()), 

566 dest='kinds', 

567 metavar='KINDS', 

568 help='Content kinds to query. ``KINDS`` is a comma-separated list ' 

569 'of content kinds. Choices: %s. By default, all content ' 

570 'kinds are queried.' % ldq(sq.supported_content_kinds())) 

571 

572 if 'codes' not in without: 

573 group.add_argument( 

574 '--codes', 

575 dest='codes', 

576 metavar='CODES', 

577 help='Code patterns to query (``STA``, ``NET.STA``, ' 

578 '``NET.STA.LOC``, ``NET.STA.LOC.CHA``, or ' 

579 '``NET.STA.LOC.CHA.EXTRA``). The pattern may contain ' 

580 'wildcards ``*`` (zero or more arbitrary characters), ``?`` ' 

581 '(single arbitrary character), and ``[CHARS]`` (any ' 

582 'character out of ``CHARS``). Multiple patterns can be ' 

583 'given by separating them with commas.') 

584 

585 if 'tmin' not in without: 

586 group.add_argument( 

587 '--tmin', 

588 dest='tmin', 

589 metavar='TIME', 

590 help='Begin of time interval to query. %s' % help_time_format) 

591 

592 if 'tmax' not in without: 

593 group.add_argument( 

594 '--tmax', 

595 dest='tmax', 

596 metavar='TIME', 

597 help='End of time interval to query. %s' % help_time_format) 

598 

599 if 'time' not in without: 

600 group.add_argument( 

601 '--time', 

602 dest='time', 

603 metavar='TIME', 

604 help='Time instant to query. %s' % help_time_format) 

605 

606 

607def squirrel_query_from_arguments(args): 

608 ''' 

609 Get common arguments to be used in squirrel queries from command line. 

610 

611 Use :py:func:`add_squirrel_query_arguments` to configure the parser with 

612 the necessary options. 

613 

614 :param args: 

615 Parsed command line arguments, as returned by 

616 :py:meth:`argparse.ArgumentParser.parse_args`. 

617 

618 :returns: 

619 :py:class:`dict` with any parsed option values. 

620 ''' 

621 

622 from pyrocko import squirrel as sq 

623 

624 d = {} 

625 

626 if 'kinds' in args and args.kinds: 

627 d['kind'] = args.kinds 

628 if 'tmin' in args and args.tmin: 

629 d['tmin'] = util.str_to_time_fillup(args.tmin) 

630 if 'tmax' in args and args.tmax: 

631 d['tmax'] = util.str_to_time_fillup(args.tmax) 

632 if 'time' in args and args.time: 

633 d['tmin'] = d['tmax'] = util.str_to_time_fillup(args.time) 

634 if 'codes' in args and args.codes: 

635 d['codes'] = [ 

636 sq.to_codes_guess(s.strip()) for s in args.codes.split(',')] 

637 

638 if ('tmin' in d and 'time' in d) or ('tmax' in d and 'time' in d): 

639 raise error.SquirrelError( 

640 'Options --tmin/--tmax and --time are mutually exclusive.') 

641 return d 

642 

643 

644class SquirrelCommand(object): 

645 ''' 

646 Base class for Squirrel-based CLI programs and subcommands. 

647 ''' 

648 

649 def fail(self, message): 

650 ''' 

651 Raises :py:exc:`~pyrocko.squirrel.error.ToolError`. 

652 

653 :py:meth:`SquirrelArgumentParser.run` catches 

654 :py:exc:`~pyrocko.squirrel.error.ToolError`, logs the error message and 

655 terminates with an error exit state. 

656 ''' 

657 raise error.ToolError(message) 

658 

659 def make_subparser(self, subparsers): 

660 ''' 

661 To be implemented in subcommand. Create subcommand parser. 

662 

663 Must return a newly created parser obtained with 

664 ``subparsers.add_parser(...)``, e.g.:: 

665 

666 def make_subparser(self, subparsers): 

667 return subparsers.add_parser( 

668 'plot', help='Draw a nice plot.') 

669 

670 ''' 

671 return subparsers.add_parser( 

672 self.__class__.__name__, help='Undocumented.') 

673 

674 def setup(self, parser): 

675 ''' 

676 To be implemented in subcommand. Configure parser. 

677 

678 :param parser: 

679 The argument parser to be configured. 

680 :type parser: 

681 argparse.ArgumentParser 

682 

683 Example:: 

684 

685 def setup(self, parser): 

686 parser.add_squirrel_selection_arguments() 

687 parser.add_squirrel_query_arguments() 

688 parser.add_argument( 

689 '--fmin', 

690 dest='fmin', 

691 metavar='FLOAT', 

692 type=float, 

693 help='Corner of highpass [Hz].') 

694 ''' 

695 pass 

696 

697 def run(self, parser, args): 

698 ''' 

699 To be implemented in subcommand. Main routine of the command. 

700 

701 :param parser: 

702 The argument parser to be configured. 

703 :type parser: 

704 argparse.ArgumentParser 

705 

706 :param args: 

707 Parsed command line arguments, as returned by 

708 :py:meth:`argparse.ArgumentParser.parse_args`. 

709 

710 Example:: 

711 

712 def run(self, parser, args): 

713 print('User has selected fmin = %g Hz' % args.fmin) 

714 

715 # args.make_squirrel() is available if 

716 # parser.add_squirrel_selection_arguments() was called during 

717 # setup(). 

718 

719 sq = args.make_squirrel() 

720 

721 # args.squirrel_query is available if 

722 # praser.add_squirrel_query_arguments() was called during 

723 # setup(). 

724 

725 stations = sq.get_stations(**args.squirrel_query) 

726 ''' 

727 pass 

728 

729 

730__all__ = [ 

731 'PyrockoArgumentParser', 

732 'SquirrelArgumentParser', 

733 'SquirrelCommand', 

734 'add_squirrel_selection_arguments', 

735 'squirrel_from_selection_arguments', 

736 'add_squirrel_query_arguments', 

737 'squirrel_query_from_arguments', 

738]