Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ build
.idea
__pycache__
/tests/packages/**/cache
src/sinol_make/data

# pytest-cov
.coverage
Expand Down
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ dependencies = [
[project.optional-dependencies]
tests = [
"pytest",
"pytest-cov"
"pytest-cov",
"requests-mock",
]

[project.urls]
Expand Down
5 changes: 5 additions & 0 deletions src/sinol_make/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ def main():

for command in commands:
if command.get_name() == args.command:
new_version = util.check_for_updates(__version__)
if new_version is not None:
print(util.warning(f'New version of sinol-make is available (your version: {__version__}, available version: {new_version}).\n'
f' You can update it by running `pip3 install sinol-make --upgrade`.'))

if sys.platform == 'linux' and not util.check_oiejq():
print(util.warning('`oiejq` in `~/.local/bin/` not detected, installing now...'))

Expand Down
71 changes: 71 additions & 0 deletions src/sinol_make/util.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import glob, importlib, os, sys, subprocess, requests, tarfile, yaml
import importlib.resources
import threading


def get_commands():
"""
Expand Down Expand Up @@ -154,6 +157,74 @@ def save_config(config):
yaml.dump(config, config_file)


def check_for_updates(current_version) -> str | None:
"""
Function to check if there is a new version of sinol-make.
:param current_version: current version of sinol-make
:return: returns new version if there is one, None otherwise
"""
data_dir = importlib.resources.files("sinol_make").joinpath("data")
if not data_dir.is_dir():
os.mkdir(data_dir)

# We check for new version asynchronously, so that it doesn't slow down the program.
thread = threading.Thread(target=check_version)
thread.start()
version_file = data_dir.joinpath("version")

if version_file.is_file():
version = version_file.read_text()
try:
if compare_versions(current_version, version) == -1:
return version
else:
return None
except ValueError: # If the version file is corrupted, we just ignore it.
return None
else:
return None


def check_version():
"""
Function that asynchronously checks for new version of sinol-make.
Writes the newest version to data/version file.
"""
try:
request = requests.get("https://pypi.python.org/pypi/sinol-make/json", timeout=1)
except requests.exceptions.RequestException:
return

if request.status_code != 200:
return

data = request.json()
latest_version = data["info"]["version"]

version_file = importlib.resources.files("sinol_make").joinpath("data/version")
version_file.write_text(latest_version)


def compare_versions(version_a, version_b):
"""
Function to compare two versions.
Returns 1 if version_a > version_b, 0 if version_a == version_b, -1 if version_a < version_b.
"""

def convert(version):
return tuple(map(int, version.split(".")))

version_a = convert(version_a)
version_b = convert(version_b)

if version_a > version_b:
return 1
elif version_a == version_b:
return 0
else:
return -1


def lines_diff(lines1, lines2):
"""
Function to compare two lists of lines.
Expand Down
60 changes: 60 additions & 0 deletions tests/test_util.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import os
import sys
import time
import json

import pytest
import importlib.resources

import requests
import requests_mock

from sinol_make import util

Expand All @@ -15,3 +22,56 @@ def test_install_oiejq():
assert not util.check_oiejq()

assert util.install_oiejq()


def test_compare_versions():
"""
Tests for compare_versions function
"""

assert util.compare_versions('1.0.0', '1.0.0') == 0
assert util.compare_versions('1.0.0', '1.0.1') == -1
assert util.compare_versions('1.0.1', '1.0.0') == 1
assert util.compare_versions('1.0.0', '1.1.0') == -1
assert util.compare_versions('1.1.0', '1.0.0') == 1
assert util.compare_versions('1.0.0', '2.0.0') == -1
assert util.compare_versions('2.0.0', '1.0.0') == 1
with pytest.raises(ValueError):
util.compare_versions('1.0.0', '')
with pytest.raises(ValueError):
util.compare_versions('', '1.0.0')
with pytest.raises(ValueError):
util.compare_versions('1.0.0', 'abc')
with pytest.raises(ValueError):
util.compare_versions('abc', '1.0.0')


@requests_mock.Mocker(kw="mocker")
def test_check_version(**kwargs):
"""
Tests for check_version function
Simulates wrong responses and exceptions with requests-mock
"""
mocker = kwargs["mocker"]

data_dir = importlib.resources.files('sinol_make').joinpath("data")
version_file = data_dir.joinpath("version")
if not data_dir.is_dir():
data_dir.mkdir()

# Test correct request
mocker.get("https://pypi.python.org/pypi/sinol-make/json", json={"info": {"version": "1.0.0"}})
util.check_version()
assert version_file.is_file()
assert version_file.read_text() == "1.0.0"
version_file.unlink()

# Test wrong request
mocker.get("https://pypi.python.org/pypi/sinol-make/json", status_code=404)
util.check_version()
assert not version_file.is_file()

# Time out
mocker.get("https://pypi.python.org/pypi/sinol-make/json", exc=requests.exceptions.ConnectTimeout)
util.check_version()
assert not version_file.is_file()