diff --git a/.flake8 b/.flake8
index 28639c5..ad669af 100644
--- a/.flake8
+++ b/.flake8
@@ -1,3 +1,4 @@
[flake8]
per-file-ignores =
- tests/*:E501, W503
\ No newline at end of file
+ tests/*:E501, W503
+ tests/test_bookops_worldcat.py: F401
\ No newline at end of file
diff --git a/.github/workflows/unit-tests.yaml b/.github/workflows/unit-tests.yaml
index f070ffb..ce80861 100644
--- a/.github/workflows/unit-tests.yaml
+++ b/.github/workflows/unit-tests.yaml
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
+ python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version}}
diff --git a/bookops_worldcat/query.py b/bookops_worldcat/query.py
index cac785a..568ebb4 100644
--- a/bookops_worldcat/query.py
+++ b/bookops_worldcat/query.py
@@ -6,7 +6,7 @@
from typing import Union, Tuple, TYPE_CHECKING
import sys
-from requests.models import PreparedRequest
+from requests import PreparedRequest
from requests.exceptions import ConnectionError, HTTPError, Timeout, RetryError
from .errors import WorldcatRequestError
diff --git a/dev-requirements.txt b/dev-requirements.txt
index a9ce8c4..f287921 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -518,6 +518,9 @@ six==1.16.0 ; python_version >= "3.8" and python_version < "4.0" \
tomli==2.0.1 ; python_version >= "3.8" and python_full_version <= "3.11.0a6" \
--hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \
--hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f
+types-pyyaml==6.0.12.20240917 ; python_version >= "3.8" and python_version < "4.0" \
+ --hash=sha256:392b267f1c0fe6022952462bf5d6523f31e37f6cea49b14cee7ad634b6301570 \
+ --hash=sha256:d1405a86f9576682234ef83bcb4e6fff7c9305c8b1fbad5e0bcd4f7dbdc9c587
types-requests==2.31.0.20240125 ; python_version >= "3.8" and python_version < "4.0" \
--hash=sha256:03a28ce1d7cd54199148e043b2079cdded22d6795d19a2c2a6791a4b2b5e2eb5 \
--hash=sha256:9592a9a4cb92d6d75d9b491a41477272b710e021011a2a3061157e2fb1f1a5d1
diff --git a/poetry.lock b/poetry.lock
index fb7f8ad..7397a53 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1122,6 +1122,17 @@ files = [
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
]
+[[package]]
+name = "types-pyyaml"
+version = "6.0.12.20240917"
+description = "Typing stubs for PyYAML"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "types-PyYAML-6.0.12.20240917.tar.gz", hash = "sha256:d1405a86f9576682234ef83bcb4e6fff7c9305c8b1fbad5e0bcd4f7dbdc9c587"},
+ {file = "types_PyYAML-6.0.12.20240917-py3-none-any.whl", hash = "sha256:392b267f1c0fe6022952462bf5d6523f31e37f6cea49b14cee7ad634b6301570"},
+]
+
[[package]]
name = "types-requests"
version = "2.31.0.20240125"
@@ -1249,4 +1260,4 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools",
[metadata]
lock-version = "2.0"
python-versions = "^3.8"
-content-hash = "e29863e4f33eb0046c65ae158f498bc02ceb3e5f1cc830220ba087a261857db9"
+content-hash = "9175a4a2b44bc49e980b8679325995fb25fc9445ac53ca46943766741818b91c"
diff --git a/pyproject.toml b/pyproject.toml
index 8db391e..ce4557a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -53,6 +53,7 @@ types-requests = "^2.31.0.20240125"
mkdocs-material = "^9.5.13"
mkdocstrings = "^0.24.1"
mkdocstrings-python = "^1.9.0"
+types-pyyaml = "^6.0.12.20240917"
[tool.pytest.ini_options]
testpaths = ["tests"]
diff --git a/tests/conftest.py b/tests/conftest.py
index e02bfcf..01cc096 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -3,18 +3,16 @@
import datetime
import json
import os
-
+from typing import Dict, Generator, Union
import pytest
import requests
-from requests import Response
-
from bookops_worldcat import WorldcatAccessToken, MetadataSession
@pytest.fixture
def live_keys():
- if os.name == "nt":
+ if os.name == "nt" and not os.getenv("GITHUB_ACTIONS"):
fh = os.path.join(os.environ["USERPROFILE"], ".oclc/nyp_wc_test.json")
with open(fh, "r") as file:
data = json.load(file)
@@ -24,25 +22,20 @@ def live_keys():
@pytest.fixture
-def stub_marc_xml():
+def stub_marc_xml() -> str:
stub_marc_xml = "00000nam a2200000 a 4500120827s2012 nyua 000 0 eng d 63011276 ocn850940548OCWMSengOCWMSOCLC Developer NetworkTest RecordFOR OCLC DEVELOPER NETWORK DOCUMENTATION"
return stub_marc_xml
@pytest.fixture
-def stub_holding_xml():
+def stub_holding_xml() -> str:
stub_holding_xml = "00000nx a2200000zi 4500312010zu1103280p 0 4001uueng0210908OCWMSEASTEAST-STACKS879456"
return stub_holding_xml
@pytest.fixture
-def stub_marc21():
- fh = os.path.join(
- os.environ["USERPROFILE"], "github/bookops-worldcat/tests/test.mrc"
- )
- with open(fh, "rb") as stub:
- stub_marc21 = stub.read()
- return stub_marc21
+def stub_marc21() -> bytes:
+ return b"00266nam a2200097 a 4500008004100000010001700041040002200058100002700080245001600107500004500123\x1e120827s2012 nyua 000 0 eng d\x1e \x1fa 63011276 \x1e \x1faOCWMS\x1fbeng\x1fcOCWMS\x1e0 \x1faOCLC Developer Network\x1e10\x1faTest Record\x1e \x1faFOR OCLC DEVELOPER NETWORK DOCUMENTATION\x1e\x1d"
class FakeUtcNow(datetime.datetime):
@@ -59,10 +52,10 @@ def mock_now(monkeypatch):
class MockAuthServerResponseSuccess:
"""Simulates auth server response to successful token request"""
- def __init__(self):
+ def __init__(self) -> None:
self.status_code = 200
- def json(self):
+ def json(self) -> Dict[str, str]:
expires_at = datetime.datetime.strftime(
datetime.datetime.now() + datetime.timedelta(0, 1199),
"%Y-%m-%d %H:%M:%SZ",
@@ -84,11 +77,11 @@ def json(self):
class MockAuthServerResponseFailure:
"""Simulates auth server response to successful token request"""
- def __init__(self):
+ def __init__(self) -> None:
self.status_code = 403
self.content = b""
- def json(self):
+ def json(self) -> Dict[str, Union[str, int]]:
return {
"code": 403,
"message": "Invalid scope(s): invalid (invalid) [Invalid service specified, Not on key]",
@@ -98,38 +91,38 @@ def json(self):
class MockServiceErrorResponse:
"""Simulates web service error responses"""
- def __init__(self, code, json_response, url):
+ def __init__(self, code, json_response, url) -> None:
self.status_code = code
self.msg = json_response
self.url = url
self.text = f"{json_response}"
- def json(self):
+ def json(self) -> Dict[str, str]:
return self.msg
class MockUnexpectedException:
- def __init__(self, *args, **kwargs):
+ def __init__(self, *args, **kwargs) -> None:
raise Exception
class MockTimeout:
- def __init__(self, *args, **kwargs):
+ def __init__(self, *args, **kwargs) -> None:
raise requests.exceptions.Timeout
class MockConnectionError:
- def __init__(self, *args, **kwargs):
+ def __init__(self, *args, **kwargs) -> None:
raise requests.exceptions.ConnectionError
class MockRetryError:
- def __init__(self, *args, **kwargs):
+ def __init__(self, *args, **kwargs) -> None:
raise requests.exceptions.RetryError
-class MockHTTPSessionResponse(Response):
- def __init__(self, http_code):
+class MockHTTPSessionResponse(requests.Response):
+ def __init__(self, http_code) -> None:
self.status_code = http_code
self.reason = "'foo'"
self.url = "https://foo.bar?query"
@@ -156,7 +149,7 @@ def mock_api_response(*args, http_code=http_code, **kwargs):
@pytest.fixture
-def mock_credentials():
+def mock_credentials() -> Dict[str, str]:
return {
"key": "my_WSkey",
"secret": "my_WSsecret",
@@ -165,7 +158,9 @@ def mock_credentials():
@pytest.fixture
-def mock_oauth_server_response(mock_now, *args, **kwargs):
+def mock_oauth_server_response(
+ mock_now, *args, **kwargs
+) -> MockAuthServerResponseSuccess:
return MockAuthServerResponseSuccess()
@@ -214,18 +209,20 @@ def mock_retry_error(monkeypatch):
@pytest.fixture
-def mock_token(mock_credentials, mock_successful_post_token_response):
+def mock_token(
+ mock_credentials, mock_successful_post_token_response
+) -> WorldcatAccessToken:
return WorldcatAccessToken(**mock_credentials)
@pytest.fixture
-def stub_session(mock_token):
+def stub_session(mock_token) -> Generator[MetadataSession, None, None]:
with MetadataSession(authorization=mock_token) as session:
yield session
@pytest.fixture
-def stub_retry_session(mock_token):
+def stub_retry_session(mock_token) -> Generator[MetadataSession, None, None]:
with MetadataSession(
authorization=mock_token,
totalRetries=3,
@@ -234,34 +231,3 @@ def stub_retry_session(mock_token):
allowedMethods=["GET", "POST", "PUT"],
) as session:
yield session
-
-
-@pytest.fixture
-def mock_400_response(monkeypatch):
- def mock_api_response(*args, **kwargs):
- msg = {
- "type": "MISSING_QUERY_PARAMETER",
- "title": "Validation Failure",
- "detail": "details here",
- }
- url = "https://test.org/some_endpoint"
- return MockServiceErrorResponse(code=400, json_response=msg, url=url)
-
- monkeypatch.setattr(requests.Session, "get", mock_api_response)
- monkeypatch.setattr(requests.Session, "post", mock_api_response)
- monkeypatch.setattr(requests.Session, "delete", mock_api_response)
-
-
-@pytest.fixture
-def mock_409_response(monkeypatch):
- def mock_api_response(*args, **kwargs):
- msg = {
- "code": {"value": "WS-409", "type": "application"},
- "message": "Trying to set hold while holding already exists",
- "detail": None,
- }
- url = "https://test.org/some_endpoint"
- return MockServiceErrorResponse(code=409, json_response=msg, url=url)
-
- monkeypatch.setattr(requests.Session, "post", mock_api_response)
- monkeypatch.setattr(requests.Session, "delete", mock_api_response)
diff --git a/tests/test.mrc b/tests/test.mrc
deleted file mode 100644
index 5b061b0..0000000
--- a/tests/test.mrc
+++ /dev/null
@@ -1 +0,0 @@
-00266nam a2200097 a 4500008004100000010001700041040002200058100002700080245001600107500004500123120827s2012 nyua 000 0 eng d a 63011276 aOCWMSbengcOCWMS0 aOCLC Developer Network10aTest Record aFOR OCLC DEVELOPER NETWORK DOCUMENTATION
\ No newline at end of file