Skip to content

Commit c0d40eb

Browse files
committed
feature: support modify or remove values
1 parent 596a878 commit c0d40eb

File tree

1 file changed

+60
-3
lines changed

1 file changed

+60
-3
lines changed

jsonfmt.py

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import json
55
import pyperclip
6+
import re
67
import toml
78
import yaml
89
from argparse import ArgumentParser
@@ -15,11 +16,14 @@
1516
from pygments.lexers import JsonLexer, TOMLLexer, YamlLexer
1617
from shutil import get_terminal_size
1718
from sys import stdin, stdout, stderr
18-
from typing import Any, IO, Optional, Sequence
19+
from typing import Any, List, IO, Optional, Sequence, Union
1920
from unittest.mock import patch
2021

2122
__version__ = '0.2.4'
2223

24+
NUMERIC = re.compile(r'-?\d+$|-?\d+\.\d+$|^-?\d+\.?\d+e-?\d+$')
25+
DICT_OR_LIST = re.compile(r'^\{.*\}$|^\[.*\]$')
26+
2327

2428
def print_inf(msg: Any):
2529
print(f'\033[1;94mjsonfmt:\033[0m \033[0;94m{msg}\033[0m', file=stdout)
@@ -69,6 +73,47 @@ def parse_to_pyobj(text: str, jpath: Optional[str]) -> tuple[Any, str]:
6973
return subelements, fmt
7074

7175

76+
def forward_by_keys(py_obj: Any, keys: str) -> tuple[Any, Union[str, int]]:
77+
next_k = lambda obj, k: int(k) if isinstance(obj, list) else k
78+
79+
_keys = keys.replace(']', '').replace('[', '.').split('.')
80+
for k in _keys[:-1]:
81+
py_obj = py_obj[next_k(py_obj, k)]
82+
else:
83+
return py_obj, next_k(py_obj, _keys[-1])
84+
85+
86+
def load_value(value: str):
87+
if NUMERIC.match(value):
88+
return eval(value)
89+
elif DICT_OR_LIST.match(value):
90+
try:
91+
return eval(value)
92+
except Exception:
93+
return value
94+
else:
95+
return value
96+
97+
98+
def modify_pyobj(py_obj: Any, sets: List[str], pops: List[str]):
99+
for kv in sets:
100+
try:
101+
keys, value = kv.split('=')
102+
bottom, last_k = forward_by_keys(py_obj, keys)
103+
bottom[last_k] = load_value(value)
104+
except (IndexError, KeyError, ValueError):
105+
print_err(f'invalid key path: {kv}')
106+
continue
107+
108+
for keys in pops:
109+
try:
110+
bottom, last_k = forward_by_keys(py_obj, keys)
111+
bottom.pop(last_k)
112+
except (IndexError, KeyError):
113+
print_err(f'invalid key path: {keys}')
114+
continue
115+
116+
72117
def get_overview(py_obj: Any):
73118
def clip_value(value: Any):
74119
if isinstance(value, str):
@@ -136,11 +181,17 @@ def output(output_fp: IO, text: str, fmt: str, cp2clip: bool):
136181

137182
def process(input_fp: IO, jpath: Optional[str], to_format: Optional[str], *,
138183
compact: bool, cp2clip: bool, escape: bool, indent: int,
139-
overview: bool, overwrite: bool, sort_keys: bool):
184+
overview: bool, overwrite: bool, sort_keys: bool,
185+
sets: Optional[list], pops: Optional[list]):
140186
# parse and format
141187
input_text = input_fp.read()
142188
py_obj, fmt = parse_to_pyobj(input_text, jpath)
143189

190+
if sets or pops:
191+
sets = sets or []
192+
pops = pops or []
193+
modify_pyobj(py_obj, sets, pops)
194+
144195
if overview:
145196
py_obj = get_overview(py_obj)
146197

@@ -178,6 +229,10 @@ def parse_cmdline_args(args: Optional[Sequence[str]] = None):
178229
help='output part of the object via jsonpath')
179230
parser.add_argument('-s', dest='sort_keys', action='store_true',
180231
help='sort keys of objects on output')
232+
parser.add_argument('--set', nargs='*', metavar="foo.k1=v1 k2[i]=v2",
233+
help='set the keys to values')
234+
parser.add_argument('--pop', nargs='*', metavar="k1 foo.k2 k3[i]",
235+
help='pop the specified keys')
181236
parser.add_argument(dest='files', nargs='*',
182237
help='the files that will be processed')
183238
parser.add_argument('-v', dest='version', action='version',
@@ -208,7 +263,9 @@ def main():
208263
indent=args.indent,
209264
overview=args.overview,
210265
overwrite=args.overwrite,
211-
sort_keys=args.sort_keys)
266+
sort_keys=args.sort_keys,
267+
sets=args.set,
268+
pops=args.pop)
212269
except ParseError as err:
213270
print_err(err)
214271
except FileNotFoundError:

0 commit comments

Comments
 (0)