Skip to content

Commit e2479c4

Browse files
committed
yaml loading: avoid datetime objects
1 parent 49d107b commit e2479c4

File tree

11 files changed

+63
-27
lines changed

11 files changed

+63
-27
lines changed

Makefile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ COVBASE=coverage run --append
3333

3434
# Updating the Major & Minor version below?
3535
# Don't forget to update setup.py as well
36-
VERSION=8.1.$(shell date +%Y%m%d%H%M%S --utc --date=`git log --first-parent \
36+
VERSION=8.2.$(shell date +%Y%m%d%H%M%S --utc --date=`git log --first-parent \
3737
--max-count=1 --format=format:%cI`)
3838

3939
## all : default task
@@ -70,7 +70,8 @@ docs: FORCE
7070

7171
## clean : clean up all temporary / machine-generated files
7272
clean: FORCE
73-
rm -f ${MODILE}/*.pyc tests/*.pyc
73+
rm -rf ${MODULE}/__pycache__ ${MODULE}/tests/__pycache__
74+
rm -f *.so ${MODULE}/*.so ${MODULE}/tests/*.so ${MODULE}/avro/*.so
7475
python setup.py clean --all || true
7576
rm -Rf .coverage
7677
rm -f diff-cover.html

schema_salad/metaschema.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@
2525
from urllib.request import pathname2url
2626

2727
from ruamel.yaml.comments import CommentedMap
28-
from ruamel.yaml.main import YAML
2928

3029
from schema_salad.exceptions import SchemaSaladException, ValidationException
3130
from schema_salad.fetcher import DefaultFetcher, Fetcher
3231
from schema_salad.sourceline import SourceLine, add_lc_filename
32+
from schema_salad.utils import yaml_no_ts # requires schema-salad v8.2+
3333

3434
_vocab = {} # type: Dict[str, str]
3535
_rvocab = {} # type: Dict[str, str]
@@ -561,8 +561,7 @@ def _document_load_by_url(loader, url, loadingOptions):
561561
else:
562562
textIO = StringIO(text)
563563
textIO.name = str(url)
564-
yaml = YAML()
565-
yaml.preserve_quotes = True # type: ignore
564+
yaml = yaml_no_ts()
566565
result = yaml.load(textIO)
567566
add_lc_filename(result, url)
568567

@@ -2866,8 +2865,7 @@ def load_document(doc, baseuri=None, loadingOptions=None):
28662865

28672866
def load_document_by_string(string, uri, loadingOptions=None):
28682867
# type: (Any, str, Optional[LoadingOptions]) -> Any
2869-
yaml = YAML()
2870-
yaml.preserve_quotes = True # type: ignore
2868+
yaml = yaml_no_ts()
28712869
result = yaml.load(string)
28722870
add_lc_filename(result, uri)
28732871

schema_salad/python_codegen.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -541,8 +541,7 @@ def load_document(doc, baseuri=None, loadingOptions=None):
541541
542542
def load_document_by_string(string, uri, loadingOptions=None):
543543
# type: (Any, str, Optional[LoadingOptions]) -> Any
544-
yaml = YAML()
545-
yaml.preserve_quotes = True # type: ignore
544+
yaml = yaml_no_ts()
546545
result = yaml.load(string)
547546
add_lc_filename(result, uri)
548547

schema_salad/python_codegen_support.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@
2121
from urllib.request import pathname2url
2222

2323
from ruamel.yaml.comments import CommentedMap
24-
from ruamel.yaml.main import YAML
2524

2625
from schema_salad.exceptions import SchemaSaladException, ValidationException
2726
from schema_salad.fetcher import DefaultFetcher, Fetcher
2827
from schema_salad.sourceline import SourceLine, add_lc_filename
28+
from schema_salad.utils import yaml_no_ts # requires schema-salad v8.2+
2929

3030
_vocab = {} # type: Dict[str, str]
3131
_rvocab = {} # type: Dict[str, str]
@@ -557,8 +557,7 @@ def _document_load_by_url(loader, url, loadingOptions):
557557
else:
558558
textIO = StringIO(text)
559559
textIO.name = str(url)
560-
yaml = YAML()
561-
yaml.preserve_quotes = True # type: ignore
560+
yaml = yaml_no_ts()
562561
result = yaml.load(textIO)
563562
add_lc_filename(result, url)
564563

schema_salad/ref_resolver.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
from rdflib.plugins.parsers.notation3 import BadSyntax
2929
from ruamel.yaml.comments import CommentedMap, CommentedSeq, LineCol
3030
from ruamel.yaml.error import MarkedYAMLError
31-
from ruamel.yaml.main import YAML
3231

3332
from .exceptions import SchemaSaladException, ValidationException
3433
from .fetcher import DefaultFetcher
@@ -44,6 +43,7 @@
4443
ResolveType,
4544
aslist,
4645
onWindows,
46+
yaml_no_ts,
4747
)
4848

4949
_logger = logging.getLogger("salad")
@@ -984,8 +984,7 @@ def fetch(
984984
else:
985985
textIO = StringIO(text)
986986
textIO.name = str(url)
987-
yaml = YAML()
988-
yaml.preserve_quotes = True # type: ignore
987+
yaml = yaml_no_ts()
989988
attachments = yaml.load_all(textIO)
990989
result = cast(Union[CommentedSeq, CommentedMap], next(attachments))
991990

schema_salad/schema.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121

2222
from pkg_resources import resource_stream
2323
from ruamel.yaml.comments import CommentedMap, CommentedSeq
24-
from ruamel.yaml.main import YAML
2524

2625
from schema_salad.utils import (
2726
CacheType,
@@ -31,6 +30,7 @@
3130
convert_to_dict,
3231
flatten,
3332
json_dumps,
33+
yaml_no_ts,
3434
)
3535

3636
from . import _logger, jsonld_context, ref_resolver, validate
@@ -183,7 +183,7 @@ def get_metaschema() -> Tuple[Names, List[Dict[str, str]], Loader]:
183183
with resource_stream("schema_salad", "metaschema/metaschema.yml") as stream:
184184
loader.cache["https://w3id.org/cwl/salad"] = stream.read().decode("UTF-8")
185185

186-
yaml = YAML()
186+
yaml = yaml_no_ts()
187187
j = yaml.load(loader.cache["https://w3id.org/cwl/salad"])
188188
add_lc_filename(j, "metaschema.yml")
189189
j2 = loader.resolve_all(j, saladp)[0]

schema_salad/tests/test_cg.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
from typing import Any
44

55
import pytest
6-
from ruamel.yaml.main import YAML
76

87
import schema_salad.metaschema as cg_metaschema
98
from schema_salad.exceptions import ValidationException
109
from schema_salad.ref_resolver import file_uri
10+
from schema_salad.utils import yaml_no_ts
1111

1212
from .matcher import JsonDiffMatcher
1313
from .util import get_data
@@ -203,8 +203,7 @@ def test_load_by_yaml_metaschema(metaschema_pre: Any) -> None:
203203
path = get_data("metaschema/metaschema.yml")
204204
assert path
205205
with open(path) as path_handle:
206-
yaml = YAML()
207-
yaml.preserve_quotes = True # type: ignore
206+
yaml = yaml_no_ts()
208207
yaml_doc = yaml.load(path_handle)
209208
doc = cg_metaschema.load_document_by_yaml(
210209
yaml_doc,

schema_salad/tests/test_examples.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
"""Test examples."""
22
import datetime
33
import os
4-
from typing import cast
4+
from io import StringIO
5+
from typing import cast, Any, Dict
56

67
from pytest import CaptureFixture
78
from ruamel.yaml.comments import CommentedMap, CommentedSeq
8-
from ruamel.yaml.main import YAML
99

1010
import schema_salad.main
1111
import schema_salad.schema
1212
from schema_salad.jsonld_context import makerdf
1313
from schema_salad.ref_resolver import Loader, file_uri, uri_file_path
1414
from schema_salad.sourceline import SourceLine, cmap
15-
from schema_salad.utils import ContextType, stdout
15+
from schema_salad.utils import ContextType, stdout, yaml_no_ts
1616

1717
from .util import get_data
1818

@@ -278,7 +278,7 @@ def test_examples() -> None:
278278
ldr, _, _, _ = schema_salad.schema.load_schema(path)
279279
path2 = get_data(f"metaschema/{a}_src.yml")
280280
assert path2
281-
yaml = YAML()
281+
yaml = yaml_no_ts()
282282
with open(path2) as src_fp:
283283
src = ldr.resolve_all(yaml.load(src_fp), "", checklinks=False)[0]
284284
path3 = get_data(f"metaschema/{a}_proc.yml")
@@ -289,7 +289,7 @@ def test_examples() -> None:
289289

290290

291291
def test_yaml_float_test() -> None:
292-
assert YAML().load("float-test: 2e-10")["float-test"] == 2e-10
292+
assert yaml_no_ts().load("float-test: 2e-10")["float-test"] == 2e-10
293293

294294

295295
def test_typedsl_ref() -> None:
@@ -425,6 +425,21 @@ def test_rdf_datetime() -> None:
425425
g2.serialize(destination=stdout(), format="n3")
426426

427427

428+
def test_yaml_datetime() -> None:
429+
"""Affirm that yaml_no_ts prevents the creation of datetime objects."""
430+
example: Dict[str, Any] = {
431+
"id": "foo",
432+
"bar": {"id": "baz"},
433+
}
434+
example["s:dateCreated"] = datetime.datetime(2020, 10, 8)
435+
yaml = yaml_no_ts()
436+
stream = StringIO()
437+
yaml.dump(example, stream)
438+
stream2 = StringIO(stream.getvalue())
439+
example2 = yaml.load(stream2)
440+
assert isinstance(example2["s:dateCreated"], str)
441+
442+
428443
def test_subscoped_id() -> None:
429444
ldr = Loader({})
430445
ctx = {

schema_salad/utils.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import requests
2020
from rdflib.graph import Graph
2121
from ruamel.yaml.comments import CommentedMap, CommentedSeq
22+
from ruamel.yaml.constructor import RoundTripConstructor
23+
from ruamel.yaml.main import YAML
2224

2325
if TYPE_CHECKING:
2426
from .fetcher import Fetcher
@@ -115,3 +117,26 @@ def json_dumps(
115117
def stdout() -> BufferedWriter:
116118
"""Build a replacement for sys.stdout that allow for writing binary data."""
117119
return os.fdopen(sys.stdout.fileno(), "wb", closefd=False)
120+
121+
122+
class _RoundTripNoTimeStampConstructor(RoundTripConstructor):
123+
def construct_yaml_timestamp(self: Any, node: Any, values: Any = None) -> Any:
124+
return node.value
125+
126+
127+
_RoundTripNoTimeStampConstructor.add_constructor(
128+
u"tag:yaml.org,2002:timestamp",
129+
_RoundTripNoTimeStampConstructor.construct_yaml_timestamp,
130+
)
131+
132+
133+
def yaml_no_ts() -> YAML:
134+
"""
135+
Get a YAML loader that won't parse timestamps into datetime objects.
136+
137+
Such datetime objects can't be easily dumped into JSON.
138+
"""
139+
yaml = YAML(typ="rt")
140+
yaml.preserve_quotes = True # type: ignore
141+
yaml.Constructor = _RoundTripNoTimeStampConstructor
142+
return yaml

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@
8989

9090
setup(
9191
name="schema-salad",
92-
version="8.1", # update the VERSION prefix in the Makefile as well 🙂
92+
version="8.2", # update the VERSION prefix in the Makefile as well 🙂
9393
description="Schema Annotations for Linked Avro Data (SALAD)",
9494
long_description=open(README).read(),
9595
long_description_content_type="text/x-rst",

0 commit comments

Comments
 (0)