diff --git a/routeros_api/__init__.py b/routeros_api/__init__.py index 712a3bc..5914cf6 100644 --- a/routeros_api/__init__.py +++ b/routeros_api/__init__.py @@ -1,6 +1,6 @@ -from routeros_api.api import connect -from routeros_api.api import RouterOsApiPool -from routeros_api import query from routeros_api import api_structure +from routeros_api import query +from routeros_api.api import RouterOsApiPool +from routeros_api.api import connect __all__ = ['connect', 'RouterOsApiPool', 'query', 'api_structure'] diff --git a/routeros_api/api.py b/routeros_api/api.py index ff7d99d..7ba3776 100644 --- a/routeros_api/api.py +++ b/routeros_api/api.py @@ -1,22 +1,27 @@ -import hashlib import binascii +import hashlib + from routeros_api import api_communicator -from routeros_api import communication_exception_parsers from routeros_api import api_socket from routeros_api import api_structure from routeros_api import base_api +from routeros_api import communication_exception_parsers from routeros_api import exceptions from routeros_api import resource -def connect(host, username='admin', password='', port=None, plaintext_login=False, use_ssl=False, ssl_verify=True, ssl_verify_hostname=True, ssl_context=None): - return RouterOsApiPool(host, username, password, port, plaintext_login, use_ssl, ssl_verify, ssl_verify_hostname, ssl_context).get_api() +def connect(host, username='admin', password='', port=None, plaintext_login=False, use_ssl=False, ssl_verify=True, + ssl_verify_hostname=True, ssl_context=None): + return RouterOsApiPool( + host, username, password, port, plaintext_login, use_ssl, ssl_verify, ssl_verify_hostname, ssl_context, + ).get_api() class RouterOsApiPool(object): socket_timeout = 15.0 - def __init__(self, host, username='admin', password='', port=None, plaintext_login=False, use_ssl=False, ssl_verify=True, ssl_verify_hostname=True, ssl_context=None): + def __init__(self, host, username='admin', password='', port=None, plaintext_login=False, use_ssl=False, + ssl_verify=True, ssl_verify_hostname=True, ssl_context=None): self.host = host self.username = username self.password = password @@ -41,8 +46,9 @@ class RouterOsApiPool(object): def get_api(self): if not self.connected: - self.socket = api_socket.get_socket(self.host, self.port, - timeout=self.socket_timeout, use_ssl=self.use_ssl, ssl_verify=self.ssl_verify, ssl_verify_hostname=self.ssl_verify_hostname, ssl_context=self.ssl_context) + self.socket = api_socket.get_socket( + self.host, self.port, timeout=self.socket_timeout, use_ssl=self.use_ssl, ssl_verify=self.ssl_verify, + ssl_verify_hostname=self.ssl_verify_hostname, ssl_context=self.ssl_context) base = base_api.Connection(self.socket) communicator = api_communicator.ApiCommunicator(base) self.api = RouterOsApi(communicator) @@ -83,7 +89,7 @@ class RouterOsApi(object): login = login.encode() if isinstance(password, str): password = password.encode() - response = self.get_binary_resource('/').call('login',{ 'name': login, 'password': password }) + response = self.get_binary_resource('/').call('login', {'name': login, 'password': password}) else: response = self.get_binary_resource('/').call('login') if 'ret' in response.done_message: diff --git a/routeros_api/api_communicator/__init__.py b/routeros_api/api_communicator/__init__.py index 073fa9b..c38250d 100644 --- a/routeros_api/api_communicator/__init__.py +++ b/routeros_api/api_communicator/__init__.py @@ -1,7 +1,7 @@ -from routeros_api.api_communicator import base from routeros_api.api_communicator import async_decorator -from routeros_api.api_communicator import exception_decorator +from routeros_api.api_communicator import base from routeros_api.api_communicator import encoding_decorator +from routeros_api.api_communicator import exception_decorator from routeros_api.api_communicator import key_cleaner_decorator diff --git a/routeros_api/api_communicator/async_decorator.py b/routeros_api/api_communicator/async_decorator.py index a29d7d7..b2ed211 100644 --- a/routeros_api/api_communicator/async_decorator.py +++ b/routeros_api/api_communicator/async_decorator.py @@ -6,6 +6,7 @@ class AsyncApiCommunicator(object): tag = self.inner.send(*args, **kwargs) return ResponsePromise(self.inner, tag) + class ResponsePromise(object): def __init__(self, receiver, tag): self.receiver = receiver diff --git a/routeros_api/api_communicator/base.py b/routeros_api/api_communicator/base.py index a620dea..9d2501d 100644 --- a/routeros_api/api_communicator/base.py +++ b/routeros_api/api_communicator/base.py @@ -1,6 +1,6 @@ from routeros_api import exceptions -from routeros_api import sentence from routeros_api import query +from routeros_api import sentence class ApiCommunicatorBase(object): @@ -9,17 +9,14 @@ class ApiCommunicatorBase(object): self.tag = 0 self.response_buffor = {} - def send(self, path, command, arguments=None, queries=None, - additional_queries=()): + def send(self, path, command, arguments=None, queries=None, additional_queries=()): tag = self._get_next_tag() - command = self.get_command(path, command, arguments, queries, tag=tag, - additional_queries=additional_queries) + command = self.get_command(path, command, arguments, queries, tag=tag, additional_queries=additional_queries) self.send_command(command) self.response_buffor[tag] = AsynchronousResponse(command=command) return tag - def get_command(self, path, command, arguments=None, queries=None, - tag=None, additional_queries=()): + def get_command(self, path, command, arguments=None, queries=None, tag=None, additional_queries=()): arguments = arguments or {} queries = queries or {} command = sentence.CommandSentence(path, command, tag=tag) @@ -40,7 +37,7 @@ class ApiCommunicatorBase(object): def receive(self, tag): response_buffor_manager = AsynchronousResponseBufforManager(self, tag) - while(not response_buffor_manager.done): + while not response_buffor_manager.done: response_buffor_manager.step_to_finish_response() response_buffor_manager.clean() response = response_buffor_manager.response @@ -84,7 +81,7 @@ class SingleResponse(object): elif self.response.type == b'trap': asynchronous_response.error = self.response.attributes[b'message'] elif self.response.type == b'fatal': - del(buffor[self.response.tag]) + del (buffor[self.response.tag]) message = "Fatal error executing command {command}".format( command=asynchronous_response.command) raise exceptions.RouterOsApiFatalCommunicationError(message) @@ -132,7 +129,7 @@ class AsynchronousResponseBufforManager(object): return self.response.done def clean(self): - del(self.receiver.response_buffor[self.tag]) + del (self.receiver.response_buffor[self.tag]) class AsynchronousResponse(list): @@ -154,7 +151,6 @@ class AsynchronousResponse(list): else: return None - def map(self, function): result = type(self)(map(function, self), command=self.command) result.done_message = function(self.done_message) diff --git a/routeros_api/api_communicator/encoding_decorator.py b/routeros_api/api_communicator/encoding_decorator.py index 757a1f8..e9dd1bf 100644 --- a/routeros_api/api_communicator/encoding_decorator.py +++ b/routeros_api/api_communicator/encoding_decorator.py @@ -2,8 +2,7 @@ class EncodingApiCommunicator(object): def __init__(self, inner): self.inner = inner - def call(self, path, command, arguments=None, queries=None, - additional_queries=()): + def call(self, path, command, arguments=None, queries=None, additional_queries=()): path = path.encode() command = command.encode() arguments = self.transform_dictionary(arguments or {}) diff --git a/routeros_api/api_communicator/key_cleaner_decorator.py b/routeros_api/api_communicator/key_cleaner_decorator.py index e8eba7c..b9c9e4b 100644 --- a/routeros_api/api_communicator/key_cleaner_decorator.py +++ b/routeros_api/api_communicator/key_cleaner_decorator.py @@ -31,6 +31,7 @@ def encode_key(key): else: return key + def decode_dictionary(dictionary): return dict([(decode_key(key), value) for key, value in dictionary.items()]) diff --git a/routeros_api/api_socket.py b/routeros_api/api_socket.py index a1f28c2..a766ca6 100644 --- a/routeros_api/api_socket.py +++ b/routeros_api/api_socket.py @@ -1,6 +1,8 @@ import socket import ssl + from routeros_api import exceptions + try: import errno except ImportError: @@ -8,7 +10,9 @@ except ImportError: EINTR = getattr(errno, 'EINTR', 4) -def get_socket(hostname, port, use_ssl=False, ssl_verify=True, ssl_verify_hostname=True, ssl_context=None, timeout=15.0): + +def get_socket(hostname, port, use_ssl=False, ssl_verify=True, ssl_verify_hostname=True, ssl_context=None, + timeout=15.0): while True: try: api_socket = socket.create_connection((hostname, port), timeout=timeout) @@ -28,9 +32,10 @@ def get_socket(hostname, port, use_ssl=False, ssl_verify=True, ssl_verify_hostna ssl_context.check_hostname = False ssl_context.verify_mode = ssl.CERT_NONE if ssl_context is not None: - api_socket = ssl_context.wrap_socket(api_socket,server_hostname=hostname) + api_socket = ssl_context.wrap_socket(api_socket, server_hostname=hostname) return SocketWrapper(api_socket) + # http://stackoverflow.com/a/14855726 def set_keepalive(sock, after_idle_sec=1, interval_sec=3, max_fails=5): """Set TCP keepalive on an open socket. diff --git a/routeros_api/base_api.py b/routeros_api/base_api.py index 00a0f74..f860780 100644 --- a/routeros_api/base_api.py +++ b/routeros_api/base_api.py @@ -2,7 +2,6 @@ import socket from routeros_api import exceptions - LENGTH_MATRIX = [ (0x80, 0x0), (0x40, 0x80), diff --git a/routeros_api/communication_exception_parsers.py b/routeros_api/communication_exception_parsers.py index d779050..00b457d 100644 --- a/routeros_api/communication_exception_parsers.py +++ b/routeros_api/communication_exception_parsers.py @@ -1,4 +1,5 @@ import re + from routeros_api import exceptions diff --git a/routeros_api/exceptions.py b/routeros_api/exceptions.py index 2ef5f39..fe821cb 100644 --- a/routeros_api/exceptions.py +++ b/routeros_api/exceptions.py @@ -1,22 +1,28 @@ class RouterOsApiError(Exception): pass + class RouterOsApiConnectionError(RouterOsApiError): pass + class FatalRouterOsApiError(RouterOsApiError): pass + class RouterOsApiParsingError(RouterOsApiError): pass + class RouterOsApiCommunicationError(RouterOsApiError): def __init__(self, message, original_message): super(RouterOsApiCommunicationError, self).__init__(message, original_message) self.original_message = original_message + class RouterOsApiFatalCommunicationError(RouterOsApiError): pass + class RouterOsApiConnectionClosedError(RouterOsApiConnectionError): pass diff --git a/routeros_api/query.py b/routeros_api/query.py index 602d46a..188c8a0 100644 --- a/routeros_api/query.py +++ b/routeros_api/query.py @@ -1,5 +1,6 @@ from routeros_api import utils + class BasicQuery(object): operator = None diff --git a/routeros_api/resource.py b/routeros_api/resource.py index 0904635..09eb187 100644 --- a/routeros_api/resource.py +++ b/routeros_api/resource.py @@ -35,11 +35,11 @@ class RouterOsBinaryResource(object): def call(self, command, arguments=None, queries=None, additional_queries=()): - return self.call_async(command, arguments=arguments, queries=queries, - additional_queries=additional_queries).get() + return self.call_async( + command, arguments=arguments, queries=queries, additional_queries=additional_queries, + ).get() - def call_async(self, command, arguments=None, queries=None, - additional_queries=()): + def call_async(self, command, arguments=None, queries=None, additional_queries=()): return self.communicator.call( self.path, command, arguments=arguments, queries=queries, additional_queries=additional_queries) @@ -53,8 +53,7 @@ class RouterOsResource(RouterOsBinaryResource): self.structure = structure super(RouterOsResource, self).__init__(communicator, path) - def call_async(self, command, arguments=None, queries=None, - additional_queries=()): + def call_async(self, command, arguments=None, queries=None, additional_queries=()): arguments = self.transform_dictionary(arguments or {}) queries = self.transform_dictionary(queries or {}) promise = self.communicator.call( diff --git a/routeros_api/sentence.py b/routeros_api/sentence.py index 5df1a7c..28fdbd0 100644 --- a/routeros_api/sentence.py +++ b/routeros_api/sentence.py @@ -3,7 +3,6 @@ import re from routeros_api import exceptions from routeros_api import query - response_re = re.compile(rb'^!(re|trap|fatal|done)$') attribute_re = re.compile(rb'^=([^=]+)=(.*)$', re.DOTALL) tag_re = re.compile(rb'^\.tag=(.*)$') @@ -26,7 +25,6 @@ class ResponseSentence(object): sentence) return response - def parse_attributes(self, serialized_attributes): for serialized in serialized_attributes: attribute_match = attribute_re.match(serialized) @@ -57,8 +55,8 @@ class CommandSentence(object): formated = [self.path + self.command] for key, value in self.attributes.items(): formated.append(b'=' + key + b'=' + value) - for query in self.queries: - formated.extend(query.get_api_format()) + for _query in self.queries: + formated.extend(_query.get_api_format()) if self.tag is not None: formated.append(b'.tag=' + self.tag) return formated diff --git a/setup.cfg b/setup.cfg index d08cbd5..2bf7d9a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,3 +1,11 @@ +[flake8] +max-line-length = 120 + +[isort] +force_single_line = true +line_length = 120 +lines_between_types = 1 + [zest.releaser] create-wheel = yes diff --git a/setup.py b/setup.py index 94309af..a661fb7 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,3 @@ -import sys - from setuptools import find_packages from setuptools import setup diff --git a/tests/test_api_communicator.py b/tests/test_api_communicator.py index 6ec328c..4bee354 100644 --- a/tests/test_api_communicator.py +++ b/tests/test_api_communicator.py @@ -5,8 +5,9 @@ try: except ImportError: import mock -from routeros_api import exceptions from routeros_api import api_communicator +from routeros_api import exceptions + class TestCommunicator(TestCase): def test_login_call(self): diff --git a/tests/test_base_api.py b/tests/test_base_api.py index 38cc98b..d3127a4 100644 --- a/tests/test_base_api.py +++ b/tests/test_base_api.py @@ -93,14 +93,14 @@ class TestToBytes(TestCase): class TestConnection(TestCase): - def test_sending(self, ): + def test_sending(self): socket = mock.Mock() connection = base_api.Connection(socket) connection.send_sentence([b'foo', b'bar']) expected = [ mock.call(b'\x03foo'), mock.call(b'\x03bar'), - mock.call(b'\x00') + mock.call(b'\x00'), ] self.assertEqual(expected, socket.send.mock_calls) diff --git a/tests/test_resource.py b/tests/test_resource.py index 90ada5d..21d40c9 100644 --- a/tests/test_resource.py +++ b/tests/test_resource.py @@ -9,7 +9,6 @@ from routeros_api import api_structure as structure from routeros_api import resource from routeros_api.api_communicator import base - BYTES_STRUCTURE = {'bytes': structure.BytesField()} BOOLEAN_STRUCTURE = {'boolean': structure.BooleanField()} diff --git a/tests/test_sentence.py b/tests/test_sentence.py index 1ab67c2..5c52f7f 100644 --- a/tests/test_sentence.py +++ b/tests/test_sentence.py @@ -1,13 +1,9 @@ from unittest import TestCase -try: - from unittest import mock -except ImportError: - import mock - from routeros_api import exceptions from routeros_api import sentence + class TestResponseSentence(TestCase): def test_done(self): response = sentence.ResponseSentence.parse([b'!done']) @@ -34,6 +30,7 @@ class TestResponseSentence(TestCase): self.assertEqual(response.type, b'trap') self.assertEqual(response.attributes[b'message'], b'b') + class TestCommandSentence(TestCase): def test_login_sentence(self): command = sentence.CommandSentence(b'/', b'login') @@ -51,4 +48,4 @@ class TestCommandSentence(TestCase): command = sentence.CommandSentence(b'/interface/', b'print', tag=b'0') command.filter(name=b'wlan0') self.assertEqual(command.get_api_format(), - [b'/interface/print', b'?name=wlan0', b'.tag=0']) \ No newline at end of file + [b'/interface/print', b'?name=wlan0', b'.tag=0']) diff --git a/tests/test_socket.py b/tests/test_socket.py index ef82dce..94c47f9 100644 --- a/tests/test_socket.py +++ b/tests/test_socket.py @@ -1,4 +1,5 @@ import socket + from unittest import TestCase try: @@ -9,13 +10,14 @@ except ImportError: from routeros_api import api_socket from routeros_api import exceptions + class TestSocketWrapper(TestCase): def test_socket(self): inner = mock.Mock() wrapper = api_socket.SocketWrapper(inner) inner.recv.side_effect = [ socket.error(api_socket.EINTR), - 'bytes' + 'bytes', ] self.assertEqual(wrapper.receive(5), 'bytes') @@ -37,7 +39,7 @@ class TestGetSocket(TestCase): def test_with_other_error(self, create_connection_mock): create_connection_mock.side_effect = [ socket.error(1), - None + None, ] self.assertRaises(exceptions.RouterOsApiConnectionError, api_socket.get_socket, 'host', 123) diff --git a/tox.ini b/tox.ini index c6044c8..17bf4d4 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,18 @@ [tox] requires = tox>=4 -env_list = py{39,310,311,312} +env_list = lint, py{39,310,311,312} [testenv] description = run unit tests commands = python -m unittest + +[testenv:lint] +description = run linters +skip_install = true +deps = + flake8 + flake8-commas + isort +commands = + flake8 + isort --check --diff .