Add linters.

This commit is contained in:
Jakub Gocławski 2024-05-28 15:35:00 +02:00
parent bd2aaa7559
commit e1a2eabc08
No known key found for this signature in database
GPG key ID: DB23479BBC13E2A3
22 changed files with 82 additions and 54 deletions

View file

@ -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']

View file

@ -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:

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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 {})

View file

@ -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()])

View file

@ -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.

View file

@ -2,7 +2,6 @@ import socket
from routeros_api import exceptions
LENGTH_MATRIX = [
(0x80, 0x0),
(0x40, 0x80),

View file

@ -1,4 +1,5 @@
import re
from routeros_api import exceptions

View file

@ -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

View file

@ -1,5 +1,6 @@
from routeros_api import utils
class BasicQuery(object):
operator = None

View file

@ -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(

View file

@ -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

View file

@ -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

View file

@ -1,5 +1,3 @@
import sys
from setuptools import find_packages
from setuptools import setup

View file

@ -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):

View file

@ -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)

View file

@ -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()}

View file

@ -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'])
[b'/interface/print', b'?name=wlan0', b'.tag=0'])

View file

@ -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)

13
tox.ini
View file

@ -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 .