Repository: python-tap/tappy

Issue:
Additional newlines added in YAML blocks for multi-line strings
Hello!

I've observed that `_extract_yaml_block()` performs `"\n".join(raw_yaml)` without having stripped the newline character in each possible line, resulting in unwanted empty lines when parsing multi-line strings in YAML blocks.

I'll give an example. With this file:

```python
#!/usr/bin/python3

import tap.parser

tap_input = """TAP version 13
1..2
ok 1 A passing test
   ---
   message: test
   severity: fail
   output: |+
     this is a multiline
     string but with tap
     somehow blank lines
     are added to it.
"""
parser = tap.parser.Parser()

for i, l in enumerate(parser.parse_text(tap_input)):
    if i == 2:
        print("{!r}".format(l.yaml_block["output"].splitlines()))
```

The output is:

```
['', 'this is a multiline', '', 'string but with tap', '', 'somehow blank lines', '', 'are added to it.']
```

Where as I expected:

```
['this is a multiline', 'string but with tap', 'somehow blank lines', 'are added to it.']
```

I will submit a PR shortly.

Comments:

Patch:
diff --git a/AUTHORS b/AUTHORS
index e87e3b8..3735496 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -3,6 +3,7 @@ tappy was originally created by Matt Layman.
 Contributors
 ------------
 
+* Adeodato Simó
 * Andrew McNamara
 * Chris Clarke
 * Erik Cederstrand
diff --git a/docs/releases.rst b/docs/releases.rst
index dd80f97..7f708c7 100644
--- a/docs/releases.rst
+++ b/docs/releases.rst
@@ -5,6 +5,7 @@ Version 3.1, To Be Released
 ---------------------------
 
 * Add support for Python 3.8.
+* Fix parsing of multi-line strings in YAML blocks (#111)
 
 Version 3.0, Released January 10, 2020
 --------------------------------------
diff --git a/tap/parser.py b/tap/parser.py
index 637436b..2f28bd8 100644
--- a/tap/parser.py
+++ b/tap/parser.py
@@ -181,7 +181,8 @@ WARNING: Optional imports not found, TAP 13 output will be
         try:
             next(fh)
             while indent_match.match(fh.peek()):
-                raw_yaml.append(next(fh).replace(indent, "", 1))
+                yaml_line = next(fh).replace(indent, "", 1)
+                raw_yaml.append(yaml_line.rstrip("\n"))
                 # check for the end and stop adding yaml if encountered
                 if self.yaml_block_end.match(fh.peek()):
                     next(fh)


Test patch:
diff --git a/tap/tests/test_parser.py b/tap/tests/test_parser.py
index 431297a..ac8f3d3 100644
--- a/tap/tests/test_parser.py
+++ b/tap/tests/test_parser.py
@@ -348,7 +348,12 @@ class TestParser(unittest.TestCase):
                  got:
                    - foo
                  expect:
-                   - bar"""
+                   - bar
+               output: |-
+                 a multiline string
+                 must be handled properly
+                 even with | pipes
+                 | here > and: there"""
         )
         parser = Parser()
         lines = []
@@ -358,20 +363,21 @@ class TestParser(unittest.TestCase):
 
         if have_yaml:
             converted_yaml = yaml.safe_load(
-                u"""
+                u'''
                message: test
                severity: fail
                data:
                  got:
                    - foo
                  expect:
-                   - bar"""
+                   - bar
+               output: "a multiline string\\nmust be handled properly\\neven with | pipes\\n| here > and: there"'''
             )
             self.assertEqual(3, len(lines))
             self.assertEqual(13, lines[0].version)
             self.assertEqual(converted_yaml, lines[2].yaml_block)
         else:
-            self.assertEqual(11, len(lines))
+            self.assertEqual(16, len(lines))
             self.assertEqual(13, lines[0].version)
             for l in list(range(3, 11)):
                 self.assertEqual("unknown", lines[l].category)