Skip to content

Commit 7972698

Browse files
committed
Update yason.py to use ruamel.yaml
This is to fix issues with conda-forda that now supports ruamel.yaml only (older libs deprecated). Also ran ruff on yason.py to find a few bugs and reformat.
1 parent 882b9bb commit 7972698

File tree

2 files changed

+92
-58
lines changed

2 files changed

+92
-58
lines changed

scripts/environment.yml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,14 @@ dependencies:
1414
- python=3
1515
- pandoc=2
1616
- pypandoc=1
17-
- beautifulsoup4=4.7
18-
- ruamel.yaml=0.15
19-
- scipy=1.2
20-
- pygments=2.4
17+
- beautifulsoup4=4
18+
- scipy=1
19+
- pygments=2
2120
- jinja2=2.10
2221
- numpy=1.16
2322
- notebook=5
2423
- nbconvert=5
25-
- jupyter_contrib_nbextensions=0.5
24+
- jupyter_contrib_nbextensions
2625
- sdl2=2
2726
- jsonschema=3
2827
- ruamel.yaml

scripts/yason.py

Lines changed: 88 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
#!/usr/bin/env python
22

33
import sys
4+
45
if sys.version_info < (3, 0):
5-
sys.stdout.write("Sorry, Python 3 og higher required\n")
6+
sys.stderr.write("Sorry, Python 3 og higher required\n")
67
sys.exit(1)
78

89
import os
9-
import json, sys, argparse
10+
import io
11+
import json
12+
import sys
13+
import argparse
1014
import warnings
1115

1216
try:
@@ -23,23 +27,20 @@
2327
jinja2 = None
2428

2529

26-
# API compatibility between pyyaml and ruamel.yaml might break in the future
27-
# https://yaml.readthedocs.io/en/latest/api.html
2830
try:
2931
import ruamel.yaml as yaml
3032
except ImportError:
31-
import yaml
32-
from yaml import (
33-
safe_load as yaml_safe_load,
34-
safe_dump as yaml_safe_dump,
35-
)
33+
print("ruame.yaml is required", file=sys.stderr)
34+
sys.exit(1)
3635
else:
36+
3737
def yaml_safe_load(stream):
3838
return yaml.YAML(typ="safe").load(stream)
3939

4040
def yaml_safe_dump(data, stream=None, **kwargs):
4141
return yaml.YAML(typ="safe").dump(data, stream=stream, **kwargs)
4242

43+
4344
try:
4445
pygments = True
4546
from pygments import highlight
@@ -48,20 +49,32 @@ def yaml_safe_dump(data, stream=None, **kwargs):
4849
except ImportError:
4950
pygments = False
5051

51-
parser = argparse.ArgumentParser(description='Convert json to yaml and vice versa')
52-
parser.add_argument('--indent', '-i', dest='indent', default=4, type=int, help='text indentation')
53-
parser.add_argument('--json', '-j', dest='alwaysjson', action='store_true', help='always output to json')
54-
parser.add_argument('infile', nargs='?', type=argparse.FileType('r'), default=sys.stdin, help='json/yaml input')
52+
parser = argparse.ArgumentParser(description="Convert json to yaml and vice versa")
53+
parser.add_argument(
54+
"--indent", "-i", dest="indent", default=4, type=int, help="text indentation"
55+
)
56+
parser.add_argument(
57+
"--json", "-j", dest="alwaysjson", action="store_true", help="always output to json"
58+
)
59+
parser.add_argument(
60+
"infile",
61+
nargs="?",
62+
type=argparse.FileType("r"),
63+
default=sys.stdin,
64+
help="json/yaml input",
65+
)
5566

5667
if pygments:
57-
parser.add_argument('--color', '-c', dest='color', action='store_true', help='syntax colored output')
68+
parser.add_argument(
69+
"--color", "-c", dest="color", action="store_true", help="syntax colored output"
70+
)
5871

5972
args = parser.parse_args()
6073

6174
terminal_red = "\033[91m"
6275
terminal_yellow = "\033[93m"
6376
terminal_default = "\033[39m"
64-
77+
6578
if pygments:
6679
if args.color:
6780
formatter = Terminal256Formatter
@@ -70,90 +83,112 @@ def yaml_safe_dump(data, stream=None, **kwargs):
7083

7184

7285
def human_readable_path(path):
73-
out=str()
86+
out = str()
7487
for i in path:
7588
if not isinstance(i, int):
76-
out += str(i)+' -> '
89+
out += str(i) + " -> "
7790
return out
7891

92+
7993
def eprint(*args, **kwargs):
80-
''' print to stderr '''
94+
"""print to stderr"""
8195
print(*args, file=sys.stderr, **kwargs)
8296

8397

