From 1fa53636429d4be3f24a933c47592ed48dd6bc08 Mon Sep 17 00:00:00 2001 From: xi Date: Tue, 21 Aug 2007 20:25:34 +0000 Subject: [PATCH] Make compose() and load() ensure that the input stream contains a single document. Fixes #54. git-svn-id: http://svn.pyyaml.org/pyyaml/trunk@258 18f92427-320e-0410-9341-c67f048884a3 --- ext/_yaml.pyx | 18 ++++++++++++++++ lib/yaml/__init__.py | 26 +++++++++++------------- lib/yaml/composer.py | 21 +++++++++++++++++++ lib/yaml/constructor.py | 7 +++++++ tests/data/empty-documents.single-loader-error | 2 ++ tests/data/explicit-document.single-loader-error | 4 ++++ tests/data/implicit-document.single-loader-error | 3 +++ tests/test_errors.py | 18 ++++++++++++++++ 8 files changed, 85 insertions(+), 14 deletions(-) create mode 100644 tests/data/empty-documents.single-loader-error create mode 100644 tests/data/explicit-document.single-loader-error create mode 100644 tests/data/implicit-document.single-loader-error diff --git a/ext/_yaml.pyx b/ext/_yaml.pyx index ac8f803..76b307a 100644 --- a/ext/_yaml.pyx +++ b/ext/_yaml.pyx @@ -663,6 +663,24 @@ cdef class CParser: if self.parsed_event.type != YAML_STREAM_END_EVENT: return self._compose_document() + def get_single_node(self): + self._parse_next_event() + yaml_event_delete(&self.parsed_event) + self._parse_next_event() + document = None + if self.parsed_event.type != YAML_STREAM_END_EVENT: + document = self._compose_document() + self._parse_next_event() + if self.parsed_event.type != YAML_STREAM_END_EVENT: + mark = Mark(self.stream_name, + self.parsed_event.start_mark.index, + self.parsed_event.start_mark.line, + self.parsed_event.start_mark.column, + None, None) + raise ComposerError("expected a single document in the stream", + document.start_mark, "but found another document", mark) + return document + cdef object _compose_document(self): yaml_event_delete(&self.parsed_event) node = self._compose_node(None, None) diff --git a/lib/yaml/__init__.py b/lib/yaml/__init__.py index bd233a8..e131795 100644 --- a/lib/yaml/__init__.py +++ b/lib/yaml/__init__.py @@ -35,8 +35,7 @@ def compose(stream, Loader=Loader): and produce the corresponding representation tree. """ loader = Loader(stream) - if loader.check_node(): - return loader.get_node() + return loader.get_single_node() def compose_all(stream, Loader=Loader): """ @@ -47,6 +46,14 @@ def compose_all(stream, Loader=Loader): while loader.check_node(): yield loader.get_node() +def load(stream, Loader=Loader): + """ + Parse the first YAML document in a stream + and produce the corresponding Python object. + """ + loader = Loader(stream) + return loader.get_single_data() + def load_all(stream, Loader=Loader): """ Parse all YAML documents in a stream @@ -56,14 +63,13 @@ def load_all(stream, Loader=Loader): while loader.check_data(): yield loader.get_data() -def load(stream, Loader=Loader): +def safe_load(stream): """ Parse the first YAML document in a stream and produce the corresponding Python object. + Resolve only basic YAML tags. """ - loader = Loader(stream) - if loader.check_data(): - return loader.get_data() + return load(stream, SafeLoader) def safe_load_all(stream): """ @@ -73,14 +79,6 @@ def safe_load_all(stream): """ return load_all(stream, SafeLoader) -def safe_load(stream): - """ - Parse the first YAML document in a stream - and produce the corresponding Python object. - Resolve only basic YAML tags. - """ - return load(stream, SafeLoader) - def emit(events, stream=None, Dumper=Dumper, canonical=None, indent=None, width=None, allow_unicode=None, line_break=None): diff --git a/lib/yaml/composer.py b/lib/yaml/composer.py index 9f5cd87..06e5ac7 100644 --- a/lib/yaml/composer.py +++ b/lib/yaml/composer.py @@ -26,6 +26,27 @@ class Composer(object): if not self.check_event(StreamEndEvent): return self.compose_document() + def get_single_node(self): + # Drop the STREAM-START event. + self.get_event() + + # Compose a document if the stream is not empty. + document = None + if not self.check_event(StreamEndEvent): + document = self.compose_document() + + # Ensure that the stream contains no more documents. + if not self.check_event(StreamEndEvent): + event = self.get_event() + raise ComposerError("expected a single document in the stream", + document.start_mark, "but found another document", + event.start_mark) + + # Drop the STREAM-END event. + self.get_event() + + return document + def compose_document(self): # Drop the DOCUMENT-START event. self.get_event() diff --git a/lib/yaml/constructor.py b/lib/yaml/constructor.py index a1295c8..cc68af1 100644 --- a/lib/yaml/constructor.py +++ b/lib/yaml/constructor.py @@ -37,6 +37,13 @@ class BaseConstructor(object): if self.check_node(): return self.construct_document(self.get_node()) + def get_single_data(self): + # Ensure that the stream contains a single document and construct it. + node = self.get_single_node() + if node is not None: + return self.construct_document(node) + return None + def construct_document(self, node): data = self.construct_object(node) while self.state_generators: diff --git a/tests/data/empty-documents.single-loader-error b/tests/data/empty-documents.single-loader-error new file mode 100644 index 0000000..f8dba8d --- /dev/null +++ b/tests/data/empty-documents.single-loader-error @@ -0,0 +1,2 @@ +--- # first document +--- # second document diff --git a/tests/data/explicit-document.single-loader-error b/tests/data/explicit-document.single-loader-error new file mode 100644 index 0000000..46c6f8b --- /dev/null +++ b/tests/data/explicit-document.single-loader-error @@ -0,0 +1,4 @@ +--- +foo: bar +--- +foo: bar diff --git a/tests/data/implicit-document.single-loader-error b/tests/data/implicit-document.single-loader-error new file mode 100644 index 0000000..f8c9a5c --- /dev/null +++ b/tests/data/implicit-document.single-loader-error @@ -0,0 +1,3 @@ +foo: bar +--- +foo: bar diff --git a/tests/test_errors.py b/tests/test_errors.py index 2a49660..a678f2b 100644 --- a/tests/test_errors.py +++ b/tests/test_errors.py @@ -16,6 +16,10 @@ class TestErrors(test_appliance.TestAppliance): #self._load_string(invalid_filename) self.failUnlessRaises(YAMLError, lambda: self._load_string(invalid_filename)) + def _testLoaderSingleErrors(self, test_name, invalid_filename): + self._load_single(invalid_filename) + self.failUnlessRaises(YAMLError, lambda: self._load_single(invalid_filename)) + def _testEmitterErrors(self, test_name, invalid_filename): events = list(load(file(invalid_filename, 'rb').read(), Loader=test_emitter.EventsLoader)) @@ -66,8 +70,22 @@ class TestErrors(test_appliance.TestAppliance): #print "%s:" % exc.__class__.__name__, exc raise + def _load_single(self, filename): + try: + return load(file(filename, 'rb').read()) + except YAMLError, exc: + #except ScannerError, exc: + #except ParserError, exc: + #except ComposerError, exc: + #except ConstructorError, exc: + #print '.'*70 + #print "%s:" % filename + #print "%s:" % exc.__class__.__name__, exc + raise + TestErrors.add_tests('testLoaderErrors', '.loader-error') TestErrors.add_tests('testLoaderStringErrors', '.loader-error') +TestErrors.add_tests('testLoaderSingleErrors', '.single-loader-error') TestErrors.add_tests('testEmitterErrors', '.emitter-error') TestErrors.add_tests('testDumperErrors', '.dumper-error') -- 2.11.4.GIT