From c3125fb1e15158da695678fc93e3b6d3d951589d Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Sat, 16 Jun 2018 02:04:08 -0400 Subject: [PATCH 1/8] Bump version to 1.2.4 --- configure.ac | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 80b8306..ff12176 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], [3]) +define([VERSION_FIX], [4]) define([VERSION_NUMBER], VERSION_MAJOR[.]VERSION_MINOR[.]VERSION_FIX) define([VERSION_SUFFIX], [_master]) diff --git a/setup.py b/setup.py index 79257c7..f793e5e 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ def readme(): setup(name='amcrest', - version='1.2.3', + version='1.2.4', description='Python wrapper implementation for Amcrest cameras.', long_description=readme(), author='Douglas Schilling Landgraf, Marcelo Moreira de Mello', From 910e6a8b5a7512d759ca7e166e082779fea803da Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Wed, 27 Jun 2018 13:38:49 -0400 Subject: [PATCH 2/8] Added IP3M-941W --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 982f603..b8332a8 100644 --- a/README.rst +++ b/README.rst @@ -144,7 +144,7 @@ Supportability Matrix +-------------------------+---------------+----------+-----------------+ | IPM-HX1B | Yes | working | | +-------------------------+---------------+----------+-----------------+ -| IP3M-941 | Yes | working | | +| IP3M-941/941W | Yes | working | | +-------------------------+---------------+----------+-----------------+ | IP3M-HX2 | Yes (partial) | working | | +-------------------------+---------------+----------+-----------------+ From 1bfcc1ea408635caa56438b6d9d608ce39db139a Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Tue, 1 Jan 2019 20:22:12 -0500 Subject: [PATCH 3/8] Fixed typo report on issue #100 --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index b8332a8..b94ebb7 100644 --- a/README.rst +++ b/README.rst @@ -66,7 +66,7 @@ Usage CTRL-C to stop the continuous audio flow or use a timer #Move camera down - camera.ptz_control_command(action="start", code="Down", arg1=0, arg2=0, arg3=0))) + camera.ptz_control_command(action="start", code="Down", arg1=0, arg2=0, arg3=0) #Record realtime stream into a file camera.realtime_stream(path_file="/home/user/Desktop/myvideo") From 488088d27f888b822e87c127a6963328d63d07de Mon Sep 17 00:00:00 2001 From: Phil Bruckner Date: Fri, 1 Mar 2019 14:12:59 -0600 Subject: [PATCH 4/8] Fix command error handling after last retry If last retry resulted in an error the command method did not raise an exception, but rather logged, e.g., "Trying again due error 401" followed by "Query worked. Exit code: 401". Now if all attempts, including all retries, result in errors, an exception will be raised. Also changed log messages for request errors to warnings, except for on last retry, which is logged as an error. --- src/amcrest/http.py | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/amcrest/http.py b/src/amcrest/http.py index 141c87e..e49d385 100644 --- a/src/amcrest/http.py +++ b/src/amcrest/http.py @@ -142,10 +142,7 @@ def command(self, cmd, retries=None, timeout_cmd=None): session.mount('https://', HTTPAdapter(max_retries=self._retries_conn)) url = self.__base_url(cmd) - resp = None - loop = 0 - while loop <= self._retries_conn: - loop += 1 + for loop in range(1, 2 + self._retries_conn): _LOGGER.debug("Running query attempt %s", loop) try: resp = session.get( @@ -157,16 +154,12 @@ def command(self, cmd, retries=None, timeout_cmd=None): resp.raise_for_status() break except requests.HTTPError as error: - _LOGGER.debug("Trying again due error %s", error) - continue - - # keep the loop when 401 - if resp.status_code == 401: - continue - - # if we got here, let's raise because it did not work - if resp is None: - raise ValueError('Something went wrong!!!!') + if loop <= self._retries_conn: + _LOGGER.warning("Trying again due to error %s", error) + continue + else: + _LOGGER.error("Query failed due to error %s", error) + raise _LOGGER.debug("Query worked. Exit code: <%s>", resp.status_code) return resp From 4d4d6980b37067fceec01384e75d33b9d7137a47 Mon Sep 17 00:00:00 2001 From: Phil Bruckner Date: Sat, 2 Mar 2019 11:52:43 -0600 Subject: [PATCH 5/8] Reuse requests session in command if possible --- src/amcrest/http.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/amcrest/http.py b/src/amcrest/http.py index e49d385..8958983 100644 --- a/src/amcrest/http.py +++ b/src/amcrest/http.py @@ -59,14 +59,19 @@ def __init__(self, host, port, user, else: self._timeout_protocol = timeout_protocol - if retries_connection is None: - self._retries_conn = MAX_RETRY_HTTP_CONNECTION - else: - self._retries_conn = retries_connection + self._retries_conn = None + self._set_command_session(retries_connection or MAX_RETRY_HTTP_CONNECTION) self._token = self._generate_token() self._set_name() + def _set_command_session(self, max_retries): + if max_retries is not None and self._retries_conn != max_retries: + self._retries_conn = max_retries + self._session = requests.Session() + self._session.mount('http://', HTTPAdapter(max_retries=max_retries)) + self._session.mount('https://', HTTPAdapter(max_retries=max_retries)) + def _generate_token(self): """Discover which authentication method to use. @@ -134,18 +139,13 @@ def command(self, cmd, retries=None, timeout_cmd=None): if timeout_cmd is not None: self._timeout_protocol = timeout_cmd - if retries is not None: - self._retries_conn = retries - - session = requests.Session() - session.mount('http://', HTTPAdapter(max_retries=self._retries_conn)) - session.mount('https://', HTTPAdapter(max_retries=self._retries_conn)) + self._set_command_session(retries) url = self.__base_url(cmd) for loop in range(1, 2 + self._retries_conn): _LOGGER.debug("Running query attempt %s", loop) try: - resp = session.get( + resp = self._session.get( url, auth=self._token, stream=True, From b503845d41653202a357ccb5fbbd499316469861 Mon Sep 17 00:00:00 2001 From: Phil Bruckner Date: Sat, 2 Mar 2019 12:00:28 -0600 Subject: [PATCH 6/8] Fix houndci-bot errors --- src/amcrest/http.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/amcrest/http.py b/src/amcrest/http.py index 8958983..c605f08 100644 --- a/src/amcrest/http.py +++ b/src/amcrest/http.py @@ -60,7 +60,8 @@ def __init__(self, host, port, user, self._timeout_protocol = timeout_protocol self._retries_conn = None - self._set_command_session(retries_connection or MAX_RETRY_HTTP_CONNECTION) + self._set_command_session( + retries_connection or MAX_RETRY_HTTP_CONNECTION) self._token = self._generate_token() self._set_name() @@ -69,8 +70,10 @@ def _set_command_session(self, max_retries): if max_retries is not None and self._retries_conn != max_retries: self._retries_conn = max_retries self._session = requests.Session() - self._session.mount('http://', HTTPAdapter(max_retries=max_retries)) - self._session.mount('https://', HTTPAdapter(max_retries=max_retries)) + self._session.mount( + 'http://', HTTPAdapter(max_retries=max_retries)) + self._session.mount( + 'https://', HTTPAdapter(max_retries=max_retries)) def _generate_token(self): """Discover which authentication method to use. From be113c78736afc2f1e3cee7ea4f7eb7fc8db968a Mon Sep 17 00:00:00 2001 From: Phil Bruckner Date: Sat, 2 Mar 2019 22:49:29 -0600 Subject: [PATCH 7/8] Keep session per max_retries Create a dictionary of sessions, with max_retries as key, so they can be reused once created. Also _retries_conn and _timeout_protocol usage was not thread safe; fix that. --- src/amcrest/http.py | 51 ++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/src/amcrest/http.py b/src/amcrest/http.py index c605f08..80c1617 100644 --- a/src/amcrest/http.py +++ b/src/amcrest/http.py @@ -12,8 +12,8 @@ # vim:sw=4:ts=4:et import logging import requests - from requests.adapters import HTTPAdapter +import threading from amcrest.utils import clean_url, pretty @@ -54,27 +54,16 @@ def __init__(self, host, port, user, self._protocol = protocol self._base_url = self.__base_url() - if timeout_protocol is None: - self._timeout_protocol = TIMEOUT_HTTP_PROTOCOL - else: - self._timeout_protocol = timeout_protocol + self._retries_default = (retries_connection or + MAX_RETRY_HTTP_CONNECTION) + self._timeout_default = timeout_protocol or TIMEOUT_HTTP_PROTOCOL - self._retries_conn = None - self._set_command_session( - retries_connection or MAX_RETRY_HTTP_CONNECTION) + self._session = {} + self._get_session_lock = threading.Lock() self._token = self._generate_token() self._set_name() - def _set_command_session(self, max_retries): - if max_retries is not None and self._retries_conn != max_retries: - self._retries_conn = max_retries - self._session = requests.Session() - self._session.mount( - 'http://', HTTPAdapter(max_retries=max_retries)) - self._session.mount( - 'https://', HTTPAdapter(max_retries=max_retries)) - def _generate_token(self): """Discover which authentication method to use. @@ -132,6 +121,18 @@ def __base_url(self, param=""): def get_base_url(self): return self._base_url + def _get_session(self, max_retries): + with self._get_session_lock: + try: + return self._session[max_retries] + except KeyError: + session = requests.Session() + adapter = HTTPAdapter(max_retries=max_retries) + session.mount('http://', adapter) + session.mount('https://', adapter) + self._session[max_retries] = session + return session + def command(self, cmd, retries=None, timeout_cmd=None): """ Args: @@ -139,20 +140,19 @@ def command(self, cmd, retries=None, timeout_cmd=None): timeout_cmd - timeout, default 3sec retries - maximum number of retries each connection should attempt """ - if timeout_cmd is not None: - self._timeout_protocol = timeout_cmd - - self._set_command_session(retries) + retries = retries or self._retries_default + timeout = timeout_cmd or self._timeout_default + session = self._get_session(retries) url = self.__base_url(cmd) - for loop in range(1, 2 + self._retries_conn): + for loop in range(1, 2 + retries): _LOGGER.debug("Running query attempt %s", loop) try: - resp = self._session.get( + resp = session.get( url, auth=self._token, stream=True, - timeout=self._timeout_protocol, + timeout=timeout, ) resp.raise_for_status() break @@ -171,8 +171,7 @@ def command_audio(self, cmd, file_content, http_header, timeout=None): url = self.__base_url(cmd) - if timeout is not None: - timeout = self._timeout_protocol + timeout = timeout or self._timeout_default try: requests.post( From 5196954861e1c2db190127c9f6302f9a247756fa Mon Sep 17 00:00:00 2001 From: Phil Bruckner Date: Sun, 3 Mar 2019 07:42:10 -0600 Subject: [PATCH 8/8] Fix typo Missed changing one occurence of self._retries_conn to retries in command method. --- src/amcrest/http.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/amcrest/http.py b/src/amcrest/http.py index 80c1617..b6bf7c5 100644 --- a/src/amcrest/http.py +++ b/src/amcrest/http.py @@ -157,7 +157,7 @@ def command(self, cmd, retries=None, timeout_cmd=None): resp.raise_for_status() break except requests.HTTPError as error: - if loop <= self._retries_conn: + if loop <= retries: _LOGGER.warning("Trying again due to error %s", error) continue else: