From 6efe8ab210abc47f8da2a3d734985a4f1b8655cd Mon Sep 17 00:00:00 2001 From: rlskoeser Date: Thu, 5 Oct 2023 12:07:45 -0400 Subject: [PATCH 1/4] Replace attrdict with addict --- piffle/presentation.py | 48 +++++++++++++++++--------------------- setup.py | 4 ++-- tests/test_presentation.py | 18 ++++++++++++-- 3 files changed, 40 insertions(+), 30 deletions(-) diff --git a/piffle/presentation.py b/piffle/presentation.py index 872dd26..cee43ca 100644 --- a/piffle/presentation.py +++ b/piffle/presentation.py @@ -2,7 +2,7 @@ import os.path import urllib -from attrdict import AttrMap +import addict import requests @@ -19,8 +19,8 @@ def get_iiif_url(url): return requests.get(url, **request_options) -class IIIFPresentation(AttrMap): - """:class:`attrdict.AttrMap` subclass for read access to IIIF Presentation +class IIIFPresentation(addict.Dict): + """:class:`addict.Dict` subclass for read access to IIIF Presentation content""" # TODO: document sample use, e.g. @ fields @@ -91,47 +91,43 @@ def short_id(cls, uri): # split on slashes and return the last portion return uri.split("/")[-1] - def __getattr__(self, key): - """ - Access an item as an attribute. - """ - # override getattr to allow use of keys with leading @, - # which are otherwise not detected as present and not valid - at_key = self._handle_at_keys(key) - if ( - key not in self - or (key not in self.at_fields and at_key not in self) - or not self._valid_name(key) - ): - raise AttributeError( - "'{cls}' instance has no attribute '{name}'".format( - cls=self.__class__.__name__, name=key - ) - ) - return self._build(self[key]) - - def _handle_at_keys(self, key): + def __missing__(self, key): + raise KeyError(self._key(key)) + + def _key(self, key): + # convert key to @key if in the list of fields that requires it if key in self.at_fields: key = "@%s" % key return key + def __getattr__(self, key): + try: + # addict getattr just calls getitem + return super().__getattr__(self._key(key)) + except KeyError: + # python hasattr checks for attribute error + # translate key error to attribute error, + # since in an attr dict it's kind of both + raise AttributeError + def __getitem__(self, key): """ Access a value associated with a key. """ - return self._mapping[self._handle_at_keys(key)] + val = super().__getitem__(self._key(key)) + return val def __setitem__(self, key, value): """ Add a key-value pair to the instance. """ - self._mapping[self._handle_at_keys(key)] = value + return super().__setitem__(self._key(key), value) def __delitem__(self, key): """ Delete a key-value pair """ - del self._mapping[self._handle_at_keys(key)] + super().__delitem__(self._key(key)) @property def first_label(self): diff --git a/setup.py b/setup.py index b90906d..1854bca 100755 --- a/setup.py +++ b/setup.py @@ -36,11 +36,11 @@ url="https://github.com/princeton-cdh/piffle", license="Apache License, Version 2.0", packages=find_packages(), - install_requires=["requests", "cached-property", "attrdict"], + install_requires=["requests", "cached-property", "addict"], setup_requires=["pytest-runner"], tests_require=test_requirements, extras_require={"test": test_requirements, "dev": dev_requirements}, - description="Python library for generating IIIF Image API urls", + description="Python library for workign with IIIF Image and Presentation APIs", long_description=LONG_DESCRIPTION, long_description_content_type="text/markdown", classifiers=CLASSIFIERS, diff --git a/tests/test_presentation.py b/tests/test_presentation.py index 24688be..e0830b5 100644 --- a/tests/test_presentation.py +++ b/tests/test_presentation.py @@ -98,13 +98,13 @@ def test_toplevel_attrs(self): def test_nested_attrs(self): pres = IIIFPresentation.from_file(self.test_manifest) - assert isinstance(pres.sequences, tuple) + assert isinstance(pres.sequences, list) assert ( pres.sequences[0].id == "https://plum.princeton.edu/concern/scanned_resources/ph415q7581/manifest/sequence/normal" ) assert pres.sequences[0].type == "sc:Sequence" - assert isinstance(pres.sequences[0].canvases, tuple) + assert isinstance(pres.sequences[0].canvases, list) assert ( pres.sequences[0].canvases[0].id == "https://plum.princeton.edu/concern/scanned_resources/ph415q7581/manifest/canvas/p02871v98d" @@ -124,6 +124,20 @@ def test_del(self): assert not hasattr(pres, "label") assert not hasattr(pres, "type") + # accessing missing keys as item vs accessing as attribute + # currently results in different errors + # (accurate, but potentially confusing?) + + with pytest.raises(KeyError): + assert not pres["label"] + with pytest.raises(KeyError): + assert not pres["type"] + + with pytest.raises(AttributeError): + assert not pres.label + with pytest.raises(AttributeError): + assert not pres.type + def test_first_label(self): pres = IIIFPresentation.from_file(self.test_manifest) assert pres.first_label == pres.label[0] From 3b42718ae523bc1b21cac78c80fa9f6756d9f256 Mon Sep 17 00:00:00 2001 From: rlskoeser Date: Thu, 5 Oct 2023 12:09:06 -0400 Subject: [PATCH 2/4] Update python matrix for unit test workflow --- .github/workflows/unit_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 10ebec0..0e75de8 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python: [3.7, 3.8] + python: [3.8, 3.9, 3.10, 3.11] steps: - name: Checkout repository From 3aedc3bb620e7f4fed5ad33b41114a50b07a342d Mon Sep 17 00:00:00 2001 From: rlskoeser Date: Thu, 5 Oct 2023 12:18:23 -0400 Subject: [PATCH 3/4] Use quotes: python 3.10, not 3.1 --- .github/workflows/unit_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 0e75de8..01721a7 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python: [3.8, 3.9, 3.10, 3.11] + python: [3.8, 3.9, "3.10", 3.11] steps: - name: Checkout repository From a29a348640551871393c585336b0ae4ead597ded Mon Sep 17 00:00:00 2001 From: rlskoeser Date: Thu, 5 Oct 2023 13:12:11 -0400 Subject: [PATCH 4/4] Document update to supported versions of python --- CHANGELOG.md | 1 + README.md | 4 ++-- setup.py | 7 ++++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a84856..9b27c86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## 0.5 +* Now supports python 3.8-3.11; dropped support for Python versions 3.6, 3.7 * The alias for accessing `piffle.image` via `piffle.iiif` has been removed * Setup pre-commit hooks and adopted Ruff+Black style formatting diff --git a/README.md b/README.md index c411542..9bbe274 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,9 @@ object-oriented, pythonic fashion. [![Maintainability](https://api.codeclimate.com/v1/badges/d37850d90592f9d628df/maintainability)](https://codeclimate.com/github/Princeton-CDH/piffle/maintainability) -Piffle is tested on Python 3.6-3.8. +Piffle is tested on Python 3.8—3.11. -Piffle was originally developed by Rebecca Sutton Koeser at Emory University as a part of [Readux](https://github.com/ecds/readux>) and forked as a separate project under [emory-lits-labs](https://github.com/emory-lits-labs/). It was later transferred to Rebecca Sutton Koeser at the Center for Digital Humanities at Princeton. +Piffle was originally developed by Rebecca Sutton Koeser at Emory University as a part of [Readux](https://github.com/ecds/readux) and forked as a separate project under [emory-lits-labs](https://github.com/emory-lits-labs/). It was later transferred to Rebecca Sutton Koeser at the Center for Digital Humanities at Princeton. ## Installation and example use: diff --git a/setup.py b/setup.py index 1854bca..86ab18c 100755 --- a/setup.py +++ b/setup.py @@ -12,16 +12,17 @@ pass CLASSIFIERS = [ - "Development Status :: 3 - Alpha", + "Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.6", - "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", "Topic :: Software Development :: Libraries :: Python Modules", ]