Skip to content

Commit 0d2dd37

Browse files
committed
refactor
- roll fetch.py into client.py - removed fetch.py - remove direct api access - defaults for client init including default tind api key & url environment fetching - added basic .flake8 style settings - updated readme - updated tests
1 parent ea41b7a commit 0d2dd37

6 files changed

Lines changed: 206 additions & 364 deletions

File tree

.flake8

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[flake8]
2+
max-line-length = 100
3+
# Equivalent to allow-init-docstring, which we set on pydoclint.
4+
extend-ignore = DOC301,F401

README.md

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# python-tind-client
22

3-
Python library for interacting with the [TIND ILS](https://tind.io) API.
3+
Python library for interacting with the [TIND DA](https://tind.io) API.
44

55
## Requirements
66

@@ -23,11 +23,11 @@ pip install "python-tind-client[debug]" # debugpy
2323

2424
## Configuration
2525

26-
Create a `TINDClient` with explicit configuration values:
26+
Create a `TINDClient` with optional configuration values:
2727

28-
- `api_key` (required): Your TIND API token
29-
- `api_url` (required): Base URL of the TIND instance (e.g. `https://tind.example.edu`)
30-
- `default_storage_dir` (optional): Default output directory for downloaded files
28+
- `api_key` (optional): Your TIND API token. Falls back to the `TIND_API_KEY` environment variable.
29+
- `api_url` (optional): Base URL of the TIND instance (e.g. `https://tind.example.edu`). Falls back to the `TIND_API_URL` environment variable.
30+
- `default_storage_dir` (optional): Default output directory for downloaded files. Defaults to `./tmp`.
3131

3232
## Usage
3333

@@ -44,39 +44,35 @@ client = TINDClient(
4444

4545
### Fetch pyMARC metadata for a record
4646
```python
47-
record = client.fetch_metadata("12345")
47+
record = client.fetch_metadata("116262")
4848
print(record["245"]["a"]) # title
4949
```
5050

5151
### Fetch file metadata for a record
5252
```python
53-
metadata = client.fetch_file_metadata("12345")
54-
print(metadata[0]) # first file metadata dict
55-
print(metadata[0]["url"]) # file download URL
53+
metadata = client.fetch_file_metadata("116262")
54+
print(metadata[0]) # first file metadata dict
55+
print(metadata[0]["url"]) # file download URL
5656
```
5757

5858
### Download a file
5959
```python
6060
# use metadata from previous example
61-
path_to_download = client.fetch_file(metadata[0].url)
61+
path_to_download = client.fetch_file(metadata[0]["url"])
6262
```
6363

64-
## Functional fetch API
65-
66-
The functions in `tind_client.fetch` are available for direct use and now accept
67-
explicit credentials instead of a client object.
68-
64+
### Search for records
6965
```python
70-
from tind_client.fetch import fetch_metadata
66+
# return a list of record IDs matching a query
67+
ids = client.fetch_ids_search("collection:'Disabled Students Program Photos'")
7168

72-
record = fetch_metadata(
73-
"12345",
74-
api_key="your-token",
75-
api_url="https://tind.example.edu",
76-
)
77-
```
69+
# return PyMARC records matching a query
70+
records = client.fetch_search_metadata("collection:'Disabled Students Program Photos'")
7871

79-
For most use cases, prefer `TINDClient` methods as the primary interface.
72+
# return raw XML or PyMARC records from a paginated search
73+
xml_results = client.search("collection:'Disabled Students Program Photos'", result_format="xml")
74+
pymarc_results = client.search("collection:'Disabled Students Program Photos'", result_format="pymarc")
75+
```
8076

8177
## Running tests
8278

tests/test_fetch.py

Lines changed: 14 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
"""
2-
Tests for tind_client.fetch.
2+
Tests for TINDClient methods (fetch operations).
33
"""
44

55
import json
66

77
import pytest
88
import requests_mock as req_mock # noqa: F401 — activates the requests_mock fixture
99

10-
from tind_client import TINDClient, fetch
10+
from tind_client import TINDClient
1111
from tind_client.errors import RecordNotFoundError, TINDError
1212

1313
BASE_URL = "https://tind.example.edu"
@@ -28,17 +28,15 @@ def test_fetch_metadata_success(
2828
requests_mock.get(
2929
f"{BASE_URL}/record/12345/", text=sample_marc_xml, status_code=200
3030
)
31-
record = fetch.fetch_metadata(
32-
"12345", api_key=client.api_key, api_url=client.api_url
33-
)
31+
record = client.fetch_metadata("12345")
3432
assert record["245"]["a"] == "Sample Title"
3533

3634

3735
def test_fetch_metadata_404(requests_mock: req_mock.Mocker, client: TINDClient) -> None:
3836
"""fetch_metadata raises RecordNotFoundError on HTTP 404."""
3937
requests_mock.get(f"{BASE_URL}/record/99999/", text="", status_code=404)
4038
with pytest.raises(RecordNotFoundError):
41-
fetch.fetch_metadata("99999", api_key=client.api_key, api_url=client.api_url)
39+
client.fetch_metadata("99999")
4240

4341

4442
def test_fetch_metadata_empty_body(
@@ -47,7 +45,7 @@ def test_fetch_metadata_empty_body(
4745
"""fetch_metadata raises RecordNotFoundError when the response body is empty."""
4846
requests_mock.get(f"{BASE_URL}/record/11111/", text=" ", status_code=200)
4947
with pytest.raises(RecordNotFoundError):
50-
fetch.fetch_metadata("11111", api_key=client.api_key, api_url=client.api_url)
48+
client.fetch_metadata("11111")
5149

5250

5351
# ---------------------------------------------------------------------------
@@ -58,7 +56,7 @@ def test_fetch_metadata_empty_body(
5856
def test_fetch_file_invalid_url(client: TINDClient) -> None:
5957
"""fetch_file raises ValueError for non-TIND download URLs."""
6058
with pytest.raises(ValueError):
61-
fetch.fetch_file("https://not-a-tind-url.com/file.pdf", api_key=client.api_key)
59+
client.fetch_file("https://not-a-tind-url.com/file.pdf")
6260

6361

6462
def test_fetch_file_success(
@@ -74,7 +72,7 @@ def test_fetch_file_success(
7472
status_code=200,
7573
headers={"Content-Disposition": 'attachment; filename="document.pdf"'},
7674
)
77-
path = fetch.fetch_file(url, api_key=client.api_key, output_dir=str(tmp_path))
75+
path = client.fetch_file(url, output_dir=str(tmp_path))
7876
assert path.endswith("document.pdf")
7977

8078

@@ -87,7 +85,7 @@ def test_fetch_file_not_found(
8785
url = f"{BASE_URL}/files/missing/download"
8886
requests_mock.get(url, status_code=404)
8987
with pytest.raises(RecordNotFoundError):
90-
fetch.fetch_file(url, api_key=client.api_key, output_dir=str(tmp_path))
88+
client.fetch_file(url, output_dir=str(tmp_path))
9189

9290

9391
# ---------------------------------------------------------------------------
@@ -105,9 +103,7 @@ def test_fetch_file_metadata_success(
105103
text=json.dumps(payload),
106104
status_code=200,
107105
)
108-
result = fetch.fetch_file_metadata(
109-
"12345", api_key=client.api_key, api_url=client.api_url
110-
)
106+
result = client.fetch_file_metadata("12345")
111107
assert result[0]["name"] == "file.pdf"
112108

113109

@@ -121,9 +117,7 @@ def test_fetch_file_metadata_error(
121117
status_code=404,
122118
)
123119
with pytest.raises(TINDError):
124-
fetch.fetch_file_metadata(
125-
"12345", api_key=client.api_key, api_url=client.api_url
126-
)
120+
client.fetch_file_metadata("12345")
127121

128122

129123
# ---------------------------------------------------------------------------
@@ -140,9 +134,7 @@ def test_fetch_ids_search_success(
140134
text=json.dumps({"hits": ["1", "2", "3"]}),
141135
status_code=200,
142136
)
143-
ids = fetch.fetch_ids_search(
144-
"title:python", api_key=client.api_key, api_url=client.api_url
145-
)
137+
ids = client.fetch_ids_search("title:python")
146138
assert ids == ["1", "2", "3"]
147139

148140

@@ -156,9 +148,7 @@ def test_fetch_ids_search_error(
156148
status_code=400,
157149
)
158150
with pytest.raises(TINDError):
159-
fetch.fetch_ids_search(
160-
"title:python", api_key=client.api_key, api_url=client.api_url
161-
)
151+
client.fetch_ids_search("title:python")
162152

163153

164154
# ---------------------------------------------------------------------------
@@ -169,12 +159,7 @@ def test_fetch_ids_search_error(
169159
def test_search_invalid_format(client: TINDClient) -> None:
170160
"""search raises ValueError for unsupported result_format values."""
171161
with pytest.raises(ValueError, match="Unexpected result format"):
172-
fetch.search(
173-
"title:test",
174-
api_key=client.api_key,
175-
api_url=client.api_url,
176-
result_format="csv",
177-
)
162+
client.search("title:test", result_format="csv")
178163

179164

180165
def test_search_returns_xml(
@@ -191,12 +176,7 @@ def test_search_returns_xml(
191176

192177
requests_mock.get(f"{BASE_URL}/search", text=wrapped, status_code=200)
193178

194-
results = fetch.search(
195-
"title:sample",
196-
api_key=client.api_key,
197-
api_url=client.api_url,
198-
result_format="xml",
199-
)
179+
results = client.search("title:sample", result_format="xml")
200180
assert isinstance(results, list)
201181
assert len(results) >= 1
202182
assert requests_mock.call_count == 1

tind_client/__init__.py

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,15 @@
11
"""
2-
python-tind-client — Python library for interacting with the TIND ILS API.
2+
python-tind-client — Python library for interacting with the TIND DA API.
33
"""
44

55
__copyright__ = "© 2026 The Regents of the University of California. MIT license."
66

77
from .client import TINDClient
8-
from .api import tind_download, tind_get
98
from .errors import AuthorizationError, RecordNotFoundError, TINDError
10-
from .fetch import (
11-
fetch_file,
12-
fetch_file_metadata,
13-
fetch_ids_search,
14-
fetch_marc_by_ids,
15-
fetch_metadata,
16-
fetch_search_metadata,
17-
search,
18-
)
199

2010
__all__ = [
2111
"TINDClient",
22-
"tind_get",
23-
"tind_download",
2412
"AuthorizationError",
2513
"RecordNotFoundError",
2614
"TINDError",
27-
"fetch_metadata",
28-
"fetch_file",
29-
"fetch_file_metadata",
30-
"fetch_ids_search",
31-
"fetch_marc_by_ids",
32-
"fetch_search_metadata",
33-
"search",
3415
]

0 commit comments

Comments
 (0)