Skip to content

Commit 07ce128

Browse files
committed
put client.search() back in
1 parent 1cb5879 commit 07ce128

4 files changed

Lines changed: 65 additions & 26 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Added
1111
- client method to write search results to an XML file, with validation against expected number of records to be written
1212
- client method to return an iterator of XML records from a search, to support streaming results for large result sets
13+
- xml fixture files for future tests
1314

1415
### Changed
15-
- N/A
16+
- README examples
1617

1718
### Deprecated
1819
- N/A
1920

2021
### Removed
21-
- client method to return raw XML or pyMARC records from a search, in favor of separate methods for each result format and a streaming iterator method for XML results
22+
- N/A
2223

2324
### Fixed
2425
- N/A

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@ ids = client.fetch_ids_search("collection:'Disabled Students Program Photos'")
7474
# return PyMARC records matching a query
7575
records = client.fetch_search_metadata("collection:'Disabled Students Program Photos'")
7676

77+
# return raw XML or PyMARC records from a paginated search
78+
# NOTE: for large result sets, use the write_search_results_to_file() method and then parse that file
79+
xml_results = client.search("collection:'Disabled Students Program Photos'", result_format="xml")
80+
pymarc_results = client.search("collection:'Disabled Students Program Photos'", result_format="pymarc")
81+
7782
# search Tind with a query and write results to an XML file in the default storage directory
7883
records_written = client.write_search_results_to_file("Old Emperor Norton", "full_norton_results.xml")
7984
```

tests/test_fetch.py

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -156,27 +156,27 @@ def test_fetch_ids_search_error(
156156
# ---------------------------------------------------------------------------
157157

158158

159-
# def test_search_invalid_format(client: TINDClient) -> None:
160-
# """search raises ValueError for unsupported result_format values."""
161-
# with pytest.raises(ValueError, match="Unexpected result format"):
162-
# client.search("title:test", result_format="csv")
163-
164-
165-
# def test_search_returns_xml(
166-
# sample_marc_xml: str,
167-
# requests_mock: req_mock.Mocker,
168-
# client: TINDClient,
169-
# ) -> None:
170-
# """search returns a list of XML strings when result_format='xml'."""
171-
# # Wrap sample XML in a search_id element so pagination terminates correctly.
172-
# wrapped = sample_marc_xml.replace(
173-
# "<collection",
174-
# "<root><search_id></search_id><collection",
175-
# ).replace("</collection>", "</collection></root>")
176-
177-
# requests_mock.get(f"{BASE_URL}/search", text=wrapped, status_code=200)
178-
179-
# results = client.search("title:sample", result_format="xml")
180-
# assert isinstance(results, list)
181-
# assert len(results) >= 1
182-
# assert requests_mock.call_count == 1
159+
def test_search_invalid_format(client: TINDClient) -> None:
160+
"""search raises ValueError for unsupported result_format values."""
161+
with pytest.raises(ValueError, match="Unexpected result format"):
162+
client.search("title:test", result_format="csv")
163+
164+
165+
def test_search_returns_xml(
166+
sample_marc_xml: str,
167+
requests_mock: req_mock.Mocker,
168+
client: TINDClient,
169+
) -> None:
170+
"""search returns a list of XML strings when result_format='xml'."""
171+
# Wrap sample XML in a search_id element so pagination terminates correctly.
172+
wrapped = sample_marc_xml.replace(
173+
"<collection",
174+
"<root><search_id></search_id><collection",
175+
).replace("</collection>", "</collection></root>")
176+
177+
requests_mock.get(f"{BASE_URL}/search", text=wrapped, status_code=200)
178+
179+
results = client.search("title:sample", result_format="xml")
180+
assert isinstance(results, list)
181+
assert len(results) >= 1
182+
assert requests_mock.call_count == 1

tind_client/client.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,39 @@ def fetch_search_metadata(self, query: str) -> list[Record]:
146146
ids = self.fetch_ids_search(query)
147147
return self.fetch_marc_by_ids(ids)
148148

149+
def search(self, query: str, result_format: str = "xml") -> list[Any]:
150+
"""Search TIND and return results as XML strings or PyMARC records.
151+
152+
:param str query: A TIND search string.
153+
:param str result_format: ``'xml'`` for XML strings, ``'pymarc'`` for PyMARC records.
154+
:raises ValueError: When ``result_format`` is neither ``'xml'`` nor ``'pymarc'``.
155+
:returns list: Records as XML strings or PyMARC Record objects.
156+
"""
157+
if result_format not in ("xml", "pymarc"):
158+
raise ValueError(
159+
f"Unexpected result format: {result_format} is neither 'xml' nor 'pymarc'"
160+
)
161+
162+
recs: list[Any] = []
163+
search_id = None
164+
165+
while True:
166+
response = self._search_request(query, search_id=search_id)
167+
xml, search_id = self._retrieve_xml_search_id(response)
168+
collection = xml.find("{http://www.loc.gov/MARC21/slim}collection")
169+
records = list(collection) if collection is not None else []
170+
171+
if result_format == "pymarc":
172+
recs = recs + parse_xml_to_array(StringIO(response))
173+
else:
174+
for record in records:
175+
recs.append(E.tostring(record, encoding="unicode"))
176+
177+
if not search_id or not records:
178+
break
179+
180+
return recs
181+
149182
def write_search_results_to_file(self, query: str, output_file_name: str = "tind.xml") -> int:
150183
"""Search TIND and stream results to an XML file.
151184

0 commit comments

Comments
 (0)