diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3e2239c..bf6743d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,6 +43,20 @@ jobs: pip install virtualenv nox nox -f noxfile.py -s type_check + compatibility-3-13: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install system dependencies + run: sudo apt-get update && sudo apt-get install -y libjpeg-dev + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.13' + - run: | + pip install virtualenv nox + nox -f noxfile.py -s compatibility + compatibility-3-12: runs-on: ubuntu-latest steps: diff --git a/build_golden_images.py b/build_golden_images.py index 7ed730d..52ca9c3 100644 --- a/build_golden_images.py +++ b/build_golden_images.py @@ -18,7 +18,7 @@ import json import os import os.path -import pkg_resources +import importlib.resources import pybadges from tests import image_server @@ -46,17 +46,22 @@ def main(): parser = argparse.ArgumentParser( description='generate a github-style badge given some text and colors') - parser.add_argument( - '--source-path', - default=pkg_resources.resource_filename(__name__, - 'tests/test-badges.json'), - help='the text to show on the left-hand-side of the badge') + with importlib.resources.as_file( + importlib.resources.files('tests') / + 'test-badges.json') as test_badges_path: + parser.add_argument( + '--source-path', + default=str(test_badges_path), + help='the text to show on the left-hand-side of the badge') + + with importlib.resources.as_file( + importlib.resources.files('tests') / + 'golden-images') as golden_images_path: + parser.add_argument( + '--destination-dir', + default=str(golden_images_path), + help='the text to show on the left-hand-side of the badge') - parser.add_argument( - '--destination-dir', - default=pkg_resources.resource_filename(__name__, - 'tests/golden-images'), - help='the text to show on the left-hand-side of the badge') args = parser.parse_args() generate_images(args.source_path, args.destination_dir) diff --git a/noxfile.py b/noxfile.py index fedd5f4..930ad3d 100644 --- a/noxfile.py +++ b/noxfile.py @@ -45,7 +45,7 @@ def unit(session): 'install', [ 'Jinja2==3.0.0', - 'Pillow==8.3.2', # Oldest version that supports Python 3.7 to 3.10. + 'Pillow>=11.2.1,<12.0.0', 'requests>=2.25.0,<3.0.0', 'urllib3>=1.25.0,<2.0.0', # urllib3 2.0.0+ requires Python 3.8+ 'xmldiff==2.4' diff --git a/pybadges/__init__.py b/pybadges/__init__.py index 0bc6bee..87a3eff 100644 --- a/pybadges/__init__.py +++ b/pybadges/__init__.py @@ -30,12 +30,12 @@ """ import base64 -import imghdr import mimetypes from typing import Optional import urllib.parse from xml.dom import minidom +import filetype import jinja2 import requests @@ -102,7 +102,8 @@ def _embed_image(url: str) -> str: else: with open(url, 'rb') as f: image_data = f.read() - image_type = imghdr.what(None, image_data) + kind = filetype.guess(image_data) + image_type = kind.extension if kind is not None else None if not image_type: mime_type, _ = mimetypes.guess_type(url, strict=False) if not mime_type: diff --git a/pybadges/precalculated_text_measurer.py b/pybadges/precalculated_text_measurer.py index 6e769af..5374f99 100644 --- a/pybadges/precalculated_text_measurer.py +++ b/pybadges/precalculated_text_measurer.py @@ -18,7 +18,8 @@ import io import json -import pkg_resources +import importlib.resources +import lzma from typing import cast, Mapping, TextIO, Type from pybadges import text_measurer @@ -74,19 +75,23 @@ def default(cls) -> 'PrecalculatedTextMeasurer': if cls._default_cache is not None: return cls._default_cache - if pkg_resources.resource_exists(__name__, 'default-widths.json.xz'): - import lzma - with pkg_resources.resource_stream(__name__, - 'default-widths.json.xz') as f: - with lzma.open(f, "rt") as g: - cls._default_cache = PrecalculatedTextMeasurer.from_json( - cast(TextIO, g)) + try: + if importlib.resources.is_resource(__package__, + 'default-widths.json.xz'): + with importlib.resources.open_binary( + __package__, 'default-widths.json.xz') as f: + with lzma.open(f, "rt") as g: + cls._default_cache = PrecalculatedTextMeasurer.from_json( + cast(TextIO, g)) + return cls._default_cache + elif importlib.resources.is_resource(__package__, + 'default-widths.json'): + with importlib.resources.open_text(__package__, + 'default-widths.json', + encoding='utf-8') as f: + cls._default_cache = PrecalculatedTextMeasurer.from_json(f) return cls._default_cache - elif pkg_resources.resource_exists(__name__, 'default-widths.json'): - with pkg_resources.resource_stream(__name__, - 'default-widths.json') as f: - cls._default_cache = PrecalculatedTextMeasurer.from_json( - io.TextIOWrapper(f, encoding='utf-8')) - return cls._default_cache - else: - raise ValueError('could not load default-widths.json') + else: + raise ValueError('could not load default-widths.json') + except Exception as e: + raise ValueError(f'Error loading default-widths.json: {e}') diff --git a/setup.py b/setup.py index 88559b2..61ee93f 100644 --- a/setup.py +++ b/setup.py @@ -47,14 +47,11 @@ def replace_relative_with_absolute(match): 'Intended Audience :: Developers', 'Intended Audience :: Developers', 'License :: OSI Approved :: Apache Software License', - 'Programming Language :: Python', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', + 'Programming Language :: Python :: 3.13', 'Operating System :: OS Independent', ], description='A library and command-line tool for generating Github-style ' + @@ -67,8 +64,10 @@ def replace_relative_with_absolute(match): }, long_description=get_long_description(), long_description_content_type='text/markdown', - python_requires='>=3.4', - install_requires=['Jinja2>=3,<4', 'requests>=2.22.0,<3'], + python_requires='>=3.9', + install_requires=[ + 'Jinja2>=3,<4', 'requests>=2.22.0,<3', 'filetype>=1.2.0,<2.0.0' + ], extras_require={ 'pil-measurement': ['Pillow>=6,<10'], 'dev': [