OILS / builtin / error_ysh.py View on Github | oilshell.org

177 lines, 100 significant
1from __future__ import print_function
2
3from _devbuild.gen.option_asdl import option_i
4from _devbuild.gen.runtime_asdl import cmd_value, CommandStatus
5from _devbuild.gen.syntax_asdl import loc
6from core import error
7from core.error import e_die_status, e_usage
8from core import executor
9from core import state
10from core import vm
11from frontend import flag_util
12from frontend import typed_args
13from mycpp import mops
14from mycpp.mylib import log
15
16_ = log
17
18from typing import Any, TYPE_CHECKING
19if TYPE_CHECKING:
20 from core import ui
21 from osh import cmd_eval
22
23
24class ctx_Try(object):
25
26 def __init__(self, mutable_opts):
27 # type: (state.MutableOpts) -> None
28
29 mutable_opts.Push(option_i.errexit, True)
30 self.mutable_opts = mutable_opts
31
32 def __enter__(self):
33 # type: () -> None
34 pass
35
36 def __exit__(self, type, value, traceback):
37 # type: (Any, Any, Any) -> None
38 self.mutable_opts.Pop(option_i.errexit)
39
40
41class Try(vm._Builtin):
42 """Allows explicit handling of errors.
43
44 Takes command argv, or a block:
45
46 try ls /bad
47
48 try {
49 var x = 1 / 0
50
51 ls | wc -l
52
53 diff <(sort left.txt) <(sort right.txt)
54 }
55
56 TODO:
57 - Set _error_str (e.UserErrorString())
58 - Set _error_location
59 - These could be used by a 'raise' builtin? Or 'reraise'
60
61 try {
62 foo
63 }
64 if (_status !== 0) {
65 echo 'hello'
66 raise # reads _status, _error_str, and _error_location ?
67 }
68 """
69
70 def __init__(
71 self,
72 mutable_opts, # type: state.MutableOpts
73 mem, # type: state.Mem
74 cmd_ev, # type: cmd_eval.CommandEvaluator
75 shell_ex, # type: vm._Executor
76 errfmt, # type: ui.ErrorFormatter
77 ):
78 # type: (...) -> None
79 self.mutable_opts = mutable_opts
80 self.mem = mem
81 self.shell_ex = shell_ex
82 self.cmd_ev = cmd_ev
83 self.errfmt = errfmt
84
85 def Run(self, cmd_val):
86 # type: (cmd_value.Argv) -> int
87 _, arg_r = flag_util.ParseCmdVal('try_',
88 cmd_val,
89 accept_typed_args=True)
90
91 rd = typed_args.ReaderForProc(cmd_val)
92 cmd = rd.RequiredBlock()
93 rd.Done()
94
95 status = 0 # success by default
96 try:
97 with ctx_Try(self.mutable_opts):
98 unused = self.cmd_ev.EvalCommand(cmd)
99 except error.Expr as e:
100 status = e.ExitStatus()
101 except error.ErrExit as e:
102 status = e.ExitStatus()
103
104 except error.Structured as e:
105 status = e.ExitStatus()
106 self.mem.SetTryError(e.ToDict())
107
108 self.mem.SetTryStatus(status)
109 return 0
110
111
112class Error(vm._Builtin):
113
114 def __init__(self):
115 # type: () -> None
116 pass
117
118 def Run(self, cmd_val):
119 # type: (cmd_value.Argv) -> int
120 _, arg_r = flag_util.ParseCmdVal('error',
121 cmd_val,
122 accept_typed_args=True)
123
124 message = arg_r.Peek()
125 if message is None:
126 raise error.Usage('expected a message to display',
127 cmd_val.arg_locs[0])
128
129 rd = typed_args.ReaderForProc(cmd_val)
130 # Status 10 is distinct from what the Oils interpreter itself uses. We
131 # use status 3 for expressions and 4 for encode/decode, and 10 "leaves
132 # room" for others.
133 # The user is of course free to choose status 1.
134 status = mops.BigTruncate(rd.NamedInt('code', 10))
135
136 # attach rest of named args to _error Dict
137 properties = rd.RestNamed()
138 rd.Done()
139
140 if status == 0:
141 raise error.Usage('status must be a non-zero integer',
142 cmd_val.arg_locs[0])
143
144 raise error.Structured(status, message, cmd_val.arg_locs[0],
145 properties)
146
147
148class BoolStatus(vm._Builtin):
149
150 def __init__(self, shell_ex, errfmt):
151 # type: (vm._Executor, ui.ErrorFormatter) -> None
152 self.shell_ex = shell_ex
153 self.errfmt = errfmt
154
155 def Run(self, cmd_val):
156 # type: (cmd_value.Argv) -> int
157
158 _, arg_r = flag_util.ParseCmdVal('boolstatus', cmd_val)
159
160 if arg_r.Peek() is None:
161 e_usage('expected a command to run', loc.Missing)
162
163 argv, locs = arg_r.Rest2()
164 cmd_val2 = cmd_value.Argv(argv, locs, cmd_val.typed_args,
165 cmd_val.pos_args, cmd_val.named_args,
166 cmd_val.block_arg)
167
168 cmd_st = CommandStatus.CreateNull(alloc_lists=True)
169 status = self.shell_ex.RunSimpleCommand(cmd_val2, cmd_st,
170 executor.DO_FORK)
171
172 if status not in (0, 1):
173 e_die_status(status,
174 'boolstatus expected status 0 or 1, got %d' % status,
175 locs[0])
176
177 return status