mirror of
https://github.com/socialwifi/RouterOS-api.git
synced 2025-08-31 07:09:48 +02:00
Add linters.
This commit is contained in:
parent
bd2aaa7559
commit
e1a2eabc08
22 changed files with 82 additions and 54 deletions
|
@ -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']
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {})
|
||||
|
|
|
@ -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()])
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -2,7 +2,6 @@ import socket
|
|||
|
||||
from routeros_api import exceptions
|
||||
|
||||
|
||||
LENGTH_MATRIX = [
|
||||
(0x80, 0x0),
|
||||
(0x40, 0x80),
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import re
|
||||
|
||||
from routeros_api import exceptions
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from routeros_api import utils
|
||||
|
||||
|
||||
class BasicQuery(object):
|
||||
operator = None
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
2
setup.py
2
setup.py
|
@ -1,5 +1,3 @@
|
|||
import sys
|
||||
|
||||
from setuptools import find_packages
|
||||
from setuptools import setup
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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()}
|
||||
|
||||
|
|
|
@ -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'])
|
||||
|
|
|
@ -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
13
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 .
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue