From 285f3b409af69d1717a7c8db6c5a1cc6bfdcd641 Mon Sep 17 00:00:00 2001 From: "Dmitry S. Luhtionov" Date: Thu, 6 Dec 2018 16:49:32 +0200 Subject: [PATCH 1/2] Fix check on empty column in `parse_table()` --- lib/text.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/text.py b/lib/text.py index 97781bcda5..a5b1602c34 100644 --- a/lib/text.py +++ b/lib/text.py @@ -2,7 +2,7 @@ # --------------------------------------------------------------------- # Various text-processing utilities # --------------------------------------------------------------------- -# Copyright (C) 2007-2017 The NOC Project +# Copyright (C) 2007-2018 The NOC Project # See LICENSE for details # --------------------------------------------------------------------- import re @@ -101,7 +101,7 @@ def parse_table(s, allow_wrap=False, allow_extend=False, max_width=0, footer=Non # print("Too many: %s" % s) if allow_wrap: row = [line[f:t] for f, t in columns] - if row[0].startswith(" ") and r: + if not row[0].strip() and r: # first column is empty for i, x in enumerate(row): r[-1][i] += x if not x.strip() else "%s%s" % (n_row_delim, x) else: -- GitLab From b836a2227a7b58507d486c6d77fac4a27cbcc6bb Mon Sep 17 00:00:00 2001 From: Dmitry Volodin Date: Mon, 10 Dec 2018 13:09:39 +0300 Subject: [PATCH 2/2] unittests for noc.lib.text --- lib/text.py | 42 +++++++++++++++++------------------ tests/test_text.py | 55 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 21 deletions(-) create mode 100644 tests/test_text.py diff --git a/lib/text.py b/lib/text.py index a5b1602c34..ae351ea2ec 100644 --- a/lib/text.py +++ b/lib/text.py @@ -5,23 +5,14 @@ # Copyright (C) 2007-2018 The NOC Project # See LICENSE for details # --------------------------------------------------------------------- + +# Python modules import re -import six from itertools import izip_longest +# Third-party modules +import six from numpy import array -# -# Parse string containing table an return a list of table rows. -# Each row is a list of cells. -# Columns are determined by a sequences of ---- or ==== which are -# determines rows bounds. -# Examples: -# First Second Third -# ----- ------ ----- -# a b c -# ddd eee fff -# Will be parsed down to the [["a","b","c"],["ddd","eee","fff"]] -# rx_header_start = re.compile(r"^\s*[-=]+[\s\+]+[-=]+") rx_col = re.compile(r"^([\s\+]*)([\-]+|[=]+)") @@ -29,6 +20,17 @@ rx_col = re.compile(r"^([\s\+]*)([\-]+|[=]+)") def parse_table(s, allow_wrap=False, allow_extend=False, max_width=0, footer=None, n_row_delim=""): # pylint: disable=line-too-long """ + Parse string containing table an return a list of table rows. + Each row is a list of cells. + Columns are determined by a sequences of ---- or ==== which are + determines rows bounds. + Examples: + First Second Third + ----- ------ ----- + a b c + ddd eee fff + Will be parsed down to the [["a","b","c"],["ddd","eee","fff"]] + :param s: Table for parsing :type s: str :param allow_wrap: Union if cell contins multiple line @@ -41,12 +43,6 @@ def parse_table(s, allow_wrap=False, allow_extend=False, max_width=0, footer=Non :type footer: string :param n_row_delim: Append delimiter to next cell line :type n_row_delim: string - >>> parse_table("First Second Third\\n----- ------ -----\\na b c\\nddd eee fff\\n") - [['a', 'b', 'c'], ['ddd', 'eee', 'fff']] - >>> parse_table("First Second Third\\n----- ------ -----\\na c\\nddd eee fff\\n") - [['a', '', 'c'], ['ddd', 'eee', 'fff']] - >>> parse_table("VLAN Status Name Ports\\n---- ------- -------------------------------- ---------------------------------\\n4090 Static VLAN4090 f0/5, f0/6, f0/7, f0/8, g0/9\\n g0/10\\n", allow_wrap=True, n_row_delim=", ") # noqa - [['4090', 'Static', 'VLAN4090', 'f0/5, f0/6, f0/7, f0/8, g0/9, g0/10']] """ r = [] columns = [] @@ -101,9 +97,13 @@ def parse_table(s, allow_wrap=False, allow_extend=False, max_width=0, footer=Non # print("Too many: %s" % s) if allow_wrap: row = [line[f:t] for f, t in columns] - if not row[0].strip() and r: # first column is empty + if r and not row[0].strip(): + # first column is empty for i, x in enumerate(row): - r[-1][i] += x if not x.strip() else "%s%s" % (n_row_delim, x) + if x.strip() and not r[-1][i].endswith(n_row_delim) and not x.startswith(n_row_delim): + r[-1][i] += "%s%s" % (n_row_delim, x) + else: + r[-1][i] += x else: r += [row] else: diff --git a/tests/test_text.py b/tests/test_text.py new file mode 100644 index 0000000000..a186969c20 --- /dev/null +++ b/tests/test_text.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +# ---------------------------------------------------------------------- +# noc.lib.text tests +# ---------------------------------------------------------------------- +# Copyright (C) 2007-2018 The NOC Project +# See LICENSE for details +# ---------------------------------------------------------------------- + +# Thirt-party modules +import pytest +# NOC modules +from noc.lib.text import parse_table + + +@pytest.mark.parametrize("value,kwargs,expected", [ + ( + "First Second Third\n" + "----- ------ -----\n" + "a b c\n" + "ddd eee fff\n", + {}, + [["a", "b", "c"], ["ddd", "eee", "fff"]] + ), + ( + "First Second Third\n" + "----- ------ -----\n" + "a c\n" + "ddd eee fff\n", + {}, + [["a", "", "c"], ["ddd", "eee", "fff"]] + ), + ( + "VLAN Status Name Ports\n" + "---- ------- -------------------------------- ---------------------------------\n" + "4090 Static VLAN4090 f0/5, f0/6, f0/7, f0/8, g0/9\n" + " g0/10\n", + {"allow_wrap": True, "n_row_delim": ", "}, + [["4090", "Static", "VLAN4090", "f0/5, f0/6, f0/7, f0/8, g0/9, g0/10"]] + ), + ( + " MSTI ID Vid list\n" + " ------- -------------------------------------------------------------\n" + " CIST 1-11,15-122,124-250,253,257-300,302-445,447-709\n" + " ,720-759,770-879,901-3859,3861-4094\n" + " 1 12-14,123,251-252,254-256,301,446,710-719,760-769,\n" + " 880-900,3860\n", + {"allow_wrap": True, "n_row_delim": ","}, + [ + ["CIST", "1-11,15-122,124-250,253,257-300,302-445,447-709,720-759,770-879,901-3859,3861-4094"], + ["1", "12-14,123,251-252,254-256,301,446,710-719,760-769,880-900,3860"] + ] + ) +]) +def test_parse_table(value, kwargs, expected): + assert parse_table(value, **kwargs) == expected -- GitLab