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

175 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 foo
62 if (_status != 0) {
63 echo 'hello'
64 raise # reads _status, _error_str, and _error_location ?
65 }
66 """
67
68 def __init__(
69 self,
70 mutable_opts, # type: state.MutableOpts
71 mem, # type: state.Mem
72 cmd_ev, # type: cmd_eval.CommandEvaluator
73 shell_ex, # type: vm._Executor
74 errfmt, # type: ui.ErrorFormatter
75 ):
76 # type: (...) -> None
77 self.mutable_opts = mutable_opts
78 self.mem = mem
79 self.shell_ex = shell_ex
80 self.cmd_ev = cmd_ev
81 self.errfmt = errfmt
82
83 def Run(self, cmd_val):
84 # type: (cmd_value.Argv) -> int
85 _, arg_r = flag_util.ParseCmdVal('try_',
86 cmd_val,
87 accept_typed_args=True)
88
89 rd = typed_args.ReaderForProc(cmd_val)
90 cmd = rd.RequiredBlock()
91 rd.Done()
92
93 status = 0 # success by default
94 try:
95 with ctx_Try(self.mutable_opts):
96 unused = self.cmd_ev.EvalCommand(cmd)
97 except error.Expr as e:
98 status = e.ExitStatus()
99 except error.ErrExit as e:
100 status = e.ExitStatus()
101
102 except error.Structured as e:
103 status = e.ExitStatus()
104 self.mem.SetTryError(e.ToDict())
105
106 self.mem.SetTryStatus(status)
107 return 0
108
109
110class Error(vm._Builtin):
111
112 def __init__(self):
113 # type: () -> None
114 pass
115
116 def Run(self, cmd_val):
117 # type: (cmd_value.Argv) -> int
118 _, arg_r = flag_util.ParseCmdVal('error',
119 cmd_val,
120 accept_typed_args=True)
121
122 message = arg_r.Peek()
123 if message is None:
124 raise error.Usage('expected a message to display',
125 cmd_val.arg_locs[0])
126
127 rd = typed_args.ReaderForProc(cmd_val)
128 # Status 10 is distinct from what the Oils interpreter itself uses. We
129 # use status 3 for expressions and 4 for encode/decode, and 10 "leaves
130 # room" for others.
131 # The user is of course free to choose status 1.
132 status = mops.BigTruncate(rd.NamedInt('status', 10))
133
134 # attach rest of named args to _error Dict
135 properties = rd.RestNamed()
136 rd.Done()
137
138 if status == 0:
139 raise error.Usage('status must be a non-zero integer',
140 cmd_val.arg_locs[0])
141
142 raise error.Structured(status, message, cmd_val.arg_locs[0],
143 properties)
144
145
146class BoolStatus(vm._Builtin):
147
148 def __init__(self, shell_ex, errfmt):
149 # type: (vm._Executor, ui.ErrorFormatter) -> None
150 self.shell_ex = shell_ex
151 self.errfmt = errfmt
152
153 def Run(self, cmd_val):
154 # type: (cmd_value.Argv) -> int
155
156 _, arg_r = flag_util.ParseCmdVal('boolstatus', cmd_val)
157
158 if arg_r.Peek() is None:
159 e_usage('expected a command to run', loc.Missing)
160
161 argv, locs = arg_r.Rest2()
162 cmd_val2 = cmd_value.Argv(argv, locs, cmd_val.typed_args,
163 cmd_val.pos_args, cmd_val.named_args,
164 cmd_val.block_arg)
165
166 cmd_st = CommandStatus.CreateNull(alloc_lists=True)
167 status = self.shell_ex.RunSimpleCommand(cmd_val2, cmd_st,
168 executor.DO_FORK)
169
170 if status not in (0, 1):
171 e_die_status(status,
172 'boolstatus expected status 0 or 1, got %d' % status,
173 locs[0])
174
175 return status