3
\$\begingroup\$

I have written code to manage a string and retrieve from it a dictionary which must contain pairs key-value dependent from the string.

To be more clear I'll show some examples and after that the code which works with the string input and give desired output.

Examples

Input descr Input String Output Dictionary
normal test case "String1 <TAG1|VAL1>" {'INFO': 'String1 VAL1', 'TAG1': 'VAL1'}
normal test case "<TAG1|VAL1> String1 <TAG2|VAL2>" {'INFO': 'VAL1 String1 VAL2', 'TAG1': 'VAL1', 'TAG2': 'VAL2'}
Special input; < inside VAL "<TAG1|<VAL1> String1 <TAG2|VAL2>" {'INFO': '<VAL1 String1 VAL2', 'TAG1': '<VAL1', 'TAG2': 'VAL2'}
Special input; missed a > char after VAL1 "<TAG1|VAL1 String1 <TAG2|VAL2>" {'INFO': '<TAG1|VAL1 String1 VAL2', 'TAG2': 'VAL2'}
String input without tag "String1 and not tag" {'INFO': 'String1 and not tag'}
String input with val empty "string <tag|> the tag is discarded" {'INFO': 'string the tag is discarded'}

The examples show that inside the input string could be present one or more special pairs with the following syntax:

SYNTAX of the string

pairs <TAG|VAL> inside the string:

  • < and > are 2 delimiters
  • | is a separator between the key (TAG) of the output dictionary and the value (VAL) of that key dictionary

My Code

I have saved my code in the file code_for_review.py:

""" Function: save_tags_from_info()
"""
def save_tags_from_info(info):
    dict = {}
    while True:
        if info is None:
            break
        info_split = info.split('<', 1)
        if len(info_split) < 2:
            # '<' delimiter not present
            break
        # '<' delimiter is present
        info = info_split[1]
        info_split = info.split('>', 1)
        if len(info_split) < 2:
            # '>' delimiter not present
            break
        tag_value = info_split[0]
        info = info_split[1]    # used to continue the analysis of the info string in the substring which follow '>'
        # looking for separator '|' (to manage some not standard input)
        tag_value_split = tag_value.split('|')
        if len(tag_value_split) < 2:
            # between delimiterss '<' and '>' is not present the separator '|'
            break
        # found at least one separator '|'
        if len(tag_value_split) > 2:
            # not standrd condition: found more the one char '|': it is excluded the first element of the list
            tag_value_split = tag_value_split[1:]
            # looking for the first delimiter ('<') to find the key of the output dictionary
            correct_key = tag_value_split[0].split('<', 1)
            if len(correct_key) < 2:
                break
            tag_value_split[0] = correct_key[1]
        tag_key = tag_value_split[0]
        tag_value = tag_value_split[1]
        dict[tag_key] = tag_value
    return dict

""" Function: remove_tag_from_info()
"""
def remove_tag_from_info(info, dict):
    for key in dict:
        tag_value = dict[key]
        # remove char '<' tag and '|' from info
        info = info.replace('<' + key + '|', '')
        # remove char '>' which follow the value in the pair <TAG|value>
        pos = info.find(tag_value + '>')
        if pos >= 0:
            info = info.replace(tag_value + '>', tag_value, 1)
    return info

input_string_list = ["String1 <TAG1|VAL1>", "<TAG1|VAL1> String1 <TAG2|VAL2>",
                     "<TAG1|<VAL1> String1 <TAG2|VAL2>", "<TAG1|VAL1 String1 <TAG2|VAL2>",
                     "String1 and not tag", "string <tag|> the tag is discarded"]

def get_dictionary_from_info_string():
    for info in input_string_list:
        dict_extracted_tag = save_tags_from_info(info)
        d = {}
        d['INFO'] = remove_tag_from_info(info, dict_extracted_tag)
        dict_extracted_tag = {k: v for k, v in dict_extracted_tag.items() if v}
        d.update(dict_extracted_tag)
        print(d)

get_dictionary_from_info_string()

The output of the execution of the previous code is:

{'INFO': 'String1 VAL1', 'TAG1': 'VAL1'}
{'INFO': 'VAL1 String1 VAL2', 'TAG1': 'VAL1', 'TAG2': 'VAL2'}
{'INFO': '<VAL1 String1 VAL2', 'TAG1': '<VAL1', 'TAG2': 'VAL2'}
{'INFO': '<TAG1|VAL1 String1 VAL2', 'TAG2': 'VAL2'}
{'INFO': 'String1 and not tag'}
{'INFO': 'string  the tag is discarded'}

The output it is described in the table showed above.

Question

