Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

python upgrades #13

Merged
merged 4 commits into from
Oct 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
48 changes: 22 additions & 26 deletions piffle/presentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import os.path
import urllib

from attrdict import AttrMap
import addict

import requests

Expand All @@ -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
Expand Down Expand Up @@ -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):
Expand Down
11 changes: 6 additions & 5 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
]

Expand All @@ -36,11 +37,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,
Expand Down
18 changes: 16 additions & 2 deletions tests/test_presentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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]
Expand Down