Commit | Line | Data |
---|---|---|
410f6138 CH |
1 | # The software in this package is distributed under the GNU General |
2 | # Public License version 2 (with a special exception described below). | |
3 | # | |
4 | # A copy of GNU General Public License (GPL) is included in this distribution, | |
5 | # in the file COPYING.GPL. | |
6 | # | |
7 | # As a special exception, if other files instantiate templates or use macros | |
8 | # or inline functions from this file, or you compile this file and link it | |
9 | # with other works to produce a work based on this file, this file | |
10 | # does not by itself cause the resulting work to be covered | |
11 | # by the GNU General Public License. | |
12 | # | |
13 | # However the source code for this file must still be made available | |
14 | # in accordance with section (3) of the GNU General Public License. | |
15 | # | |
16 | # This exception does not invalidate any other reasons why a work based | |
17 | # on this file might be covered by the GNU General Public License. | |
18 | ||
19 | """ | |
20 | argparse_helpers: Some convenience helpers for argparse | |
21 | ||
22 | Featuring | |
23 | - NonExitingParser: a subclass of ArgumentParser that does not do sys.exit(2) | |
24 | on parse error but instead raises a ArgParserWantsExit exception. | |
25 | - function existing_file which can be used as a type in add_argument() calls | |
26 | ||
27 | .. codeauthor:: Intra2net | |
28 | """ | |
29 | ||
30 | from argparse import ArgumentParser, ArgumentTypeError | |
31 | from os.path import isfile | |
32 | ||
33 | ||
34 | class ArgParserWantsExit(Exception): | |
35 | """ | |
36 | Exception raised from NonExitingParser instead of calling sys.exit(2). | |
37 | """ | |
38 | ||
39 | def __init__(self, message): | |
40 | super(ArgParserWantsExit, self).__init__("Error parsing args: " + | |
41 | message) | |
42 | ||
43 | ||
44 | class NonExitingParser(ArgumentParser): | |
45 | """ArgumentParser that does not call sys.exit(2) on parse failure. | |
46 | ||
47 | Calling sys.exit also just raises a SystemExit exception. But that is not | |
48 | a subclass of Exception and not as explicit and specific as this one. | |
49 | ||
50 | Convenient e.g. for global try-except blocks e.g. in a daemon:: | |
51 | ||
52 | def main(): | |
53 | log = None | |
54 | try: | |
55 | log = create_log() | |
56 | parser = NonExitingParser() | |
57 | # ... add args ... | |
58 | args = parser.parse_args() | |
59 | # ... rest of your program ... | |
60 | except Exception as exc: | |
61 | log.error('uncaught exception: exc') # will always show in log | |
62 | ||
63 | """ | |
64 | def error(self, message): | |
65 | """Called when error occurred parsing args. Raise error, not exit.""" | |
66 | raise ArgParserWantsExit(message) | |
67 | ||
68 | ||
69 | def existing_file(filename): | |
70 | """ | |
71 | Function that raises ArgumentTypeError if argument is not an existing file. | |
72 | ||
73 | Returns filename if it is valid. Can be used as `type` argument in | |
74 | :py:meth:`argparse.ArgumentParser.add_argument` | |
75 | ||
76 | If you want to open the file, you can as well use the python built-in | |
77 | :py:class:`argparse.FileType` instead. | |
78 | """ | |
79 | if isfile(filename): | |
80 | return filename | |
81 | raise ArgumentTypeError('{} is not an existing file'.format(filename)) | |
752bf4ea CH |
82 | |
83 | ||
84 | def existing_file_or_empty(filename=''): | |
85 | """ | |
86 | Like :py:func:`existing_file` but accepts empty filename (returns '' then). | |
87 | """ | |
88 | if not filename.strip(): | |
89 | return '' | |
90 | return existing_file(filename) |