|
2 | 2 | Copyright © 2025 John Liu |
3 | 3 | """ |
4 | 4 |
|
| 5 | +import importlib.metadata |
5 | 6 | import itertools |
6 | 7 | import json |
7 | 8 | import subprocess |
8 | 9 | import tomllib |
9 | 10 | from datetime import datetime |
10 | | -from importlib.metadata import version |
11 | 11 | from multiprocessing import Pool, cpu_count |
12 | 12 | from os.path import getmtime, getsize |
13 | 13 | from pathlib import Path |
14 | 14 |
|
15 | 15 | import piexif |
16 | 16 | import pillow_heif |
| 17 | +import requests |
17 | 18 | from loguru import logger |
| 19 | +from packaging import version # compare versions safely |
18 | 20 | from PIL import Image, ImageChops |
19 | 21 | from PIL.TiffImagePlugin import IFDRational |
20 | 22 | from tqdm import tqdm |
21 | 23 |
|
22 | | -from batch_img.const import PATTERNS, PKG_NAME, REPLACE, TS_FORMAT, VER |
| 24 | +from batch_img.const import PATTERNS, REPLACE, TS_FORMAT, VER |
23 | 25 |
|
24 | 26 | pillow_heif.register_heif_opener() # allow Pillow to open HEIC files |
25 | 27 |
|
26 | 28 |
|
27 | 29 | class Common: |
28 | 30 | @staticmethod |
29 | | - def get_version() -> str: |
30 | | - """ |
31 | | - Get this package version using several ways |
| 31 | + def get_version(pkg_name: str) -> str: |
| 32 | + """Get this package version by various ways |
| 33 | +
|
| 34 | + Args: |
| 35 | + pkg_name: package name str |
| 36 | +
|
| 37 | + Returns: |
| 38 | + str: |
32 | 39 | """ |
33 | 40 | try: |
34 | | - return version(PKG_NAME) |
| 41 | + return importlib.metadata.version(pkg_name) |
35 | 42 | except (FileNotFoundError, ImportError, ValueError) as e: |
36 | | - # Use lazy % formatting in logging for efficiency |
37 | | - logger.warning(f"importlib.metadata.version Error: {e}") |
38 | | - logger.debug("Try to get version from pyproject.toml file") |
| 43 | + logger.warning(f"importlib.metadata.version() Error: {e}") |
| 44 | + logger.debug("Get version from pyproject.toml file") |
39 | 45 | pyproject = Path(__file__).parent.parent / "pyproject.toml" |
40 | 46 | with open(pyproject, "rb") as f: |
41 | 47 | return tomllib.load(f)["project"][VER] |
42 | 48 |
|
| 49 | + @staticmethod |
| 50 | + def check_latest_version(pkg_name: str) -> str: |
| 51 | + """Check if the installed version is the latest one |
| 52 | +
|
| 53 | + Args: |
| 54 | + pkg_name: package name str |
| 55 | +
|
| 56 | + Returns: |
| 57 | + str |
| 58 | + """ |
| 59 | + try: |
| 60 | + jsn_url = f"https://pypi.org/pypi/{pkg_name}/json" |
| 61 | + response = requests.get(jsn_url, timeout=8) |
| 62 | + if response.status_code != 200: |
| 63 | + msg = f"⚠️ Error get data from PyPI: {jsn_url}" |
| 64 | + logger.error(msg) |
| 65 | + return msg |
| 66 | + |
| 67 | + latest_ver = response.json()["info"]["version"] |
| 68 | + cur_ver = Common.get_version(pkg_name) |
| 69 | + if version.parse(cur_ver) < version.parse(latest_ver): |
| 70 | + msg = ( |
| 71 | + f"🔔 Update available: {cur_ver} → {latest_ver}\n" |
| 72 | + f"Please run '{pkg_name} --update'" |
| 73 | + ) |
| 74 | + else: |
| 75 | + msg = f"✅ {pkg_name} is up to date ({cur_ver})" |
| 76 | + logger.info(msg) |
| 77 | + except requests.RequestException as e: |
| 78 | + msg = f"requests.get() Exception: {e}" |
| 79 | + logger.error(msg) |
| 80 | + except (KeyError, json.JSONDecodeError) as e: |
| 81 | + msg = f"Error parse PyPI response: {e}" |
| 82 | + logger.error(msg) |
| 83 | + return msg |
| 84 | + |
| 85 | + @staticmethod |
| 86 | + def update_package(pkg_name: str) -> str: |
| 87 | + """Update the package to the latest version |
| 88 | +
|
| 89 | + Args: |
| 90 | + pkg_name: package name str |
| 91 | +
|
| 92 | + Returns: |
| 93 | + str |
| 94 | + """ |
| 95 | + logger.info(f"🔄 Updating {pkg_name} ...") |
| 96 | + cmd = f"uv pip install --upgrade {pkg_name}" |
| 97 | + try: |
| 98 | + Common.run_cmd(cmd) |
| 99 | + msg = "✅ Update completed." |
| 100 | + logger.info(msg) |
| 101 | + except subprocess.CalledProcessError as e: |
| 102 | + msg = f"❌ Failed to update {pkg_name}: {e}" |
| 103 | + logger.error(msg) |
| 104 | + return msg |
| 105 | + |
43 | 106 | @staticmethod |
44 | 107 | def run_cmd(cmd: str) -> tuple: |
45 | 108 | """Run a command on the host and get the output |
|
0 commit comments