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

141 lines, 80 significant
1from __future__ import print_function
2
3from _devbuild.gen.runtime_asdl import cmd_value
4from _devbuild.gen.value_asdl import value, value_e
5from core import dev
6from core import error
7from core import state
8from core import vm
9from display import ui
10from frontend import args
11from frontend import flag_util
12from mycpp.mylib import log
13
14from typing import cast, Dict, TYPE_CHECKING
15if TYPE_CHECKING:
16 from core import optview
17 from osh import cmd_eval
18
19_ = log
20
21
22class IsMain(vm._Builtin):
23 """
24 if is-main { echo hi }
25 """
26
27 def __init__(self, mem):
28 # type: (state.Mem) -> None
29 self.mem = mem
30
31 def Run(self, cmd_val):
32 # type: (cmd_value.Argv) -> int
33 return 0 if self.mem.is_main else 1
34
35
36class SourceGuard(vm._Builtin):
37 """
38 source-guard main || return
39 """
40
41 def __init__(self, guards, exec_opts, errfmt):
42 # type: (Dict[str, bool], optview.Exec, ui.ErrorFormatter) -> None
43 self.guards = guards
44 self.exec_opts = exec_opts
45 self.errfmt = errfmt
46
47 def Run(self, cmd_val):
48 # type: (cmd_value.Argv) -> int
49 _, arg_r = flag_util.ParseCmdVal('source-guard', cmd_val)
50 name, _ = arg_r.ReadRequired2('requires a name')
51 #log('guards %s', self.guards)
52 if name in self.guards:
53 # already defined
54 if self.exec_opts.redefine_module():
55 self.errfmt.PrintMessage(
56 '(interactive) Reloading source file %r' % name)
57 return 0
58 else:
59 return 1
60 self.guards[name] = True
61 return 0
62
63
64class ModuleInvoke(vm._Builtin):
65 """
66 This is a builtin for the __invoke__ method of Obj my-module
67
68 use my-module.ysh
69 my-module my-proc
70 """
71
72 def __init__(self, cmd_ev, tracer, errfmt):
73 # type: (cmd_eval.CommandEvaluator, dev.Tracer, ui.ErrorFormatter) -> None
74 self.cmd_ev = cmd_ev
75 self.tracer = tracer
76 self.errfmt = errfmt
77
78 def Run(self, cmd_val):
79 # type: (cmd_value.Argv) -> int
80
81 arg_r = args.Reader(cmd_val.argv, locs=cmd_val.arg_locs)
82 arg_r.Next() # move past the module name
83
84 invokable_name, invokable_loc = arg_r.Peek2()
85 if invokable_name is None:
86 raise error.Usage(
87 'module must be invoked with a proc name argument',
88 cmd_val.arg_locs[0])
89
90 argv, locs = arg_r.Rest2() # include proc name
91
92 self_obj = cmd_val.self_obj
93 assert self_obj is not None # wouldn't have been called
94
95 val = self_obj.d.get(invokable_name)
96
97 #log('invokable_name %r', invokable_name)
98 #log('argv %r', argv)
99
100 # Similar to Procs::GetInvokable() - Proc or Obj
101
102 if val is not None:
103 # OK this is a proc 'log', so we found self, so now just invoke it
104 # with the args. No self obj!
105 cmd_val2 = cmd_value.Argv(argv, locs, cmd_val.is_last_cmd, None,
106 cmd_val.proc_args)
107
108 if val.tag() == value_e.Proc:
109 proc = cast(value.Proc, val)
110 #log('proc %r', proc.name)
111
112 with dev.ctx_Tracer(self.tracer, 'module-invoke',
113 cmd_val.argv):
114 status = self.cmd_ev.RunProc(proc, cmd_val2)
115 return status
116
117 # The module itself is an invokable Obj, but it also CONTAINS an
118 # invokable Obj
119 proc_val, self_obj2 = state.ValueIsInvokableObj(val)
120 cmd_val2.self_obj = self_obj2
121 if proc_val:
122 if proc_val.tag() != value_e.Proc:
123 # Technically we can run it like this, but I don't see a
124 # use case. It seems confusing.
125 #return self.cmd_ev.shell_ex.RunBuiltinProc(proc_val.builtin, cmd_val2)
126
127 raise error.TypeErr(
128 proc_val,
129 "__invoke__ on %r should be a user-defined Proc" %
130 invokable_name, invokable_loc)
131 proc = cast(value.Proc, proc_val)
132
133 with dev.ctx_Tracer(self.tracer, 'module-invoke',
134 cmd_val.argv):
135 status = self.cmd_ev.RunProc(proc, cmd_val2)
136 return status
137
138 # Any other type of value
139 raise error.Usage(
140 "module doesn't contain invokable %r" % invokable_name,
141 invokable_loc)