Repository: slarse/labelbot

Issue:
Functions for parsing wanted labels from issue body and allowed labels from labels file
Labels should be defined in the issue body like so:

```
Blablabla

:label:`some label` :label:`some other label`
```

No artificial restrictions on where the labels are placed or how many there are. The function should only return **unique labels**, duplicates are discarded.

The labels in the `.allowed-labels` file should simply be newline separated labels. Lines starting with `#` are ignored.

Comments:

Patch:
diff --git a/labelbot/parse.py b/labelbot/parse.py
new file mode 100644
index 0000000..2c40500
--- /dev/null
+++ b/labelbot/parse.py
@@ -0,0 +1,41 @@
+"""Parse label information from issue and config files.
+
+.. module:: parse
+    :synopsis: Functions for extracting info from files.
+"""
+import re
+
+from typing import List
+
+COMMENT_CHAR = "#"
+# maybe need an OS independent match here
+ALLOWED_LABEL_SEP = "\n"
+
+
+def parse_wanted_labels(text: str) -> List[str]:
+    """Extract the labels defined by :label:`LABEL` tokens in a string.
+
+    Args:
+        text: Typically an issue body with label markup.
+    Returns:
+        A list of extracted labels.
+    """
+    label_pattern = ":label:`(.*?)`"
+    return re.findall(label_pattern, text, re.MULTILINE)
+
+
+def parse_allowed_labels(text: str) -> List[str]:
+    """Parse the allowd labels from the contents of a .allowed-labels file.
+    Each line constitutes a single label. Lines starting with a # sign are
+    ignored.
+
+    Args:
+        text: The contents of a .allowed-labels file.
+    Returns:
+        A list of defined labels
+    """
+    return [
+        stripped_line
+        for stripped_line in (line.strip() for line in text.split(ALLOWED_LABEL_SEP))
+        if not stripped_line.startswith(COMMENT_CHAR) and stripped_line
+    ]


Test patch:
diff --git a/tests/test_parse.py b/tests/test_parse.py
new file mode 100644
index 0000000..d6a185a
--- /dev/null
+++ b/tests/test_parse.py
@@ -0,0 +1,58 @@
+import pytest
+from labelbot import parse
+
+SINGLE_LABEL_ISSUE = """This is an issue body.
+
+I want to express some kind of concern. :label:`concern`
+"""
+SINGLE_LABEL = ["concern"]
+
+MULTI_LABEL_ISSUE = """This is another issue body.
+I want to both express :label:`concern` but also
+:label:`happy` :label:`multiple words in label`.
+"""
+MULTI_LABELS = ["concern", "happy", "multiple words in label"]
+
+NO_LABEL_ISSUE = """I just want to write an issue that does not contain any
+labels whatsoever."""
+
+
+@pytest.mark.parametrize(
+    "text, expected_labels",
+    [
+        (SINGLE_LABEL_ISSUE, SINGLE_LABEL),
+        (MULTI_LABEL_ISSUE, MULTI_LABELS),
+        (NO_LABEL_ISSUE, []),
+    ],
+    ids=["single-label", "multi-label", "no label"],
+)
+def test_parse_wanted_labels_correctly_parses_labels(text, expected_labels):
+    actual_labels = parse.parse_wanted_labels(text)
+    assert sorted(actual_labels) == sorted(expected_labels)
+
+
+ALLOWED_LABELS_TEXT = """nice label
+   another label
+
+best label
+"""
+ALLOWED_LABELS = ["nice label", "best label", "another label"]
+COMMENTED_LABELS_TEXT = """nice label
+ # another label
+best label"""
+COMMENT_LABELS = ["nice label", "best label"]
+EMPTY_LABELS_TEXT = ""
+
+
+@pytest.mark.parametrize(
+    "text, expected_labels",
+    [
+        (ALLOWED_LABELS_TEXT, ALLOWED_LABELS),
+        (COMMENTED_LABELS_TEXT, COMMENT_LABELS),
+        (EMPTY_LABELS_TEXT, []),
+    ],
+    ids=("multi-labels", "commented labels", "no labels"),
+)
+def test_parse_allowed_labels_correctly_parses_labeles(text, expected_labels):
+    actual_labels = parse.parse_allowed_labels(text)
+    assert sorted(actual_labels) == sorted(expected_labels)