diff --git a/README.rst b/README.rst index e8330fa..e7b01cc 100644 --- a/README.rst +++ b/README.rst @@ -81,8 +81,8 @@ Calculate diff of an OLSR 0.6.x topology: from netdiff import OlsrParser from netdiff import diff - old = OlsrParser('./stored-olsr.json') - new = OlsrParser('http://127.0.0.1:9090') + old = OlsrParser(file='./stored-olsr.json') + new = OlsrParser(url='http://127.0.0.1:9090') diff(old, new) In alternative, you may also use the subtraction operator: @@ -92,8 +92,8 @@ In alternative, you may also use the subtraction operator: from netdiff import OlsrParser from netdiff import diff - old = OlsrParser('./stored-olsr.json') - new = OlsrParser('http://127.0.0.1:9090') + old = OlsrParser(file='./stored-olsr.json') + new = OlsrParser(url='http://127.0.0.1:9090') old - new The output will be an ordered dictionary with three keys: @@ -210,18 +210,17 @@ The available parsers are: Initialization arguments ~~~~~~~~~~~~~~~~~~~~~~~~ -**data**: the only required argument, different inputs are accepted: +Data can be supplied in 3 different ways, in the following order of precedence: -* JSON formatted string representing the topology -* python `dict` (or subclass of `dict`) representing the topology -* string representing a HTTP URL where the data resides -* string representing a telnet URL where the data resides -* string representing a file path where the data resides +* ``data``: ``dict`` or ``str`` representing the topology/graph +* ``url``: URL to fetch data from +* ``file``: file path to retrieve data from -**timeout**: integer representing timeout in seconds for HTTP or telnet requests, defaults to None +Other available arguments: -**verify**: boolean indicating to the `request library whether to do SSL certificate -verification or not `_ +* **timeout**: integer representing timeout in seconds for HTTP or telnet requests, defaults to ``None`` +* **verify**: boolean indicating to the `request library whether to do SSL certificate + verification or not `_ Initialization examples ~~~~~~~~~~~~~~~~~~~~~~~ @@ -231,7 +230,7 @@ Local file example: .. code-block:: python from netdiff import BatmanParser - BatmanParser('./my-stored-topology.json') + BatmanParser(file='./my-stored-topology.json') HTTP example: @@ -239,33 +238,32 @@ HTTP example: from netdiff import NetJsonParser url = 'https://raw.githubusercontent.com/interop-dev/netjson/master/examples/network-graph.json' - NetJsonParser(url) + NetJsonParser(url=url) Telnet example with ``timeout``: .. code-block:: python from netdiff import OlsrParser - OlsrParser('telnet://127.0.1:8080', timeout=5) + OlsrParser(url='telnet://127.0.1', timeout=5) HTTPS example with self-signed SSL certificate using ``verify=False``: .. code-block:: python from netdiff import NetJsonParser - OlsrParser('https://myserver.mydomain.com/topology.json', verify=False) + OlsrParser(url='https://myserver.mydomain.com/topology.json', verify=False) NetJSON output -------------- -Netdiff parsers can return a valid `NetJSON`_ -``NetworkGraph`` object: +Netdiff parsers can return a valid `NetJSON`_ ``NetworkGraph`` object: .. code-block:: python from netdiff import OlsrParser - olsr = OlsrParser('telnet://127.0.0.1:9090') + olsr = OlsrParser(url='telnet://127.0.0.1:9090') # will return a dict olsr.json(dict=True) diff --git a/netdiff/parsers/base.py b/netdiff/parsers/base.py index fcad950..dae7ab6 100644 --- a/netdiff/parsers/base.py +++ b/netdiff/parsers/base.py @@ -23,12 +23,15 @@ class BaseParser(object): revision = None metric = None - def __init__(self, data, version=None, revision=None, metric=None, + def __init__(self, data=None, url=None, file=None, + version=None, revision=None, metric=None, timeout=None, verify=True): # noqa """ Initializes a new Parser - :param data: JSON, dict, path to file or HTTP URL of topology + :param data: ``str`` or ``dict`` containing topology data + :param url: HTTP URL to retrieve topology data + :param file: path to file containing topology data :param version: routing protocol version :param revision: routing protocol revision :param metric: routing protocol metric @@ -43,11 +46,25 @@ def __init__(self, data, version=None, revision=None, metric=None, self.metric = metric self.timeout = timeout self.verify = verify + if data is None and url is not None: + data = self._get_url(url) + elif data is None and file is not None: + data = self._get_file(file) + elif data is None and url is None and file is None: + raise ValueError('no topology data supplied, on of the following arguments' + 'must be supplied: data, url or file') self.original_data = self.to_python(data) # avoid throwing NotImplementedError in tests if self.__class__ is not BaseParser: self.graph = self.parse(self.original_data) + def _get_url(self, url): + url = urlparse.urlparse(url) + if url.scheme in ['http', 'https']: + return self._get_http(url) + if url.scheme == 'telnet': + return self._get_telnet(url) + def __sub__(self, other): return diff(other, self) @@ -61,7 +78,6 @@ def to_python(self, data): * a JSON formatted string * a dict representing a JSON structure """ - data = self._retrieve_data(data) if isinstance(data, dict): return data elif isinstance(data, six.string_types): @@ -69,34 +85,8 @@ def to_python(self, data): try: return json.loads(data) except ValueError: - raise ConversionException('Could not recognize format', data=data) - else: - raise ConversionException('Could not recognize format', data=data) - - def _retrieve_data(self, data): - """ - if recognizes a URL or a path - tries to retrieve and returns data - otherwise will return data as is - """ - if isinstance(data, six.string_types): - # if it looks like URL - if '://' in data: - url = urlparse.urlparse(data) - if url.scheme in ['http', 'https']: - return self._get_http(url) - if url.scheme == 'telnet': - return self._get_telnet(url) - # if it looks like a path - elif True in [data.startswith('./'), - data.startswith('../'), - data.startswith('/'), - data.startswith('.\\'), - data.startswith('..\\'), - data.startswith('\\'), - data[1:3].startswith(':\\')]: - return self._get_file(data) - return data + pass + raise ConversionException('Could not recognize format', data=data) def _get_file(self, path): try: diff --git a/tests/test_base.py b/tests/test_base.py index f6f76ed..98cac64 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -26,88 +26,82 @@ def _load_contents(self, file): def test_version(self): get_version() + def test_no_data_supplied(self): + with self.assertRaises(ValueError): + BaseParser() + def test_parse_file(self): dir = os.path.dirname(os.path.realpath(__file__)) path = '{0}/static/olsr-2-links.json'.format(dir) - p = BaseParser(path) + p = BaseParser(file=path) self.assertIsInstance(p.original_data, dict) with self.assertRaises(TopologyRetrievalError): - BaseParser('../wrong.json') + BaseParser(file='../wrong.json') @responses.activate def test_parse_http(self): - responses.add( - responses.GET, - 'http://localhost:9090', - body=self._load_contents('tests/static/olsr-2-links.json') - ) - p = BaseParser('http://localhost:9090') + responses.add(responses.GET, 'http://localhost:9090', + body=self._load_contents('tests/static/olsr-2-links.json')) + p = BaseParser(url='http://localhost:9090') self.assertIsInstance(p.original_data, dict) @responses.activate def test_topology_retrieval_error_http_404(self): - responses.add( - responses.GET, - 'http://404.com', - body='not found', - status=404 - ) + responses.add(responses.GET, 'http://404.com', + body='not found', status=404) with self.assertRaises(TopologyRetrievalError): - BaseParser('http://404.com') + BaseParser(url='http://404.com') @responses.activate def test_topology_retrieval_error_http(self): def request_callback(request): raise ConnectionError('test exception') - responses.add_callback( - responses.GET, - 'http://connectionerror.com', - callback=request_callback, - ) + responses.add_callback(responses.GET, 'http://connectionerror.com', + callback=request_callback) with self.assertRaises(TopologyRetrievalError): - BaseParser('http://connectionerror.com') + BaseParser(url='http://connectionerror.com') @mock.patch('telnetlib.Telnet') def test_telnet_retrieval_error(self, MockClass): - telnetlib.Telnet.side_effect=ValueError('testing exception') + telnetlib.Telnet.side_effect = ValueError('testing exception') with self.assertRaises(TopologyRetrievalError): - BaseParser('telnet://wrong.com') + BaseParser(url='telnet://wrong.com') @mock.patch('telnetlib.Telnet') def test_telnet_retrieval(self, MockClass): with self.assertRaises(ConversionException): - BaseParser('telnet://127.0.0.1') + BaseParser(url='telnet://127.0.0.1') def test_topology_retrieval_error_file(self): with self.assertRaises(TopologyRetrievalError): - BaseParser('./tests/static/wrong.json') + BaseParser(file='./tests/static/wrong.json') def test_parse_json_string(self): - p = BaseParser('{}') + p = BaseParser(data='{}') self.assertIsInstance(p.original_data, dict) - p = BaseParser(u'{}') + p = BaseParser(data=u'{}') self.assertIsInstance(p.original_data, dict) def test_parse_dict(self): - p = BaseParser({}) + p = BaseParser(data={}) self.assertIsInstance(p.original_data, dict) def test_parse_conversion_exception(self): with self.assertRaises(ConversionException): - BaseParser('wrong [] ; .') + BaseParser(data='wrong [] ; .') def test_parse_error(self): with self.assertRaises(ConversionException): - BaseParser(8) + BaseParser(data=8) def test_parse_not_implemented(self): class MyParser(BaseParser): pass with self.assertRaises(NotImplementedError): - MyParser('{}') + MyParser(data='{}') def test_json_not_implemented(self): - p = BaseParser('{}') + p = BaseParser(data='{}') with self.assertRaises(NotImplementedError): p.json() diff --git a/tests/test_netjson.py b/tests/test_netjson.py index 958cc8b..d2a910b 100644 --- a/tests/test_netjson.py +++ b/tests/test_netjson.py @@ -9,8 +9,8 @@ CURRENT_DIR = os.path.dirname(os.path.realpath(__file__)) -links2 = '{0}/static/netjson-2-links.json'.format(CURRENT_DIR) -links3 = '{0}/static/netjson-3-links.json'.format(CURRENT_DIR) +links2 = open('{0}/static/netjson-2-links.json'.format(CURRENT_DIR)).read() +links3 = open('{0}/static/netjson-3-links.json'.format(CURRENT_DIR)).read() class TestNetJsonParser(TestCase): diff --git a/tests/test_olsr.py b/tests/test_olsr.py index 3f329f9..f603f7d 100644 --- a/tests/test_olsr.py +++ b/tests/test_olsr.py @@ -9,11 +9,11 @@ CURRENT_DIR = os.path.dirname(os.path.realpath(__file__)) -links2 = '{0}/static/olsr-2-links.json'.format(CURRENT_DIR) -links2_cost = '{0}/static/olsr-2-links-cost-changed.json'.format(CURRENT_DIR) -links3 = '{0}/static/olsr-3-links.json'.format(CURRENT_DIR) -links5 = '{0}/static/olsr-5-links.json'.format(CURRENT_DIR) -links5_cost = '{0}/static/olsr-5-links-cost-changed.json'.format(CURRENT_DIR) +links2 = open('{0}/static/olsr-2-links.json'.format(CURRENT_DIR)).read() +links2_cost = open('{0}/static/olsr-2-links-cost-changed.json'.format(CURRENT_DIR)).read() +links3 = open('{0}/static/olsr-3-links.json'.format(CURRENT_DIR)).read() +links5 = open('{0}/static/olsr-5-links.json'.format(CURRENT_DIR)).read() +links5_cost = open('{0}/static/olsr-5-links-cost-changed.json'.format(CURRENT_DIR)).read() class TestOlsrParser(TestCase): diff --git a/tests/test_olsr_txtinfo.py b/tests/test_olsr_txtinfo.py index 9fc014b..0227094 100644 --- a/tests/test_olsr_txtinfo.py +++ b/tests/test_olsr_txtinfo.py @@ -9,10 +9,10 @@ CURRENT_DIR = os.path.dirname(os.path.realpath(__file__)) -links2 = '{0}/static/olsr-2-links.txt'.format(CURRENT_DIR) -links2_cost = '{0}/static/olsr-2-links-cost-changed.txt'.format(CURRENT_DIR) -links3 = '{0}/static/olsr-3-links.txt'.format(CURRENT_DIR) -links5 = '{0}/static/olsr-5-links.txt'.format(CURRENT_DIR) +links2 = open('{0}/static/olsr-2-links.txt'.format(CURRENT_DIR)).read() +links2_cost = open('{0}/static/olsr-2-links-cost-changed.txt'.format(CURRENT_DIR)).read() +links3 = open('{0}/static/olsr-3-links.txt'.format(CURRENT_DIR)).read() +links5 = open('{0}/static/olsr-5-links.txt'.format(CURRENT_DIR)).read() class TestOlsrTxtinfoParser(TestCase): diff --git a/tests/test_utils.py b/tests/test_utils.py index cf5cbb8..f25b71d 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -6,7 +6,7 @@ CURRENT_DIR = os.path.dirname(os.path.realpath(__file__)) -links2 = '{0}/static/netjson-2-links.json'.format(CURRENT_DIR) +links2 = open('{0}/static/netjson-2-links.json'.format(CURRENT_DIR)).read() class TestUtils(TestCase):