/
error.py
168 lines (124 loc) · 4.34 KB
/
error.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#!/usr/bin/env python2
"""
error.py
"""
from __future__ import print_function
from mycpp import mylib
from typing import Any, TYPE_CHECKING
if TYPE_CHECKING: # avoid circular build deps
from _devbuild.gen.syntax_asdl import Token, word_part_t, word_t
# Break circular dependency.
#from asdl import runtime
NO_SPID = -1
class _ControlFlow(Exception):
"""Internal execption for control flow.
break and continue are caught by loops, return is caught by functions.
NOTE: I tried representing this in ASDL, but in Python the base class has to
be BaseException. Also, 'Token' is in syntax.asdl but not runtime.asdl.
cflow =
-- break, continue, return, exit
Shell(Token keyword, int arg)
-- break, continue
| OilLoop(Token keyword)
-- return
| OilReturn(Token keyword, value val)
"""
def __init__(self, token, arg):
# type: (Token, int) -> None
"""
Args:
token: the keyword token
"""
self.token = token
self.arg = arg
def IsReturn(self):
# type: () -> bool
from _devbuild.gen.id_kind_asdl import Id # TODO: fix circular dep
return self.token.id == Id.ControlFlow_Return
def IsBreak(self):
# type: () -> bool
from _devbuild.gen.id_kind_asdl import Id # TODO: fix circular dep
return self.token.id == Id.ControlFlow_Break
def IsContinue(self):
# type: () -> bool
from _devbuild.gen.id_kind_asdl import Id # TODO: fix circular dep
return self.token.id == Id.ControlFlow_Continue
def StatusCode(self):
# type: () -> int
assert self.IsReturn()
return self.arg
def __repr__(self):
# type: () -> str
return '<_ControlFlow %s>' % self.token
if mylib.PYTHON:
class _ErrorWithLocation(Exception):
"""A parse error that can be formatted.
Formatting is in ui.PrintError.
"""
def __init__(self, msg, *args, **kwargs):
# type: (str, *Any, **Any) -> None
Exception.__init__(self)
self.msg = msg
self.args = args
# NOTE: We use a kwargs dict because Python 2 doesn't have keyword-only
# args.
self.span_id = kwargs.pop('span_id', NO_SPID) # type: int
self.token = kwargs.pop('token', None) # type: Token
self.part = kwargs.pop('part', None) # type: word_part_t
self.word = kwargs.pop('word', None) # type: word_t
# Runtime errors have a default status of 1. Parse errors return 2
# explicitly.
self.exit_status = kwargs.pop('status', 1) # type: int
if kwargs:
raise AssertionError('Invalid keyword args %s' % kwargs)
def HasLocation(self):
# type: () -> bool
return bool(self.span_id != NO_SPID or
self.token or self.part or self.word)
def ExitStatus(self):
# type: () -> int
return self.exit_status
def __repr__(self):
# type: () -> str
return '<%s %s %r %r %s>' % (
self.msg, self.args, self.token, self.word, self.exit_status)
def __str__(self):
# type: () -> str
# The default doesn't work very well?
return repr(self)
def UserErrorString(self):
# type: () -> str
return self.msg % self.args
# Need a better constructor
if mylib.PYTHON:
class Parse(_ErrorWithLocation):
"""Used in the parsers."""
class RedirectEval(_ErrorWithLocation):
"""Used in the CommandEvaluator.
A bad redirect causes the SimpleCommand to return with status 1. To make it
fatal, use set -o errexit.
"""
class Runtime(_ErrorWithLocation):
"""A non-fatal runtime error, e.g. for builtins."""
class FatalRuntime(_ErrorWithLocation):
"""An exception that propagates to the top level.
Used in the evaluators, and also also used in test builtin for invalid
argument.
"""
class Strict(FatalRuntime):
"""Depending on shell options, these errors may be caught and ignored.
For example, if options like these are ON:
set -o strict_arith
set -o strict_word_eval
then we re-raise the error so it's caught by the top level. Otherwise
we catch it and return a dummy value like '' or -1 (i.e. what bash commonly
does.)
TODO: Have levels, like:
OIL_STRICT_PRINT=2 # print warnings at level 2 and above
OIL_STRICT_DIE=1 # abort the program at level 1 and above
"""
if mylib.PYTHON:
class ErrExit(FatalRuntime):
"""For set -e.
Travels between WordEvaluator and CommandEvaluator.
"""