From cb568730d8bed0ff961dbf9b24958246cb02c77c Mon Sep 17 00:00:00 2001 From: CodeByMrFinchum Date: Mon, 3 Feb 2025 16:21:01 +0100 Subject: [PATCH] feat: Added local update functions --- CHANGELOG.md | 10 ++++++++ src/PyPiUpdater/pypi_updater.py | 45 +++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee74deb..678af18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 0.6.0: New Local Update Feature +- Added support for updating from a local folder containing package files. + - Scans a specified folder for available updates. + - Installs updates directly from local package files. +- **Note:** Local version handling depends on how dependencies are managed. + - Example: If a package requires **PyPiUpdater 0.6.0**, but the installed version is **0.0.1** (e.g., from a dev environment), **OptimaLab35 v0.9.1** may not install correctly. + - This is due to **pip's dependency checks**, ensuring all required versions are satisfied before installation. + +--- + ## 0.5.0: Rework (BREAKING CHANGE) - Improved code consistency: return values are now always **lists** when containing multiple objects. - **Simplified the package**: Removed the default waiting period for update checks. diff --git a/src/PyPiUpdater/pypi_updater.py b/src/PyPiUpdater/pypi_updater.py index 5767a34..4c5b835 100644 --- a/src/PyPiUpdater/pypi_updater.py +++ b/src/PyPiUpdater/pypi_updater.py @@ -4,6 +4,7 @@ import sys import os import time import json +import re from packaging import version from xml.etree import ElementTree as ET @@ -59,6 +60,50 @@ class PyPiUpdater: except subprocess.CalledProcessError as e: return [False, f"Update failed: {str(e)}"] + def check_update_local(self, folder_path): + """ + Check if a newer version of the package is available in the local folder. + + :param folder_path: Path to the folder containing package files. + :return: (bool, latest_version) - True if newer version found, otherwise False. + """ + if not os.path.exists(folder_path): + return [None, "Folder does not exist"] + + pattern = re.compile(rf"{re.escape(self.package_name.lower())}-(\d+\.\d+\.\d+[a-zA-Z0-9]*)") + + available_versions = [] + for file in os.listdir(folder_path): + match = pattern.search(file) + if match: + found_version = match.group(1) + available_versions.append(version.parse(found_version)) + + if not available_versions: + return [None, "No valid package versions found in the folder"] + + latest_version = max(available_versions) + is_newer = latest_version > self.local_version + + return [is_newer, str(latest_version)] + + def update_from_local(self, folder_path): + """ + Install the latest package version from a local folder. + + :param folder_path: Path to the folder containing package files. + :return: (bool, message) - Success status and message. + """ + print(f"Installing {self.package_name} from {folder_path}...") + try: + subprocess.run( + [sys.executable, "-m", "pip", "install", "--no-index", "--find-links", folder_path, self.package_name, "-U"], + check=True + ) + return [True, f"{self.package_name} updated successfully from local folder."] + except subprocess.CalledProcessError as e: + return [False, f"Update from local folder failed: {str(e)}"] + def restart_program(self): """Restart the Python program after an update.""" print("Restarting the application...")