I have written my code by Test Driven Development so seems to work, but I think that it is not easy to understand. I suppose that one or more regular expressions could do the work well than my code.
I'm not able to write regular expression to solve my problem and I'm here to ask if someone can help me to simplify my code with or without regular expressions?

Note: I brief explanation of the regular expression (if it is applicable for my problem) is appreciated.


EDIT

Tests used for develop the production code (TDD)

As asked by @J_H, I have updated the question adding the test code (which follows the Test Driven Development). The test code is saved in the file test.py; this file use the Python module unittest:

import unittest
from code_for_review import save_tags_from_info, remove_tag_from_info
import re

class MyTestCase(unittest.TestCase):
    #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    # Test for develop of save_tags_from_info()
    #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    def test_save_tags_from_info_01(self):
        info = "xxx <TAG1|VAL1> ssss <TAG2|VAL2>"
        self.assertDictEqual({'TAG1': 'VAL1', 'TAG2': 'VAL2'}, save_tags_from_info(info))

    def test_save_tags_from_info_02(self):
        info = "xxx <TAG3|VAL3> ssss <TAG4|VAL4>"
        self.assertDictEqual({'TAG3': 'VAL3', 'TAG4': 'VAL4'}, save_tags_from_info(info))

    def test_save_tags_from_info_03(self):
        info = "xxx <Tag1|Value1>"
        self.assertDictEqual({'Tag1': 'Value1'}, save_tags_from_info(info))

    def test_save_tags_from_info_04(self):
        info = "A String1\tString2. Strin3\t"
        self.assertDictEqual({}, save_tags_from_info(info))

    def test_save_tags_from_info_05(self):
        info = ""
        self.assertDictEqual({}, save_tags_from_info(info))

    def test_save_tags_from_info_06(self):
        info = "xxx TAG|VALUE>"
        self.assertDictEqual({}, save_tags_from_info(info))

    def test_save_tags_from_info_07(self):
        info = "xxx <TAG value>"
        self.assertDictEqual({}, save_tags_from_info(info))

    def test_save_tags_from_info_08(self):
        info = "xxx <TAG|value"
        self.assertDictEqual({}, save_tags_from_info(info))

    def test_save_tags_from_info_09(self):
        info = "String1 <TAG1|VALUE1>\t<TAG2|VAL2> val1->val2 <->\t"
        self.assertDictEqual({'TAG1': 'VALUE1', 'TAG2': 'VAL2'}, save_tags_from_info(info))

    def test_save_tags_from_info_10(self):
        info = "xxx <Tag|SSSS vvv <TAG2|VAlueeee>"
        self.assertDictEqual({'TAG2': 'VAlueeee'}, save_tags_from_info(info))

    def test_save_tags_from_info_11(self):
        info = "xxx <tAg|xxxxxx vvv ppp|AAAAA>"
        self.assertDictEqual({}, save_tags_from_info(info))

    def test_save_tags_from_info_12(self):
        info = "xxx <xxx|yyyyyy> vvv uuu|aaa>"
        self.assertDictEqual({'xxx': 'yyyyyy'}, save_tags_from_info(info))

    def test_save_tags_from_info_13(self):
        info = "xxx ppp|qqq> vvv <uuu|ddddd>"
        self.assertDictEqual({'uuu' : 'ddddd'}, save_tags_from_info(info))

    def test_save_tags_from_info_14(self):
        info = "xxx <zzz1|ppp2> vvv <kkk vvv>"
        self.assertDictEqual({'zzz1' : 'ppp2'}, save_tags_from_info(info))

    def test_save_tags_from_info_15(self):
        info = "xxx <vvv|bbb> vvv <ttt|<22222>"
        self.assertDictEqual({'vvv' : 'bbb', 'ttt': '<22222'}, save_tags_from_info(info))

    def test_save_tags_from_info_16(self):
        info = "xxx <<PRD|PATATE>> vvv <<USR|<ADMINI>>"
        self.assertDictEqual({'<PRD' : 'PATATE', '<USR': '<ADMINI'}, save_tags_from_info(info))

    def test_save_tags_from_info_17(self):
        info = "xxx <<|>> vvv <ppppp|kkk>"
        self.assertDictEqual({'<':'', 'ppppp': 'kkk'}, save_tags_from_info(info))

    def test_save_tags_from_info_18(self):
        info = ""
        self.assertDictEqual({}, save_tags_from_info(info))

    def test_save_tags_from_info_19(self):
        info = None
        self.assertDictEqual({}, save_tags_from_info(info))

    def test_save_tags_from_info_20(self):
        info = "<Baaa|Llll1>\t vvv: 3\tST. STRING2\t"
        self.assertDictEqual({'Baaa': 'Llll1'}, save_tags_from_info(info))

    def test_save_tags_from_info_21(self):
        info = "<Baaa|>\t A: 0\tIII CCCC\t"
        self.assertDictEqual({'Baaa': ''}, save_tags_from_info(info))

    #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    # Test for develop of remove_tag_from_info()
    #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    """ In the following test it has been used the regular expression:
        r'<[^|<>]*\|([^|<>]*)>' to substitute the tag <TAG|VAL> with VAL in the string info.
        The regex could be used with the re.sub() method as following:
        re.sub(r'<[^|<>]*\|([^|<>]*)>', '\\1', info)
        In the tests test_remove_tag_from_info_15(), test_remove_tag_from_info_16() and test_remove_tag_from_info_17()
        my function and the regex don't give the same results.
    """
    def test_remove_tag_from_info_01(self):
        info = "String <TAG|VAL>\tString2 \t"
        dict_extracted_tag = save_tags_from_info(info)
        self.assertEqual("String VAL\tString2 \t", remove_tag_from_info(info, dict_extracted_tag))
        self.assertEqual("String VAL\tString2 \t", re.sub(r'<[^|<>]*\|([^|<>]*)>', '\\1', info))

    def test_remove_tag_from_info_02(self):
        info = "String <TAG|>\tString2\t"
        dict_extracted_tag = save_tags_from_info(info)
        self.assertEqual("String \tString2\t", remove_tag_from_info(info, dict_extracted_tag))
        self.assertEqual("String \tString2\t", re.sub(r'<[^|<>]*\|([^|<>]*)>', '\\1', info))

    def test_remove_tag_from_info_03(self):
        info = "String1 -> String2\tStr. String3\tString4 <TAG1|*VALUE*>\t"
        dict_extracted_tag = save_tags_from_info(info)
        self.assertEqual("String1 -> String2\tStr. String3\tString4 *VALUE*\t", remove_tag_from_info(info, dict_extracted_tag))
        self.assertEqual("String1 -> String2\tStr. String3\tString4 *VALUE*\t", re.sub(r'<[^|<>]*\|([^|<>]*)>', '\\1', info))

    def test_remove_tag_from_info_04(self):
        info = ""
        dict_extracted_tag = save_tags_from_info(info)
        self.assertEqual("", remove_tag_from_info(info, dict_extracted_tag))
        self.assertEqual("", re.sub(r'<[^|<>]*\|([^|<>]*)>', '\\1', info))

    def test_remove_tag_from_info_05(self):
        info = None
        dict_extracted_tag = save_tags_from_info(info)
        self.assertIsNone(remove_tag_from_info(None, dict_extracted_tag))

    def test_remove_tag_from_info_06(self):
        info = "String1 <TAG1|VALUE1>\tString2 <TAG2|VALUE2>\t"
        dict_extracted_tag = save_tags_from_info(info)
        self.assertEqual("String1 VALUE1\tString2 VALUE2\t", remove_tag_from_info(info, dict_extracted_tag))
        self.assertEqual("String1 VALUE1\tString2 VALUE2\t", re.sub(r'<[^|<>]*\|([^|<>]*)>', '\\1', info))

    def test_remove_tag_from_info_07(self):
        info = "String1 <TAG1|VALUE1>\tString2 <TAG2|VALUE2>\tString3"
        dict_extracted_tag = save_tags_from_info(info)
        self.assertEqual("String1 VALUE1\tString2 VALUE2\tString3", remove_tag_from_info(info, dict_extracted_tag))
        self.assertEqual("String1 VALUE1\tString2 VALUE2\tString3", re.sub(r'<[^|<>]*\|([^|<>]*)>', '\\1', info))

    def test_remove_tag_from_info_08(self):
        info = "String1 <TAG1VALUE1>\t"
        dict_extracted_tag = save_tags_from_info(info)
        self.assertEqual("String1 <TAG1VALUE1>\t", remove_tag_from_info(info, dict_extracted_tag))
        self.assertEqual("String1 <TAG1VALUE1>\t", re.sub(r'<[^|<>]*\|([^|<>]*)>', '\\1', info))

    def test_remove_tag_from_info_09(self):
        info = "String1 <|VALUE1>\t"
        dict_extracted_tag = save_tags_from_info(info)
        self.assertEqual("String1 VALUE1\t", remove_tag_from_info(info, dict_extracted_tag))
        self.assertEqual("String1 VALUE1\t", re.sub(r'<[^|<>]*\|([^|<>]*)>', '\\1', info))

    def test_remove_tag_from_info_10(self):
        info = "String1 <TAG1|VALUE1>\t<TAG2|VAL2> val1->val2 <->\t"
        dict_extracted_tag = save_tags_from_info(info)
        self.assertEqual("String1 VALUE1\tVAL2 val1->val2 <->\t", remove_tag_from_info(info, dict_extracted_tag))
        self.assertEqual("String1 VALUE1\tVAL2 val1->val2 <->\t", re.sub(r'<[^|<>]*\|([^|<>]*)>', '\\1', info))

    def test_remove_tag_from_info_11(self):
        info = "xxx <Tag1|Value1 vvv <Tag2|Value2>"
        dict_extracted_tag = save_tags_from_info(info)
        self.assertEqual("xxx <Tag1|Value1 vvv Value2", remove_tag_from_info(info, dict_extracted_tag))
        self.assertEqual("xxx <Tag1|Value1 vvv Value2", re.sub(r'<[^|<>]*\|([^|<>]*)>', '\\1', info))

    def test_remove_tag_from_info_12(self):
        info = "xxx <Tag N|Value N vvv Tag M|Value M>"
        dict_extracted_tag = save_tags_from_info(info)
        self.assertEqual("xxx <Tag N|Value N vvv Tag M|Value M>", remove_tag_from_info(info, dict_extracted_tag))
        self.assertEqual("xxx <Tag N|Value N vvv Tag M|Value M>", re.sub(r'<[^|<>]*\|([^|<>]*)>', '\\1', info))

    def test_remove_tag_from_info_13(self):
        info = "xxx <Tag N|Value N> vvv Tag M|Value M>"
        dict_extracted_tag = save_tags_from_info(info)
        self.assertEqual("xxx Value N vvv Tag M|Value M>", remove_tag_from_info(info, dict_extracted_tag))
        self.assertEqual("xxx Value N vvv Tag M|Value M>", re.sub(r'<[^|<>]*\|([^|<>]*)>', '\\1', info))

    def test_remove_tag_from_info_14(self):
        info = "xxx Tag|Value> vvv <Tag2|Value2>"
        dict_extracted_tag = save_tags_from_info(info)
        self.assertEqual("xxx Tag|Value> vvv Value2", remove_tag_from_info(info, dict_extracted_tag))
        self.assertEqual("xxx Tag|Value> vvv Value2", re.sub(r'<[^|<>]*\|([^|<>]*)>', '\\1', info))

    def test_remove_tag_from_info_15(self):
        info = "xxx <Tag|Value> vvv <Tag2|<Value2>"
        dict_extracted_tag = save_tags_from_info(info)
        self.assertEqual("xxx Value vvv <Value2", remove_tag_from_info(info, dict_extracted_tag))
        # following test fails (regex not suited?)
        #self.assertEqual("xxx Value vvv <Value2", re.sub(r'<[^|<>]*\|([^|<>]*)>', '\\1', info))

    def test_remove_tag_from_info_16(self):
        info = "xxx <<PRD|PATATE>> vvv <<USR|<ADMINI>>"
        dict_extracted_tag = save_tags_from_info(info)
        self.assertEqual("xxx PATATE> vvv <ADMINI>", remove_tag_from_info(info, dict_extracted_tag))
        # following test fails
        #self.assertEqual("xxx PATATE> vvv <ADMINI>", re.sub(r'<[^|<>]*\|([^|<>]*)>', '\\1', info))

    def test_remove_tag_from_info_17(self):
        info = "xxx <<|>> vvv <Tag|Value>"
        dict_extracted_tag = save_tags_from_info(info)
        self.assertEqual("xxx > vvv Value", remove_tag_from_info(info, dict_extracted_tag))
        # following test fails (regex not suited?)
        #self.assertEqual("xxx > vvv Value", re.sub(r'<[^|<>]*\|([^|<>]*)>', '\\1', info))

    def test_remove_tag_from_info_18(self):
        info = "<Tag|Value1>\t CC: 0\tString. String2\t"
        dict_extracted_tag = save_tags_from_info(info)
        self.assertEqual("Value1\t CC: 0\tString. String2\t", remove_tag_from_info(info, dict_extracted_tag))
        self.assertEqual("Value1\t CC: 0\tString. String2\t", re.sub(r'<[^|<>]*\|([^|<>]*)>', '\\1', info))

if __name__ == '__main__':
    unittest.main()

The regex expression used in the tests

Some tests contain the regex r'<[^|<>]*\|([^|<>]*)>'. This regex comes for some suggestions received in other sites but I'm not able to understand it. Furthermore it is not exactly suited for all the test cases (see tests number 15,16,17).

\$\endgroup\$
4
  • 1
    \$\begingroup\$ The print(dict) is nice enough, and reproducible, thank you. For TDD, would you please use self.assertEqual() in a test suite, to facilitate regex refactoring? Also, to avoid shadowing builtins such as "dict" we conventionally name such a local variable d. \$\endgroup\$
    – J_H
    Commented Jun 24 at 17:41
  • \$\begingroup\$ @J_H I have change dict to d and I have added all the tests that I have written to develop the production code (as I wrote in the question I have used TDD to develop mu production code). I hope this is sufficient. \$\endgroup\$
    – User051209
    Commented Jun 25 at 7:30
  • 1
    \$\begingroup\$ I'm not able to write regular expression to solve my problem is not strictly true; you just don't know how yet. This problem is a fantastic opportunity to learn them from the ground up. \$\endgroup\$
    – Reinderien
    Commented Jun 25 at 11:48
  • \$\begingroup\$ Sorry I have corrected an instruction in the function remove_tag_from_info(). Please takes the new code of this function from my question for your tests.. \$\endgroup\$
    – User051209
    Commented Jun 25 at 12:39

1 Answer 1

3
\$\begingroup\$

It's really great that you've written and run tests. Would that all of my work were so well-tested. However, there are some issues with your tests:

MyTestCase is a catch-all, but you should subdivide it into at least two classes - one for your "save tags" tests, and one for your "remove tag". Whenever you're tempted to write

#++++++++++++++++++++++++++++++++++++++++++
# ---- **** Big new section **** ----
#++++++++++++++++++++++++++++++++++++++++++

you need to delete that comment and replace it with actual program structure, whether that be a new class, function or module.

When using a half-decent IDE, that structure will be exposed in the testing interface:

PyCharm

Also, never comment out tests. Tests failing is a healthy thing, and you should never lie and say that it's all green.

The way that you've started testing regular expressions is not systematic enough. Rather than baking in a second check with a regex that looks like

        self.assertEqual("xxx > vvv Value", remove_tag_from_info(info, dict_extracted_tag))
        # following test fails (regex not suited?)
        #self.assertEqual("xxx > vvv Value", re.sub(r'<[^|<>]*\|([^|<>]*)>', '\\1', info))

you need to

  1. remove the regexes from your test,
  2. write an alternative implementation for your tested functions, and
  3. parameterise your tests so that all of the tests are run with both the old and new implementations.

With built-in unittest this is possible but inconvenient. There are a lot of strategies, including writing metaclasses, or looping over subtests, etc. I demonstrate a hacky but functional way to abuse the inheritance system and target tests against multiple implementations.

import re
import unittest


def save_tags_from_info_op(info: str) -> dict[str, str]:
    dict = {}
    while True:
        if info is None:
            break
        info_split = info.split('<', 1)
        if len(info_split) < 2:
            # '<' delimiter not present
            break
        # '<' delimiter is present
        info = info_split[1]
        info_split = info.split('>', 1)
        if len(info_split) < 2:
            # '>' delimiter not present
            break
        tag_value = info_split[0]
        info = info_split[1]    # used to continue the analysis of the info string in the substring which follow '>'
        # looking for separator '|' (to manage some not standard input)
        tag_value_split = tag_value.split('|')
        if len(tag_value_split) < 2:
            # between delimiterss '<' and '>' is not present the separator '|'
            break
        # found at least one separator '|'
        if len(tag_value_split) > 2:
            # not standrd condition: found more the one char '|': it is excluded the first element of the list
            tag_value_split = tag_value_split[1:]
            # looking for the first delimiter ('<') to find the key of the output dictionary
            correct_key = tag_value_split[0].split('<', 1)
            if len(correct_key) < 2:
                break
            tag_value_split[0] = correct_key[1]
        tag_key = tag_value_split[0]
        tag_value = tag_value_split[1]
        dict[tag_key] = tag_value
    return dict


def remove_tag_from_info_op(info: str, dict: dict[str, str]) -> str | None:
    for key in dict:
        tag_value = dict[key]
        # remove char '<' tag and '|' from info
        info = info.replace('<' + key + '|', '')
        # remove char '>' which follow the value in the pair <TAG|value>
        pos = info.find(tag_value + '>')
        if pos >= 0:
            info = info.replace(tag_value + '>', tag_value, 1)
    return info


def remove_tag_from_info_opre(info: str, dict_: dict[str, str]) -> str | None:
    if info is None:
        return None
    return re.sub(r'<[^|<>]*\|([^|<>]*)>', '\\1', info)


def get_dictionary_from_info_string_op(info: str) -> dict[str, str]:
    dict_extracted_tag = save_tags_from_info_op(info)
    d = {'INFO': remove_tag_from_info_op(info, dict_extracted_tag)}
    dict_extracted_tag = {k: v for k, v in dict_extracted_tag.items() if v}
    d.update(dict_extracted_tag)
    return d


def get_dictionary_from_info_string_opre(info: str) -> dict[str, str]:
    dict_extracted_tag = save_tags_from_info_op(info)
    d = {'INFO': remove_tag_from_info_opre(info, dict_extracted_tag)}
    dict_extracted_tag = {k: v for k, v in dict_extracted_tag.items() if v}
    d.update(dict_extracted_tag)
    return d


class AbstractInfo:
    @staticmethod
    def save_tags_from_info(info: str) -> dict[str, str]: ...
    @staticmethod
    def remove_tag_from_info(info: str, dict_: dict[str, str]) -> str | None: ...
    @staticmethod
    def get_dictionary_from_info_string(info: str) -> dict[str, str]: ...


class OPMixin(AbstractInfo):
    save_tags_from_info = staticmethod(save_tags_from_info_op)
    remove_tag_from_info = staticmethod(remove_tag_from_info_op)
    get_dictionary_from_info_string = staticmethod(get_dictionary_from_info_string_op)


class OPRegexMixin(AbstractInfo):
    # save_tags_from_info not implemented
    remove_tag_from_info = staticmethod(remove_tag_from_info_opre)
    get_dictionary_from_info_string = staticmethod(get_dictionary_from_info_string_opre)


class Demo(AbstractInfo, unittest.TestCase):
    def test_normal(self) -> None:
        self.assertDictEqual(
            self.get_dictionary_from_info_string("String1 <TAG1|VAL1>"),
            {'INFO': 'String1 VAL1', 'TAG1': 'VAL1'},
        )

    def test_normal2(self) -> None:
        self.assertDictEqual(
            self.get_dictionary_from_info_string(   "<TAG1|VAL1> String1 <TAG2|VAL2>"),
            {'INFO': 'VAL1 String1 VAL2', 'TAG1': 'VAL1', 'TAG2': 'VAL2'},
        )

    def test_special_angle_inside(self) -> None:
        self.assertDictEqual(
            self.get_dictionary_from_info_string("<TAG1|<VAL1> String1 <TAG2|VAL2>"),
            {'INFO': '<VAL1 String1 VAL2', 'TAG1': '<VAL1', 'TAG2': 'VAL2'},
        )

    def test_special_missing_angle(self) -> None:
        self.assertDictEqual(
            self.get_dictionary_from_info_string("<TAG1|VAL1 String1 <TAG2|VAL2>"),
            {'INFO': '<TAG1|VAL1 String1 VAL2', 'TAG2': 'VAL2'},
        )

    def test_no_tags(self) -> None:
        self.assertDictEqual(
            self.get_dictionary_from_info_string("String1 and not tag"),
            {'INFO': 'String1 and not tag'},
        )

    def test_empty_val(self) -> None:
        self.assertDictEqual(
            self.get_dictionary_from_info_string("string <tag|> the tag is discarded"),
            {'INFO': 'string  the tag is discarded'},
        )


class DemoOPTests(Demo, OPMixin):
    pass
class DemoOPRegexTests(Demo, OPRegexMixin):
    pass
del Demo


class SaveTagsFromInfo(AbstractInfo, unittest.TestCase):
    """
    Test for develop of save_tags_from_info()
    """

    def test_save_tags_from_info_01(self):
        info = "xxx <TAG1|VAL1> ssss <TAG2|VAL2>"
        self.assertDictEqual({'TAG1': 'VAL1', 'TAG2': 'VAL2'}, self.save_tags_from_info(info))

    def test_save_tags_from_info_02(self):
        info = "xxx <TAG3|VAL3> ssss <TAG4|VAL4>"
        self.assertDictEqual({'TAG3': 'VAL3', 'TAG4': 'VAL4'}, self.save_tags_from_info(info))

    def test_save_tags_from_info_03(self):
        info = "xxx <Tag1|Value1>"
        self.assertDictEqual({'Tag1': 'Value1'}, self.save_tags_from_info(info))

    def test_save_tags_from_info_04(self):
        info = "A String1\tString2. Strin3\t"
        self.assertDictEqual({}, self.save_tags_from_info(info))

    def test_save_tags_from_info_05(self):
        info = ""
        self.assertDictEqual({}, self.save_tags_from_info(info))

    def test_save_tags_from_info_06(self):
        info = "xxx TAG|VALUE>"
        self.assertDictEqual({}, self.save_tags_from_info(info))

    def test_save_tags_from_info_07(self):
        info = "xxx <TAG value>"
        self.assertDictEqual({}, self.save_tags_from_info(info))

    def test_save_tags_from_info_08(self):
        info = "xxx <TAG|value"
        self.assertDictEqual({}, self.save_tags_from_info(info))

    def test_save_tags_from_info_09(self):
        info = "String1 <TAG1|VALUE1>\t<TAG2|VAL2> val1->val2 <->\t"
        self.assertDictEqual({'TAG1': 'VALUE1', 'TAG2': 'VAL2'}, self.save_tags_from_info(info))

    def test_save_tags_from_info_10(self):
        info = "xxx <Tag|SSSS vvv <TAG2|VAlueeee>"
        self.assertDictEqual({'TAG2': 'VAlueeee'}, self.save_tags_from_info(info))

    def test_save_tags_from_info_11(self):
        info = "xxx <tAg|xxxxxx vvv ppp|AAAAA>"
        self.assertDictEqual({}, self.save_tags_from_info(info))

    def test_save_tags_from_info_12(self):
        info = "xxx <xxx|yyyyyy> vvv uuu|aaa>"
        self.assertDictEqual({'xxx': 'yyyyyy'}, self.save_tags_from_info(info))

    def test_save_tags_from_info_13(self):
        info = "xxx ppp|qqq> vvv <uuu|ddddd>"
        self.assertDictEqual({'uuu' : 'ddddd'}, self.save_tags_from_info(info))

    def test_save_tags_from_info_14(self):
        info = "xxx <zzz1|ppp2> vvv <kkk vvv>"
        self.assertDictEqual({'zzz1' : 'ppp2'}, self.save_tags_from_info(info))

    def test_save_tags_from_info_15(self):
        info = "xxx <vvv|bbb> vvv <ttt|<22222>"
        self.assertDictEqual({'vvv' : 'bbb', 'ttt': '<22222'}, self.save_tags_from_info(info))

    def test_save_tags_from_info_16(self):
        info = "xxx <<PRD|PATATE>> vvv <<USR|<ADMINI>>"
        self.assertDictEqual({'<PRD' : 'PATATE', '<USR': '<ADMINI'}, self.save_tags_from_info(info))

    def test_save_tags_from_info_17(self):
        info = "xxx <<|>> vvv <ppppp|kkk>"
        self.assertDictEqual({'<':'', 'ppppp': 'kkk'}, self.save_tags_from_info(info))

    def test_save_tags_from_info_18(self):
        info = ""
        self.assertDictEqual({}, self.save_tags_from_info(info))

    def test_save_tags_from_info_19(self):
        info = None
        self.assertDictEqual({}, self.save_tags_from_info(info))

    def test_save_tags_from_info_20(self):
        info = "<Baaa|Llll1>\t vvv: 3\tST. STRING2\t"
        self.assertDictEqual({'Baaa': 'Llll1'}, self.save_tags_from_info(info))

    def test_save_tags_from_info_21(self):
        info = "<Baaa|>\t A: 0\tIII CCCC\t"
        self.assertDictEqual({'Baaa': ''}, self.save_tags_from_info(info))


class SaveTagsFromInfoOPTests(SaveTagsFromInfo, OPMixin):
    pass
del SaveTagsFromInfo


class RemoveTagFromInfo(AbstractInfo, unittest.TestCase):
    def test_remove_tag_from_info_01(self):
        info = "String <TAG|VAL>\tString2 \t"
        dict_extracted_tag = self.save_tags_from_info(info)
        self.assertEqual("String VAL\tString2 \t", self.remove_tag_from_info(info, dict_extracted_tag))

    def test_remove_tag_from_info_02(self):
        info = "String <TAG|>\tString2\t"
        dict_extracted_tag = self.save_tags_from_info(info)
        self.assertEqual("String \tString2\t", self.remove_tag_from_info(info, dict_extracted_tag))

    def test_remove_tag_from_info_03(self):
        info = "String1 -> String2\tStr. String3\tString4 <TAG1|*VALUE*>\t"
        dict_extracted_tag = self.save_tags_from_info(info)
        self.assertEqual("String1 -> String2\tStr. String3\tString4 *VALUE*\t", self.remove_tag_from_info(info, dict_extracted_tag))

    def test_remove_tag_from_info_04(self):
        info = ""
        dict_extracted_tag = self.save_tags_from_info(info)
        self.assertEqual("", self.remove_tag_from_info(info, dict_extracted_tag))

    def test_remove_tag_from_info_05(self):
        info = None
        dict_extracted_tag = self.save_tags_from_info(info)
        self.assertIsNone(self.remove_tag_from_info(None, dict_extracted_tag))

    def test_remove_tag_from_info_06(self):
        info = "String1 <TAG1|VALUE1>\tString2 <TAG2|VALUE2>\t"
        dict_extracted_tag = self.save_tags_from_info(info)
        self.assertEqual("String1 VALUE1\tString2 VALUE2\t", self.remove_tag_from_info(info, dict_extracted_tag))

    def test_remove_tag_from_info_07(self):
        info = "String1 <TAG1|VALUE1>\tString2 <TAG2|VALUE2>\tString3"
        dict_extracted_tag = self.save_tags_from_info(info)
        self.assertEqual("String1 VALUE1\tString2 VALUE2\tString3", self.remove_tag_from_info(info, dict_extracted_tag))

    def test_remove_tag_from_info_08(self):
        info = "String1 <TAG1VALUE1>\t"
        dict_extracted_tag = self.save_tags_from_info(info)
        self.assertEqual("String1 <TAG1VALUE1>\t", self.remove_tag_from_info(info, dict_extracted_tag))

    def test_remove_tag_from_info_09(self):
        info = "String1 <|VALUE1>\t"
        dict_extracted_tag = self.save_tags_from_info(info)
        self.assertEqual("String1 VALUE1\t", self.remove_tag_from_info(info, dict_extracted_tag))

    def test_remove_tag_from_info_10(self):
        info = "String1 <TAG1|VALUE1>\t<TAG2|VAL2> val1->val2 <->\t"
        dict_extracted_tag = self.save_tags_from_info(info)
        self.assertEqual("String1 VALUE1\tVAL2 val1->val2 <->\t", self.remove_tag_from_info(info, dict_extracted_tag))

    def test_remove_tag_from_info_11(self):
        info = "xxx <Tag1|Value1 vvv <Tag2|Value2>"
        dict_extracted_tag = self.save_tags_from_info(info)
        self.assertEqual("xxx <Tag1|Value1 vvv Value2", self.remove_tag_from_info(info, dict_extracted_tag))

    def test_remove_tag_from_info_12(self):
        info = "xxx <Tag N|Value N vvv Tag M|Value M>"
        dict_extracted_tag = self.save_tags_from_info(info)
        self.assertEqual("xxx <Tag N|Value N vvv Tag M|Value M>", self.remove_tag_from_info(info, dict_extracted_tag))

    def test_remove_tag_from_info_13(self):
        info = "xxx <Tag N|Value N> vvv Tag M|Value M>"
        dict_extracted_tag = self.save_tags_from_info(info)
        self.assertEqual("xxx Value N vvv Tag M|Value M>", self.remove_tag_from_info(info, dict_extracted_tag))

    def test_remove_tag_from_info_14(self):
        info = "xxx Tag|Value> vvv <Tag2|Value2>"
        dict_extracted_tag = self.save_tags_from_info(info)
        self.assertEqual("xxx Tag|Value> vvv Value2", self.remove_tag_from_info(info, dict_extracted_tag))

    def test_remove_tag_from_info_15(self):
        info = "xxx <Tag|Value> vvv <Tag2|<Value2>"
        dict_extracted_tag = self.save_tags_from_info(info)
        self.assertEqual("xxx Value vvv <Value2", self.remove_tag_from_info(info, dict_extracted_tag))

    def test_remove_tag_from_info_16(self):
        info = "xxx <<PRD|PATATE>> vvv <<USR|<ADMINI>>"
        dict_extracted_tag = self.save_tags_from_info(info)
        self.assertEqual("xxx PATATE> vvv <ADMINI>", self.remove_tag_from_info(info, dict_extracted_tag))

    def test_remove_tag_from_info_17(self):
        info = "xxx <<|>> vvv <Tag|Value>"
        dict_extracted_tag = self.save_tags_from_info(info)
        self.assertEqual("xxx > vvv Value", self.remove_tag_from_info(info, dict_extracted_tag))

    def test_remove_tag_from_info_18(self):
        info = "<Tag|Value1>\t CC: 0\tString. String2\t"
        dict_extracted_tag = self.save_tags_from_info(info)
        self.assertEqual("Value1\t CC: 0\tString. String2\t", self.remove_tag_from_info(info, dict_extracted_tag))


class RemoveTagFromInfoOPTests(RemoveTagFromInfo, OPMixin):
    pass
class RemoveTagFromInfoOPRegexTests(RemoveTagFromInfo, OPRegexMixin):
    pass
del RemoveTagFromInfo


if __name__ == '__main__':
    unittest.main()
Launching unittests with arguments python -m unittest 292712.py
...
Ran 69 tests in 0.025s

FAILED (failures=4)
\$\endgroup\$
3
  • \$\begingroup\$ Thank you very much for your suggestions. Could you better explain the third point: parameterise your tests so that all of the tests are run with both the old and new implementations. \$\endgroup\$
    – User051209
    Commented Jun 25 at 12:18
  • \$\begingroup\$ Sure; I'm cooking up a demo. \$\endgroup\$
    – Reinderien
    Commented Jun 25 at 12:19
  • 1
    \$\begingroup\$ Thank you very much. I need to understand your code to increase my Python knowledge. If I'll have enough time to study in detail all your suggestions I'll give you a feed back. \$\endgroup\$
    – User051209
    Commented Jun 27 at 7:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.