OILS
/
ysh
/
builtin_json.py
1 |
from __future__ import print_function
|
2 |
|
3 |
from _devbuild.gen import arg_types
|
4 |
from _devbuild.gen.runtime_asdl import scope_e, cmd_value
|
5 |
from _devbuild.gen.syntax_asdl import loc
|
6 |
from core import error
|
7 |
from core.error import e_usage
|
8 |
from core import pyos
|
9 |
from core import state
|
10 |
from core import vm
|
11 |
from data_lang import j8
|
12 |
from frontend import flag_spec
|
13 |
from frontend import args
|
14 |
from frontend import location
|
15 |
from frontend import match
|
16 |
from frontend import typed_args
|
17 |
from mycpp import mylib
|
18 |
from osh import builtin_misc
|
19 |
from ysh import cpython
|
20 |
|
21 |
import sys
|
22 |
import yajl
|
23 |
import posix_ as posix
|
24 |
|
25 |
from typing import TYPE_CHECKING
|
26 |
if TYPE_CHECKING:
|
27 |
from core.ui import ErrorFormatter
|
28 |
from ysh import expr_eval
|
29 |
|
30 |
_JSON_ACTION_ERROR = "builtin expects 'read' or 'write'"
|
31 |
|
32 |
|
33 |
class Json(vm._Builtin):
|
34 |
"""JSON read and write.
|
35 |
|
36 |
--pretty=0 writes it on a single line
|
37 |
--indent=2 controls multiline indentation
|
38 |
"""
|
39 |
|
40 |
def __init__(self, mem, expr_ev, errfmt, is_j8):
|
41 |
# type: (state.Mem, expr_eval.ExprEvaluator, ErrorFormatter, bool) -> None
|
42 |
self.mem = mem
|
43 |
self.expr_ev = expr_ev
|
44 |
self.errfmt = errfmt
|
45 |
if is_j8:
|
46 |
self.printer = j8.Printer(0)
|
47 |
else:
|
48 |
# TODO: restrict to JSON with some flags
|
49 |
self.printer = j8.Printer(0)
|
50 |
|
51 |
def Run(self, cmd_val):
|
52 |
# type: (cmd_value.Argv) -> int
|
53 |
arg_r = args.Reader(cmd_val.argv, locs=cmd_val.arg_locs)
|
54 |
arg_r.Next() # skip 'json'
|
55 |
|
56 |
action, action_loc = arg_r.Peek2()
|
57 |
if action is None:
|
58 |
raise error.Usage(_JSON_ACTION_ERROR, loc.Missing)
|
59 |
arg_r.Next()
|
60 |
|
61 |
if action == 'write':
|
62 |
# NOTE slightly different flags
|
63 |
# json write --surrogate-ok $'\udc00'
|
64 |
# not valid for j8 write
|
65 |
attrs = flag_spec.Parse('json_write', arg_r)
|
66 |
|
67 |
arg_jw = arg_types.json_write(attrs.attrs)
|
68 |
|
69 |
if not arg_r.AtEnd():
|
70 |
e_usage('write got too many args', arg_r.Location())
|
71 |
|
72 |
expr = typed_args.RequiredExpr(cmd_val.typed_args)
|
73 |
val = self.expr_ev.EvalExpr(expr, loc.Missing)
|
74 |
|
75 |
if arg_jw.pretty:
|
76 |
indent = arg_jw.indent
|
77 |
extra_newline = False
|
78 |
else:
|
79 |
# How yajl works: if indent is -1, then everything is on one line.
|
80 |
indent = -1
|
81 |
extra_newline = True
|
82 |
|
83 |
if 0:
|
84 |
buf = mylib.BufWriter()
|
85 |
self.printer.Print(val, buf, indent=indent)
|
86 |
sys.stdout.write(buf.getvalue())
|
87 |
sys.stdout.write('\n')
|
88 |
else:
|
89 |
|
90 |
obj = cpython._ValueToPyObj(val)
|
91 |
|
92 |
j = yajl.dumps(obj, indent=indent)
|
93 |
sys.stdout.write(j)
|
94 |
if extra_newline:
|
95 |
sys.stdout.write('\n')
|
96 |
|
97 |
elif action == 'read':
|
98 |
attrs = flag_spec.Parse('json_read', arg_r)
|
99 |
arg_jr = arg_types.json_read(attrs.attrs)
|
100 |
# TODO:
|
101 |
# Respect -validate=F
|
102 |
|
103 |
var_name, name_loc = arg_r.ReadRequired2("expected variable name")
|
104 |
if var_name.startswith(':'):
|
105 |
var_name = var_name[1:]
|
106 |
|
107 |
if not arg_r.AtEnd():
|
108 |
e_usage('read got too many args', arg_r.Location())
|
109 |
|
110 |
if not match.IsValidVarName(var_name):
|
111 |
raise error.Usage('got invalid variable name %r' % var_name,
|
112 |
name_loc)
|
113 |
|
114 |
try:
|
115 |
contents = builtin_misc.ReadAll()
|
116 |
except pyos.ReadError as e: # different paths for read -d, etc.
|
117 |
# don't quote code since YSH errexit will likely quote
|
118 |
self.errfmt.PrintMessage("read error: %s" %
|
119 |
posix.strerror(e.err_num))
|
120 |
return 1
|
121 |
|
122 |
if mylib.PYTHON:
|
123 |
try:
|
124 |
obj = yajl.loads(contents)
|
125 |
except ValueError as e:
|
126 |
self.errfmt.Print_('json read: %s' % e, blame_loc=action_loc)
|
127 |
return 1
|
128 |
|
129 |
# TODO: use token directly
|
130 |
val = cpython._PyObjToValue(obj)
|
131 |
self.mem.SetValue(location.LName(var_name), val, scope_e.LocalOnly)
|
132 |
|
133 |
else:
|
134 |
raise error.Usage(_JSON_ACTION_ERROR, action_loc)
|
135 |
|
136 |
return 0
|