Remove dependency on requests
This commit is contained in:
		@@ -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:
 | 
					 | 
				
			||||||
            with requests_mock.Mocker(session=updater.session) as mocker:
 | 
					 | 
				
			||||||
                updatefile = os.path.join(os.path.dirname(__file__), "helloworld_v2_win64.zip")
 | 
					 | 
				
			||||||
                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")
 | 
					            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 result == "update.zip"
 | 
				
			||||||
                assert sendMessage_mock.call_count > 0
 | 
					            pub_sendMessage.assert_called_once()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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,7 +100,6 @@ 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:
 | 
				
			||||||
@@ -120,12 +119,10 @@ def test_check_for_updates_update_available(tempfile):
 | 
				
			|||||||
                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:
 | 
				
			||||||
@@ -136,7 +133,6 @@ def test_check_for_updates_no_update_available(tempfile):
 | 
				
			|||||||
@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:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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:
 | 
					 | 
				
			||||||
            download: requests.Response = self.session.get(update_url, stream=True)
 | 
					 | 
				
			||||||
            total_size = int(download.headers.get('content-length', 0))
 | 
					 | 
				
			||||||
            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)
 | 
					            pub.sendMessage("updater.update-progress", total_downloaded=total_downloaded, total_size=total_size)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        filename, headers = urllib.request.urlretrieve(update_url, update_destination, _download_callback)
 | 
				
			||||||
        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)
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user