Remove dependency on requests
This commit is contained in:
parent
d7f7528a1d
commit
01b81bd8d7
@ -9,7 +9,7 @@ test:mipy:
|
|||||||
image: python:3.7
|
image: python:3.7
|
||||||
interruptible: true
|
interruptible: true
|
||||||
script:
|
script:
|
||||||
- 'pip install --upgrade mypy pytest types-requests'
|
- 'pip install --upgrade mypy pytest'
|
||||||
- 'pip install --upgrade -r requirements.txt'
|
- 'pip install --upgrade -r requirements.txt'
|
||||||
- 'mypy updater'
|
- 'mypy updater'
|
||||||
only:
|
only:
|
||||||
@ -25,7 +25,7 @@ test:pytest:
|
|||||||
image: python:3.8
|
image: python:3.8
|
||||||
interruptible: true
|
interruptible: true
|
||||||
script:
|
script:
|
||||||
- 'pip install --upgrade pytest coverage requests_mock'
|
- 'pip install --upgrade pytest coverage'
|
||||||
- 'pip install --upgrade -r requirements.txt'
|
- 'pip install --upgrade -r requirements.txt'
|
||||||
- 'coverage run --source . --omit conf.py -m pytest --junitxml=report.xml'
|
- 'coverage run --source . --omit conf.py -m pytest --junitxml=report.xml'
|
||||||
- 'coverage report'
|
- 'coverage report'
|
||||||
|
@ -1,2 +1 @@
|
|||||||
pypubsub
|
pypubsub
|
||||||
requests
|
|
@ -1,10 +1,9 @@
|
|||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
import pytest
|
import pytest
|
||||||
import requests_mock
|
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
from json.decoder import JSONDecodeError
|
from json.decoder import JSONDecodeError
|
||||||
from requests.exceptions import HTTPError
|
from urllib.error import HTTPError
|
||||||
from updater import core
|
from updater import core
|
||||||
|
|
||||||
app_name: str = "a simple app"
|
app_name: str = "a simple app"
|
||||||
@ -17,45 +16,36 @@ win32api = mock.Mock(name="win32api")
|
|||||||
win32api.__name__ = "win32api"
|
win32api.__name__ = "win32api"
|
||||||
sys.modules["win32api"] = win32api
|
sys.modules["win32api"] = win32api
|
||||||
|
|
||||||
def test_requests_session():
|
|
||||||
global app_name, current_version, endpoint
|
|
||||||
updater = core.UpdaterCore(endpoint=endpoint, app_name=app_name, current_version=current_version)
|
|
||||||
updater.create_session()
|
|
||||||
assert hasattr(updater, "session")
|
|
||||||
assert app_name in updater.session.headers.get("User-Agent")
|
|
||||||
assert current_version in updater.session.headers.get("User-Agent")
|
|
||||||
|
|
||||||
def test_get_update_information_valid_json(file_data, json_data):
|
def test_get_update_information_valid_json(file_data, json_data):
|
||||||
global app_name, current_version, endpoint
|
global app_name, current_version, endpoint
|
||||||
updater = core.UpdaterCore(endpoint=endpoint, app_name=app_name, current_version=current_version)
|
updater = core.UpdaterCore(endpoint=endpoint, app_name=app_name, current_version=current_version)
|
||||||
updater.create_session()
|
response = mock.Mock()
|
||||||
with requests_mock.Mocker(session=updater.session) as mocked:
|
response.getcode.return_value = 200
|
||||||
mocked.get(endpoint, json=json_data)
|
response.read.return_value = file_data
|
||||||
|
with mock.patch("urllib.request.urlopen", return_value=response):
|
||||||
contents = updater.get_update_information()
|
contents = updater.get_update_information()
|
||||||
assert contents == json_data
|
assert contents == json_data
|
||||||
|
|
||||||
def test_get_update_information_invalid_json(file_data, json_data):
|
def test_get_update_information_invalid_json(file_data, json_data):
|
||||||
global app_name, current_version, endpoint
|
global app_name, current_version, endpoint
|
||||||
updater = core.UpdaterCore(endpoint=endpoint, app_name=app_name, current_version=current_version)
|
updater = core.UpdaterCore(endpoint=endpoint, app_name=app_name, current_version=current_version)
|
||||||
updater.create_session()
|
response = mock.Mock()
|
||||||
with requests_mock.Mocker(session=updater.session) as mocked:
|
response.getcode.return_value = 200
|
||||||
mocked.get(endpoint, text="thisisnotjson")
|
response.read.return_value = "invalid json"
|
||||||
|
with mock.patch("urllib.request.urlopen", return_value=response):
|
||||||
with pytest.raises(JSONDecodeError):
|
with pytest.raises(JSONDecodeError):
|
||||||
contents = updater.get_update_information()
|
contents = updater.get_update_information()
|
||||||
|
|
||||||
def test_get_update_information_not_found():
|
def test_get_update_information_not_found():
|
||||||
global app_name, current_version, endpoint
|
global app_name, current_version, endpoint
|
||||||
updater = core.UpdaterCore(endpoint=endpoint, app_name=app_name, current_version=current_version)
|
updater = core.UpdaterCore(endpoint=endpoint, app_name=app_name, current_version=current_version)
|
||||||
updater.create_session()
|
with mock.patch("urllib.request.urlopen", side_effect=HTTPError(updater.endpoint, 404, "not found", None, None)):
|
||||||
with requests_mock.Mocker(session=updater.session) as mocked:
|
|
||||||
mocked.get(endpoint, status_code=404)
|
|
||||||
with pytest.raises(HTTPError):
|
with pytest.raises(HTTPError):
|
||||||
contents = updater.get_update_information()
|
contents = updater.get_update_information()
|
||||||
|
|
||||||
def test_version_data_no_update(json_data):
|
def test_version_data_no_update(json_data):
|
||||||
global app_name, endpoint
|
global app_name, endpoint
|
||||||
updater = core.UpdaterCore(endpoint=endpoint, app_name=app_name, current_version=json_data.get("current_version"))
|
updater = core.UpdaterCore(endpoint=endpoint, app_name=app_name, current_version=json_data.get("current_version"))
|
||||||
updater.create_session()
|
|
||||||
results = updater.get_version_data(json_data)
|
results = updater.get_version_data(json_data)
|
||||||
assert results == (False, False, False)
|
assert results == (False, False, False)
|
||||||
|
|
||||||
@ -66,7 +56,6 @@ def test_version_data_no_update(json_data):
|
|||||||
def test_version_data_update_available(json_data, platform, architecture):
|
def test_version_data_update_available(json_data, platform, architecture):
|
||||||
global app_name, current_version, endpoint
|
global app_name, current_version, endpoint
|
||||||
updater = core.UpdaterCore(endpoint=endpoint, app_name=app_name, current_version=current_version)
|
updater = core.UpdaterCore(endpoint=endpoint, app_name=app_name, current_version=current_version)
|
||||||
updater.create_session()
|
|
||||||
with mock.patch("platform.system", return_value=platform):
|
with mock.patch("platform.system", return_value=platform):
|
||||||
with mock.patch("platform.architecture", return_value=architecture):
|
with mock.patch("platform.architecture", return_value=architecture):
|
||||||
results = updater.get_version_data(json_data)
|
results = updater.get_version_data(json_data)
|
||||||
@ -76,25 +65,23 @@ def test_version_data_update_available(json_data, platform, architecture):
|
|||||||
def test_version_data_architecture_not_found(json_data):
|
def test_version_data_architecture_not_found(json_data):
|
||||||
global app_name, current_version, endpoint
|
global app_name, current_version, endpoint
|
||||||
updater = core.UpdaterCore(endpoint=endpoint, app_name=app_name, current_version=current_version)
|
updater = core.UpdaterCore(endpoint=endpoint, app_name=app_name, current_version=current_version)
|
||||||
updater.create_session()
|
|
||||||
with mock.patch("platform.system", return_value="nonos"):
|
with mock.patch("platform.system", return_value="nonos"):
|
||||||
with mock.patch("platform.architecture", return_value=("31bits", "")):
|
with mock.patch("platform.architecture", return_value=("31bits", "")):
|
||||||
with pytest.raises(KeyError):
|
with pytest.raises(KeyError):
|
||||||
results = updater.get_version_data(json_data)
|
results = updater.get_version_data(json_data)
|
||||||
|
|
||||||
|
def fake_download_function(url, destination, callback):
|
||||||
|
callback(0, 1024, 2048)
|
||||||
|
return ("file", "headers")
|
||||||
|
|
||||||
def test_download_update():
|
def test_download_update():
|
||||||
global app_name, current_version, endpoint
|
global app_name, current_version, endpoint
|
||||||
updater = core.UpdaterCore(endpoint=endpoint, app_name=app_name, current_version=current_version)
|
updater = core.UpdaterCore(endpoint=endpoint, app_name=app_name, current_version=current_version)
|
||||||
updater.create_session()
|
with mock.patch("pubsub.pub.sendMessage") as pub_sendMessage:
|
||||||
with mock.patch("pubsub.pub.sendMessage") as sendMessage_mock:
|
with mock.patch("urllib.request.urlretrieve", side_effect=fake_download_function):
|
||||||
with mock.patch("io.open") as open_mock:
|
result = updater.download_update(update_url="http://downloads.update.org/update.zip", update_destination="update.zip")
|
||||||
with requests_mock.Mocker(session=updater.session) as mocker:
|
assert result == "update.zip"
|
||||||
updatefile = os.path.join(os.path.dirname(__file__), "helloworld_v2_win64.zip")
|
pub_sendMessage.assert_called_once()
|
||||||
mocker.get("http://downloads.update.org/update.zip", body=open(updatefile, "rb"))
|
|
||||||
result = updater.download_update(update_url="http://downloads.update.org/update.zip", update_destination="update.zip")
|
|
||||||
open_mock.assert_called_once_with("update.zip", "w+b")
|
|
||||||
assert result == "update.zip"
|
|
||||||
assert sendMessage_mock.call_count > 0
|
|
||||||
|
|
||||||
def test_extract_archive():
|
def test_extract_archive():
|
||||||
# This only tests if archive extraction methods were called successfully and with the right parameters.
|
# This only tests if archive extraction methods were called successfully and with the right parameters.
|
||||||
|
@ -100,47 +100,43 @@ def test_on_update_almost_complete():
|
|||||||
@mock.patch("tempfile.mkdtemp", return_value="tmp")
|
@mock.patch("tempfile.mkdtemp", return_value="tmp")
|
||||||
def test_check_for_updates_update_available(tempfile):
|
def test_check_for_updates_update_available(tempfile):
|
||||||
updater = wxupdater.WXUpdater(endpoint="https://example.com/update.zip", app_name="My awesome application", current_version="0.1")
|
updater = wxupdater.WXUpdater(endpoint="https://example.com/update.zip", app_name="My awesome application", current_version="0.1")
|
||||||
with mock.patch.object(updater, "create_session") as create_session:
|
with mock.patch.object(updater, "initialize") as initialize:
|
||||||
with mock.patch.object(updater, "initialize") as initialize:
|
with mock.patch.object(updater, "get_update_information") as get_update_information:
|
||||||
with mock.patch.object(updater, "get_update_information") as get_update_information:
|
with mock.patch.object(updater, "get_version_data") as get_version_data:
|
||||||
with mock.patch.object(updater, "get_version_data") as get_version_data:
|
with mock.patch.object(updater, "on_new_update_available") as on_new_update_available:
|
||||||
with mock.patch.object(updater, "on_new_update_available") as on_new_update_available:
|
with mock.patch.object(updater, "download_update") as download_update:
|
||||||
with mock.patch.object(updater, "download_update") as download_update:
|
with mock.patch.object(updater, "extract_update") as extract_update:
|
||||||
with mock.patch.object(updater, "extract_update") as extract_update:
|
with mock.patch.object(updater, "move_bootstrap") as move_bootstrap:
|
||||||
with mock.patch.object(updater, "move_bootstrap") as move_bootstrap:
|
with mock.patch.object(updater, "on_update_almost_complete") as on_update_almost_complete:
|
||||||
with mock.patch.object(updater, "on_update_almost_complete") as on_update_almost_complete:
|
with mock.patch.object(updater, "execute_bootstrap") as execute_bootstrap:
|
||||||
with mock.patch.object(updater, "execute_bootstrap") as execute_bootstrap:
|
updater.check_for_updates()
|
||||||
updater.check_for_updates()
|
execute_bootstrap.assert_called_once()
|
||||||
execute_bootstrap.assert_called_once()
|
on_update_almost_complete.assert_called_once()
|
||||||
on_update_almost_complete.assert_called_once()
|
move_bootstrap.assert_called_once()
|
||||||
move_bootstrap.assert_called_once()
|
extract_update.assert_called_once()
|
||||||
extract_update.assert_called_once()
|
download_update.assert_called_once()
|
||||||
download_update.assert_called_once()
|
on_new_update_available.assert_called_once()
|
||||||
on_new_update_available.assert_called_once()
|
get_version_data.assert_called_once()
|
||||||
get_version_data.assert_called_once()
|
get_update_information.assert_called_once()
|
||||||
get_update_information.assert_called_once()
|
initialize.assert_called_once()
|
||||||
initialize.assert_called_once()
|
|
||||||
create_session.assert_called_once()
|
|
||||||
|
|
||||||
@mock.patch("tempfile.mkdtemp", return_value="tmp")
|
@mock.patch("tempfile.mkdtemp", return_value="tmp")
|
||||||
def test_check_for_updates_no_update_available(tempfile):
|
def test_check_for_updates_no_update_available(tempfile):
|
||||||
updater = wxupdater.WXUpdater(endpoint="https://example.com/update.zip", app_name="My awesome application", current_version="0.1")
|
updater = wxupdater.WXUpdater(endpoint="https://example.com/update.zip", app_name="My awesome application", current_version="0.1")
|
||||||
with mock.patch.object(updater, "create_session") as create_session:
|
with mock.patch.object(updater, "initialize") as initialize:
|
||||||
with mock.patch.object(updater, "initialize") as initialize:
|
with mock.patch.object(updater, "get_update_information") as update_information:
|
||||||
with mock.patch.object(updater, "get_update_information") as update_information:
|
with mock.patch.object(updater, "get_version_data", return_value=(False, False, False)) as get_version_data:
|
||||||
with mock.patch.object(updater, "get_version_data", return_value=(False, False, False)) as get_version_data:
|
result = updater.check_for_updates()
|
||||||
result = updater.check_for_updates()
|
assert result == None
|
||||||
assert result == None
|
get_version_data.assert_called_once()
|
||||||
get_version_data.assert_called_once()
|
|
||||||
|
|
||||||
@mock.patch("tempfile.mkdtemp", return_value="tmp")
|
@mock.patch("tempfile.mkdtemp", return_value="tmp")
|
||||||
def test_check_for_updates_user_cancelled_update(tempfile):
|
def test_check_for_updates_user_cancelled_update(tempfile):
|
||||||
updater = wxupdater.WXUpdater(endpoint="https://example.com/update.zip", app_name="My awesome application", current_version="0.1")
|
updater = wxupdater.WXUpdater(endpoint="https://example.com/update.zip", app_name="My awesome application", current_version="0.1")
|
||||||
with mock.patch.object(updater, "create_session") as create_session:
|
with mock.patch.object(updater, "initialize") as initialize:
|
||||||
with mock.patch.object(updater, "initialize") as initialize:
|
with mock.patch.object(updater, "get_update_information") as update_information:
|
||||||
with mock.patch.object(updater, "get_update_information") as update_information:
|
with mock.patch.object(updater, "get_version_data") as get_version_data:
|
||||||
with mock.patch.object(updater, "get_version_data") as get_version_data:
|
with mock.patch.object(updater, "on_new_update_available", return_value=False) as on_new_update_available:
|
||||||
with mock.patch.object(updater, "on_new_update_available", return_value=False) as on_new_update_available:
|
result = updater.check_for_updates()
|
||||||
result = updater.check_for_updates()
|
assert result == None
|
||||||
assert result == None
|
on_new_update_available.assert_called_once()
|
||||||
on_new_update_available.assert_called_once()
|
|
@ -9,7 +9,8 @@ import os
|
|||||||
import platform
|
import platform
|
||||||
import zipfile
|
import zipfile
|
||||||
import logging
|
import logging
|
||||||
import requests
|
import json
|
||||||
|
import urllib.request
|
||||||
from pubsub import pub # type: ignore
|
from pubsub import pub # type: ignore
|
||||||
from typing import Optional, Dict, Tuple, Union, Any
|
from typing import Optional, Dict, Tuple, Union, Any
|
||||||
from . import paths
|
from . import paths
|
||||||
@ -38,22 +39,16 @@ class UpdaterCore(object):
|
|||||||
self.app_name = app_name
|
self.app_name = app_name
|
||||||
self.password = password
|
self.password = password
|
||||||
|
|
||||||
def create_session(self) -> None:
|
|
||||||
""" Creates a requests session for calling update server. The session will add an user agent based in parameters passed to :py:class:`updater.core.updaterCore`'s constructor. """
|
|
||||||
user_agent: str = "%s/%s" % (self.app_name, self.current_version)
|
|
||||||
self.session: requests.Session = requests.Session()
|
|
||||||
self.session.headers['User-Agent'] = self.session.headers['User-Agent'] + user_agent
|
|
||||||
|
|
||||||
def get_update_information(self) -> Dict[str, Any]:
|
def get_update_information(self) -> Dict[str, Any]:
|
||||||
""" Calls the provided URL endpoint and returns information about the available update sent by the server. The format should adhere to the json specifications for updates.
|
""" Calls the provided URL endpoint and returns information about the available update sent by the server. The format should adhere to the json specifications for updates.
|
||||||
|
|
||||||
If the server returns a status code different to 200 or the json file is not valid, this will raise either a :external:py:exc:`requests.HTTPError`, :external:py:exc:`requests.RequestException` or a :external:py:exc:`json.JSONDecodeError`.
|
If the server returns a status code different to 200 or the json file is not valid, this will raise either a :py:exc:`urllib.error.HTTPError` or a :external:py:exc:`json.JSONDecodeError`.
|
||||||
|
|
||||||
:rtype: dict
|
:rtype: dict
|
||||||
"""
|
"""
|
||||||
response: requests.Response = self.session.get(self.endpoint)
|
response = urllib.request.urlopen(self.endpoint)
|
||||||
response.raise_for_status()
|
data: str = response.read()
|
||||||
content: Dict[str, Any] = response.json()
|
content: Dict[str, Any] = json.loads(data)
|
||||||
return content
|
return content
|
||||||
|
|
||||||
def get_version_data(self, content: Dict[str, Any]) -> Tuple[Union[bool, str], Union[bool, str], Union[bool, str]]:
|
def get_version_data(self, content: Dict[str, Any]) -> Tuple[Union[bool, str], Union[bool, str], Union[bool, str]]:
|
||||||
@ -99,17 +94,11 @@ class UpdaterCore(object):
|
|||||||
:returns: The update file path in the system.
|
:returns: The update file path in the system.
|
||||||
:rtype: str
|
:rtype: str
|
||||||
"""
|
"""
|
||||||
total_downloaded: int = 0
|
def _download_callback(transferred_blocks, block_size, total_size):
|
||||||
total_size: int = 0
|
total_downloaded = transferred_blocks*block_size
|
||||||
with io.open(update_destination, 'w+b') as outfile:
|
pub.sendMessage("updater.update-progress", total_downloaded=total_downloaded, total_size=total_size)
|
||||||
download: requests.Response = self.session.get(update_url, stream=True)
|
|
||||||
total_size = int(download.headers.get('content-length', 0))
|
filename, headers = urllib.request.urlretrieve(update_url, update_destination, _download_callback)
|
||||||
log.debug("Total update size: %d" % total_size)
|
|
||||||
download.raise_for_status()
|
|
||||||
for chunk in download.iter_content(chunk_size):
|
|
||||||
outfile.write(chunk)
|
|
||||||
total_downloaded += len(chunk)
|
|
||||||
pub.sendMessage("updater.update-progress", total_downloaded=total_downloaded, total_size=total_size)
|
|
||||||
log.debug("Update downloaded")
|
log.debug("Update downloaded")
|
||||||
return update_destination
|
return update_destination
|
||||||
|
|
||||||
|
@ -151,7 +151,6 @@ class WXUpdater(core.UpdaterCore):
|
|||||||
|
|
||||||
If there are updates available, displays a dialog to confirm the download of update. If the update downloads successfully, it also extracts and installs it.
|
If there are updates available, displays a dialog to confirm the download of update. If the update downloads successfully, it also extracts and installs it.
|
||||||
"""
|
"""
|
||||||
self.create_session()
|
|
||||||
self.initialize()
|
self.initialize()
|
||||||
update_info = self.get_update_information()
|
update_info = self.get_update_information()
|
||||||
version_data = self.get_version_data(update_info)
|
version_data = self.get_version_data(update_info)
|
||||||
|
Loading…
Reference in New Issue
Block a user