From 31d616bc943d780cbe26c3f32d62b68d8a4e51e9 Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Wed, 6 Mar 2019 15:49:27 -0500 Subject: [PATCH 1/4] Bumping version to 1.2.6 --- configure.ac | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 357671d..f2f98aa 100644 --- a/configure.ac +++ b/configure.ac @@ -10,7 +10,7 @@ # GNU General Public License for more details. define([VERSION_MAJOR], [1]) define([VERSION_MINOR], [2]) -define([VERSION_FIX], [5]) +define([VERSION_FIX], [6]) define([VERSION_NUMBER], VERSION_MAJOR[.]VERSION_MINOR[.]VERSION_FIX) define([VERSION_SUFFIX], [_master]) diff --git a/setup.py b/setup.py index 098870a..3b2c954 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ def readme(): setup(name='amcrest', - version='1.2.5', + version='1.2.6', description='Python wrapper implementation for Amcrest cameras.', long_description=readme(), author='Douglas Schilling Landgraf, Marcelo Moreira de Mello', From 44589697adf5b569c47331955c3ae603a075f223 Mon Sep 17 00:00:00 2001 From: Phil Bruckner Date: Sun, 10 Mar 2019 19:04:24 -0500 Subject: [PATCH 2/4] Use unique amcrest exceptions Use amcrest specific exceptions where appropriate, such as for communication and login errors. Use retries and timeouts when generating authentation object. Check for invalid login when using Basic Authentication. --- src/amcrest/__init__.py | 4 +- src/amcrest/exceptions.py | 13 ++++++ src/amcrest/http.py | 95 ++++++++++++++++++++------------------- 3 files changed, 64 insertions(+), 48 deletions(-) create mode 100644 src/amcrest/exceptions.py diff --git a/src/amcrest/__init__.py b/src/amcrest/__init__.py index b412c51..56b1ef4 100644 --- a/src/amcrest/__init__.py +++ b/src/amcrest/__init__.py @@ -10,8 +10,8 @@ # GNU General Public License for more details. # # vim:sw=4:ts=4:et - -from amcrest.http import Http +from .exceptions import * +from .http import Http class AmcrestCamera(object): diff --git a/src/amcrest/exceptions.py b/src/amcrest/exceptions.py new file mode 100644 index 0000000..81eb186 --- /dev/null +++ b/src/amcrest/exceptions.py @@ -0,0 +1,13 @@ +""" +amcrest.exceptions + +This module contains the set of amcrest's exceptions. +""" +class AmcrestError(Exception): + """General Amcrest error occurred.""" + +class CommError(AmcrestError): + """A communication error occurred.""" + +class LoginError(AmcrestError): + """A login error occurred.""" diff --git a/src/amcrest/http.py b/src/amcrest/http.py index ff4cdc1..4806388 100644 --- a/src/amcrest/http.py +++ b/src/amcrest/http.py @@ -11,27 +11,29 @@ # # vim:sw=4:ts=4:et import logging + import requests from requests.adapters import HTTPAdapter -from amcrest.utils import clean_url, pretty - -from amcrest.audio import Audio -from amcrest.event import Event -from amcrest.log import Log -from amcrest.motion_detection import MotionDetection -from amcrest.nas import Nas -from amcrest.network import Network -from amcrest.ptz import Ptz -from amcrest.record import Record -from amcrest.snapshot import Snapshot -from amcrest.special import Special -from amcrest.storage import Storage -from amcrest.system import System -from amcrest.user_management import UserManagement -from amcrest.video import Video - -from amcrest.config import TIMEOUT_HTTP_PROTOCOL, MAX_RETRY_HTTP_CONNECTION +from .exceptions import * +from .utils import clean_url, pretty + +from .audio import Audio +from .event import Event +from .log import Log +from .motion_detection import MotionDetection +from .nas import Nas +from .network import Network +from .ptz import Ptz +from .record import Record +from .snapshot import Snapshot +from .special import Special +from .storage import Storage +from .system import System +from .user_management import UserManagement +from .video import Video + +from .config import TIMEOUT_HTTP_PROTOCOL, MAX_RETRY_HTTP_CONNECTION _LOGGER = logging.getLogger(__name__) @@ -60,32 +62,40 @@ def __init__(self, host, port, user, self._token = self._generate_token() self._set_name() - def _generate_token(self): - """Discover which authentication method to use. + def get_session(self, max_retries=None): + session = requests.Session() + max_retries = max_retries or self._retries_default + adapter = HTTPAdapter(max_retries=max_retries) + session.mount('http://', adapter) + session.mount('https://', adapter) + return session - Latest firmwares requires HttpDigestAuth - Older firmwares requires HttpBasicAuth - """ + def _generate_token(self): + """Create authentation to use with requests.""" + session = self.get_session() url = self.__base_url('magicBox.cgi?action=getMachineName') try: - # try old firmware first to force 401 if fails - self._authentication = 'basic' + # try old basic method auth = requests.auth.HTTPBasicAuth(self._user, self._password) - req = requests.get(url, auth=auth) + req = session.get(url, auth=auth, timeout=self._timeout_default) + if not req.ok: + # try new digest method + auth = requests.auth.HTTPDigestAuth( + self._user, self._password) + req = session.get( + url, auth=auth, timeout=self._timeout_default) req.raise_for_status() + except requests.RequestException as error: + _LOGGER.error(error) + raise CommError('Could not communicate with camera') - except requests.HTTPError: - # if 401, then try new digest method - self._authentication = 'digest' - auth = requests.auth.HTTPDigestAuth(self._user, self._password) - req = requests.get(url, auth=auth) - req.raise_for_status() + # check if user passed + result = req.text.lower() + if 'invalid' in result or 'error' in result: + _LOGGER.error('Result from camera: %s', + req.text.strip().replace('\r\n', ': ')) + raise LoginError('Invalid credentials') - # check if user passed - if 'invalid' in req.text.lower() or \ - 'error' in req.text.lower(): - _LOGGER.info("%s Invalid credentials", req.text) - raise return auth def _set_name(self): @@ -117,13 +127,6 @@ def __base_url(self, param=""): def get_base_url(self): return self._base_url - def get_session(self, max_retries): - session = requests.Session() - adapter = HTTPAdapter(max_retries=max_retries) - session.mount('http://', adapter) - session.mount('https://', adapter) - return session - def command(self, cmd, retries=None, timeout_cmd=None): """ Args: @@ -147,13 +150,13 @@ def command(self, cmd, retries=None, timeout_cmd=None): ) resp.raise_for_status() break - except requests.HTTPError as error: + except requests.RequestException as error: if loop <= retries: _LOGGER.warning("Trying again due to error %s", error) continue else: _LOGGER.error("Query failed due to error %s", error) - raise + raise CommError('Could not communicate with camera') _LOGGER.debug("Query worked. Exit code: <%s>", resp.status_code) return resp From b5f2f4f0d30f46892840c2a209f895419c2b3855 Mon Sep 17 00:00:00 2001 From: Phil Bruckner Date: Sun, 10 Mar 2019 19:22:43 -0500 Subject: [PATCH 3/4] Fix houndci-bot errors --- src/amcrest/__init__.py | 2 +- src/amcrest/exceptions.py | 2 ++ src/amcrest/http.py | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/amcrest/__init__.py b/src/amcrest/__init__.py index 56b1ef4..cb62306 100644 --- a/src/amcrest/__init__.py +++ b/src/amcrest/__init__.py @@ -10,7 +10,7 @@ # GNU General Public License for more details. # # vim:sw=4:ts=4:et -from .exceptions import * +from .exceptions import AmcrestError, CommError, LoginError from .http import Http diff --git a/src/amcrest/exceptions.py b/src/amcrest/exceptions.py index 81eb186..863bbde 100644 --- a/src/amcrest/exceptions.py +++ b/src/amcrest/exceptions.py @@ -6,8 +6,10 @@ class AmcrestError(Exception): """General Amcrest error occurred.""" + class CommError(AmcrestError): """A communication error occurred.""" + class LoginError(AmcrestError): """A login error occurred.""" diff --git a/src/amcrest/http.py b/src/amcrest/http.py index 4806388..64c5ca2 100644 --- a/src/amcrest/http.py +++ b/src/amcrest/http.py @@ -15,7 +15,7 @@ import requests from requests.adapters import HTTPAdapter -from .exceptions import * +from .exceptions import CommError, LoginError from .utils import clean_url, pretty from .audio import Audio From 35db00c406e7ea8af584892f04a38632ab3b8527 Mon Sep 17 00:00:00 2001 From: Phil Bruckner Date: Wed, 13 Mar 2019 17:10:12 -0500 Subject: [PATCH 4/4] Fix check failures (#110) --- pylintrc | 3 ++- src/amcrest/__init__.py | 2 +- src/amcrest/exceptions.py | 2 ++ src/amcrest/http.py | 4 ++-- tui/amcrest-tui | 7 ++----- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pylintrc b/pylintrc index 25bf6bb..7b830b7 100644 --- a/pylintrc +++ b/pylintrc @@ -36,7 +36,8 @@ disable= too-many-statements, too-many-lines, too-few-public-methods, - abstract-method + abstract-method, + useless-object-inheritance [EXCEPTIONS] overgeneral-exceptions=Exception diff --git a/src/amcrest/__init__.py b/src/amcrest/__init__.py index cb62306..1d42c17 100644 --- a/src/amcrest/__init__.py +++ b/src/amcrest/__init__.py @@ -10,7 +10,7 @@ # GNU General Public License for more details. # # vim:sw=4:ts=4:et -from .exceptions import AmcrestError, CommError, LoginError +from .exceptions import AmcrestError, CommError, LoginError # noqa: F401 from .http import Http diff --git a/src/amcrest/exceptions.py b/src/amcrest/exceptions.py index 863bbde..27a9a73 100644 --- a/src/amcrest/exceptions.py +++ b/src/amcrest/exceptions.py @@ -3,6 +3,8 @@ This module contains the set of amcrest's exceptions. """ + + class AmcrestError(Exception): """General Amcrest error occurred.""" diff --git a/src/amcrest/http.py b/src/amcrest/http.py index 64c5ca2..0430db9 100644 --- a/src/amcrest/http.py +++ b/src/amcrest/http.py @@ -55,8 +55,8 @@ def __init__(self, host, port, user, self._protocol = protocol self._base_url = self.__base_url() - self._retries_default = (retries_connection or - MAX_RETRY_HTTP_CONNECTION) + self._retries_default = ( + retries_connection or MAX_RETRY_HTTP_CONNECTION) self._timeout_default = timeout_protocol or TIMEOUT_HTTP_PROTOCOL self._token = self._generate_token() diff --git a/tui/amcrest-tui b/tui/amcrest-tui index a16f3cf..226f794 100755 --- a/tui/amcrest-tui +++ b/tui/amcrest-tui @@ -200,11 +200,8 @@ if __name__ == "__main__": args = parser.parse_args() # Check if we have permission to framebuffer device - try: - with open(args.framebuffer_device, 'r') as f: - pass - except Exception: - raise + with open(args.framebuffer_device, 'r') as f: + pass # pylint: disable=fixme # FIXME: Add option to save the snapshots in the disk