From 83c89c0fc46f56e2323318d65fa649be3352f94e Mon Sep 17 00:00:00 2001 From: Muhammad Fiaz Date: Thu, 7 Dec 2023 19:25:34 +0530 Subject: [PATCH] merge the development updates (#4) * Updated README.md * Add pytest tests and GitHub Actions workflow * Add pytest tests and GitHub Actions workflow * Add pytest tests and GitHub Actions workflow * Added the Check version Capability --- checkversions/__init__.py | 38 ++++++++++++ checkversions/checkversions.py | 110 +++++++++++++++++++++++++++++++++ default_hierarchy.json | 10 +++ requirements.txt | 4 +- setup.py | 4 +- tests/__init__.py | 0 tests/test_sample.py | 46 +++++++++++++- 7 files changed, 206 insertions(+), 6 deletions(-) create mode 100644 checkversions/checkversions.py create mode 100644 default_hierarchy.json create mode 100644 tests/__init__.py diff --git a/checkversions/__init__.py b/checkversions/__init__.py index e69de29..77ef8a7 100644 --- a/checkversions/__init__.py +++ b/checkversions/__init__.py @@ -0,0 +1,38 @@ +""" +CheckVersions Package + +This package provides functionality for comparing version numbers and determining the latest version +based on a specified hierarchy. + +Usage: + from checkversions import * + + current_version = "v1.0.0" + latest_version = "v1.0.0-beta" + result = compare_versions(current_version, latest_version) + print(result) + +Functions: + - compare_versions(current_version, latest_version, custom_hierarchy=None): + Compares two versions and returns the latest version based on the provided hierarchy. + + - get_default_hierarchy_from_json(): + Loads the default hierarchy from a JSON file and returns it as a dictionary. + +Modules: + - checkversions.py + Contains the implementation of version comparison functions. + + - default_hierarchy.json + Default hierarchy configuration file in JSON format. + +Exceptions: + - DotSetupException + - FileNotFoundError + - VariableNotFoundError + - JSONDecodeError + +Note: Ensure that 'dotsetup' and 'packaging' packages are installed for proper functionality. +""" + +from .checkversions import compare_versions diff --git a/checkversions/checkversions.py b/checkversions/checkversions.py new file mode 100644 index 0000000..2942b7e --- /dev/null +++ b/checkversions/checkversions.py @@ -0,0 +1,110 @@ +import os +from packaging import version +from packaging.version import InvalidVersion +from dotsetup import DotSetup, FileNotFoundError, VariableNotFoundError, JSONDecodeError + +def get_default_hierarchy_from_json(): + """ + Load the default hierarchy from a JSON file. + + This function initializes a DotSetup instance, gets the absolute path to + the 'default_hierarchy.json' file, and loads the hierarchy from the JSON file. + + Returns: + dict: The default version hierarchy loaded from the JSON file. + + Raises: + FileNotFoundError: If the 'default_hierarchy.json' file is not found. + VariableNotFoundError: If a variable is not found during JSON loading. + JSONDecodeError: If there is an error decoding JSON. + """ + try: + # Initialize DotSetup + ds = DotSetup() + + # Get absolute path to JSON file + script_dir = os.path.dirname(os.path.abspath(__file__)) + json_file_path = os.path.join(script_dir, '../default_hierarchy.json') + + # Load from JSON file + value_json = ds.load('default_hierarchy', file_type='json', file_path=json_file_path) + return value_json + except (FileNotFoundError, VariableNotFoundError, JSONDecodeError) as e: + print(f"Error loading default hierarchy from JSON: {e}") + return {"beta": 0, "prerelease": 1, "alpha": 2, "unstable": 3, "stable": 4, "release": 5} + +def compare_versions(current_version, latest_version, custom_hierarchy=None): + """ + Compare two versions and determine the latest version. + + This function compares two versions, taking into account version numbers, + hyphen-separated parts, and additional words (e.g., beta, release, alpha). + + Args: + current_version (str): The current version. + latest_version (str): The version to compare against. + custom_hierarchy (dict, optional): Custom version hierarchy. Defaults to None. + + Returns: + str: The latest version, considering version numbers and hierarchy. + + Raises: + InvalidVersion: If there is an issue with the version comparison. + """ + try: + # Check if 'v' prefix is present and retain it in the output + current_version_output = current_version if current_version.startswith('v') else 'v' + current_version + latest_version_output = latest_version if latest_version.startswith('v') else 'v' + latest_version + + # Extract version codes before hyphen + current_version_before_hyphen = current_version.split('-')[0] + latest_version_before_hyphen = latest_version.split('-')[0] + + # Extract additional words (e.g., beta, release, alpha, stable, unstable) + current_version_words = ''.join(filter(str.isalpha, current_version)) + latest_version_words = ''.join(filter(str.isalpha, latest_version)) + + # Extract parts after hyphen for additional comparison + current_version_after_hyphen_parts = current_version.split('-')[1:] if '-' in current_version else [] + latest_version_after_hyphen_parts = latest_version.split('-')[1:] if '-' in latest_version else [] + + # Check if after hyphen contains a word not available in default or custom hierarchy + current_version_after_hyphen_word = ''.join(filter(str.isalpha, current_version_after_hyphen_parts[0])) if current_version_after_hyphen_parts else '' + latest_version_after_hyphen_word = ''.join(filter(str.isalpha, latest_version_after_hyphen_parts[0])) if latest_version_after_hyphen_parts else '' + + # Default version hierarchy + default_hierarchy = get_default_hierarchy_from_json() + + # Use custom hierarchy if provided, otherwise use default + version_hierarchy = custom_hierarchy or default_hierarchy + + # Check if the word after hyphen is in the hierarchy, else raise an error + if current_version_after_hyphen_word and current_version_after_hyphen_word not in version_hierarchy: + raise InvalidVersion(f"Version hierarchy not found for '{current_version_after_hyphen_word}'. Use custom hierarchy.") + if latest_version_after_hyphen_word and latest_version_after_hyphen_word not in version_hierarchy: + raise InvalidVersion(f"Version hierarchy not found for '{latest_version_after_hyphen_word}'. Use custom hierarchy.") + + # Use packaging.version for version comparison + current_version_obj = version.parse(current_version_before_hyphen) + latest_version_obj = version.parse(latest_version_before_hyphen) + + # Compare version numbers first + if current_version_obj < latest_version_obj: + return latest_version_output + elif current_version_obj > latest_version_obj: + return current_version_output + + # Compare hierarchy only if version numbers are equal + if version_hierarchy.get(current_version_after_hyphen_word, 5) < version_hierarchy.get(latest_version_after_hyphen_word, 5): + return latest_version_output + elif version_hierarchy.get(current_version_after_hyphen_word, 5) > version_hierarchy.get(latest_version_after_hyphen_word, 5): + return current_version_output + elif version_hierarchy.get(current_version_words, 5) < version_hierarchy.get(latest_version_words, 5): + return latest_version_output + elif version_hierarchy.get(current_version_words, 5) > version_hierarchy.get(latest_version_words, 5): + return current_version_output + + return current_version_output + + except InvalidVersion as e: + return f"Error: {e}" diff --git a/default_hierarchy.json b/default_hierarchy.json new file mode 100644 index 0000000..05cd20a --- /dev/null +++ b/default_hierarchy.json @@ -0,0 +1,10 @@ +{ + "default_hierarchy": { + "beta": 0, + "prerelease": 1, + "alpha": 2, + "unstable": 3, + "stable": 4, + "release": 5 + } +} diff --git a/requirements.txt b/requirements.txt index ada3782..7322e93 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,5 @@ pip==23.3.1 setuptools==69.0.2 -pytest==7.4.3 \ No newline at end of file +pytest==7.4.3 +dotsetup~=0.0.2 + diff --git a/setup.py b/setup.py index 242bb77..332ce74 100644 --- a/setup.py +++ b/setup.py @@ -34,8 +34,8 @@ ], python_requires='>=3.8', install_requires=INSTALL_REQUIRES, - setup_requires=['pytest-runner'], # Added setup_requires for pytest-runner - tests_require=['pytest'], # Added tests_require for pytest + setup_requires=['pytest-runner'], + tests_require=['pytest'], license='MIT License', project_urls={ 'Source Code': 'https://github.com/muhammad-fiaz/checkversions.git', diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_sample.py b/tests/test_sample.py index e956887..541d85b 100644 --- a/tests/test_sample.py +++ b/tests/test_sample.py @@ -1,3 +1,43 @@ -#sample tests case for pytest -def test_example(): - assert 1 + 1 == 2 \ No newline at end of file +import pytest +from checkversions.checkversions import compare_versions + +@pytest.fixture(params=[ + ("2.0.0-release", "v2.0.1", "v2.0.1"), # Current version is older + ("v1.0.0", "1.0.0-beta", "v1.0.0"), # Current version is newer + ("v3.1.2", "v3.1.2", "v3.1.2"), # Versions are the same + ("v2.0.0-beta", "2.0.1", "v2.0.1"), # Current version is older with hyphen + ("v1.0.0", "v1.0.0-beta", "v1.0.0"), # Current version is newer with hyphen + ("v3.1.2", "v3.1.2", "v3.1.2"), # Versions are the same with hyphen + ("v2.0.0-beta", "v2.0.1-alpha", "v2.0.1-alpha"), # Current version is older with hyphen and words + ("v1.0.0-beta", "1.0.0-alpha", "v1.0.0-alpha"), # Current version is newer with hyphen and words + ("v3.1.2-beta", "v3.1.2-alpha", "v3.1.2-alpha"), # Versions are the same with hyphen and words + ("v2.0.0-beta", "v2.0.1", "v2.0.1"), # Current version is older with words + ("v1.0.0-beta", "1.0.0", "v1.0.0"), # Current version is newer with words +]) +def version_data(request): + """ + Fixture providing version data for testing. + + Each parameter is a tuple containing three elements: + 1. Current version + 2. Latest version + 3. Expected result of the version comparison + + Returns: + tuple: A set of version data for testing. + """ + return request.param + +def test_compare_versions(version_data): + """ + Test the compare_versions function with different version scenarios. + + Args: + version_data (tuple): A tuple containing current_version, latest_version, and expected_result. + + The test compares two versions using the compare_versions function and asserts that the result + matches the expected outcome. + """ + current_version, latest_version, expected_result = version_data + result = compare_versions(current_version, latest_version) + assert result == expected_result