8498
def print_table(schema):
85-
''' pretty print schema as markdown table '''
99+
"""pretty print schema as markdown table"""
86100
properties = schema.get("properties", "")
87101
if isinstance(properties, dict):
88102
eprint(terminal_yellow + "Need help, my young apprentice?\n" + terminal_red)
89-
eprint("{:25} | {:7} | {:50}".format(25*"-", 7*"-", 50*"-"))
103+
eprint("{:25} | {:7} | {:50}".format(25 * "-", 7 * "-", 50 * "-"))
90104
eprint("{:25} | {:7} | {:50}".format("Property", "Type", "Description"))
91-
eprint("{:25} | {:7} | {:50}".format(25*"-", 7*"-", 50*"-"))
105+
eprint("{:25} | {:7} | {:50}".format(25 * "-", 7 * "-", 50 * "-"))
92106
for key, value in properties.items():
93107
required = key in schema.get("required", [""])
94108
_property = key + str("*" if required else "")
95109
if isinstance(value, dict):
96110
_description = value.get("description", "")
97111
if "type" in value:
98112
_default = value.get("default", "")
99-
if _default!="":
100-
_property = _property + '=' + str(_default)
113+
if _default != "":
114+
_property = _property + "=" + str(_default)
101115
_type = value["type"]
102116
if isinstance(_type, list):
103-
_type = '/'.join(_type)
104-
eprint("{:25} | {:7} | {}".format('`'+_property+'`', _type, _description))
117+
_type = "/".join(_type)
118+
eprint(
119+
"{:25} | {:7} | {}".format(
120+
"`" + _property + "`", _type, _description
121+
)
122+
)
105123
else:
106-
eprint("{:25} | {:7} | {}".format('`'+_property+'`', 'n/a', _description))
107-
eprint(terminal_default) # restore terminal color
124+
eprint(
125+
"{:25} | {:7} | {}".format(
126+
"`" + _property + "`", "n/a", _description
127+
)
128+
)
129+
eprint(terminal_default) # restore terminal color
130+
108131

109132
def validate_input(instance):
110-
''' JSON schema checker '''
133+
"""JSON schema checker"""
111134
if jsonschema:
112-
pathname = os.path.dirname(sys.argv[0]) # location of yason.py
113-
for subdir in ["/../docs", "/../share/faunus"]: # schema file can be in src or installed
114-
schemafile = os.path.abspath(pathname+subdir) + '/schema.yml'
135+
pathname = os.path.dirname(sys.argv[0]) # location of yason.py
136+
for subdir in [
137+
"/../docs",
138+
"/../share/faunus",
139+
]: # schema file can be in src or installed
140+
schemafile = os.path.abspath(pathname + subdir) + "/schema.yml"
115141
if os.path.exists(schemafile):
116142
with open(schemafile, "r") as f:
117143
_schema = yaml_safe_load(f)
118-
error = best_match(Draft201909Validator(_schema).iter_errors(instance))
119-
if error!=None:
120-
eprint( "{}{}\n".format(human_readable_path(error.path), error.message) )
144+
error = best_match(
145+
Draft201909Validator(_schema).iter_errors(instance)
146+
)
147+
if error is not None:
148+
eprint(
149+
"{}{}\n".format(
150+
human_readable_path(error.path), error.message
151+
)
152+
)
121153
print_table(error.schema)
122154
sys.exit(1)
123155
break
124156

157+
125158
try: # ... to read json
126-
i = args.infile.read()
159+
file_as_str = args.infile.read()
127160
if jinja2:
128161
# additional files can be used with {% include "file" %}
129162
dirs = [os.getcwd(), os.path.dirname(os.path.realpath(__file__)) + "/../top"]
130163
loader = jinja2.FileSystemLoader(dirs)
131164
env = jinja2.Environment(loader=loader)
132-
i = env.from_string(i).render() # render jinja2
133-
# i = jinja2.Template(i).render() # render jinja2
165+
file_as_str = env.from_string(file_as_str).render() # render jinja2
134166

135-
d = json.loads(i)
136-
if "mcloop" in d or "version" in d:
137-
validate_input(d)
167+
file_as_dict = json.loads(file_as_str)
168+
if "mcloop" in file_as_dict or "version" in file_as_dict:
169+
validate_input(file_as_dict)
138170
if args.alwaysjson:
139171
if pygments:
140-
i = highlight(out, JsonLexer(), formatter())
141-
print(i)
172+
file_as_str = highlight(file_as_str, JsonLexer(), formatter())
173+
print(file_as_str)
142174
else:
143-
out = yaml_safe_dump(d, indent=args.indent, allow_unicode=True)
175+
stream = io.StringIO()
176+
yaml_safe_dump(file_as_dict, stream=stream)
144177
if pygments:
145-
out = highlight(out, YamlLexer(), formatter())
146-
print(out)
178+
stream = highlight(stream.getvalue(), YamlLexer(), formatter())
179+
print(stream)
180+
else:
181+
print(stream.getvalue())
147182
except json.decoder.JSONDecodeError:
148183
try: # ... to read yaml
149-
d = yaml_safe_load(i) # plain load was deprecated in PyYAML
150-
if "mcloop" in d or "version" in d:
151-
validate_input(d)
152-
out = json.dumps(d, indent=args.indent)
184+
file_as_dict = yaml_safe_load(file_as_str)
185+
if "mcloop" in file_as_dict or "version" in file_as_dict:
186+
validate_input(file_as_dict)
187+
stream = json.dumps(file_as_dict, indent=args.indent)
153188
if pygments:
154-
out = highlight(out, JsonLexer(), formatter())
155-
print(out)
189+
stream = highlight(stream, JsonLexer(), formatter())
190+
print(stream)
156191
except yaml.parser.ParserError as exception:
157-
print("input error: invalid json or yaml format", file=sys.stderr)
158-
print(exception, file=sys.stderr)
192+
eprint("input error: invalid json or yaml format")
193+
eprint(exception)
159194
sys.exit(1)

0 commit comments

Comments
 (0)