Skip to content

Commit d5bfa38

Browse files
committed
feat: replace debug parameter with Python logging (DEBUG)
The 'debug' parameter was deprecated and will be removed in a future release. We've switched to using Python's built-in logging module with the DEBUG level.
1 parent 53015d2 commit d5bfa38

File tree

5 files changed

+59
-72
lines changed

5 files changed

+59
-72
lines changed

semanticscholar/ApiRequester.py

Lines changed: 16 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,31 @@
1+
import asyncio
2+
import json
3+
import logging
4+
import warnings
15
from typing import List, Union
26

37
import httpx
4-
import asyncio
5-
import warnings
6-
import json
7-
from tenacity import (retry as rerun, retry_if_exception_type,
8-
stop_after_attempt, wait_fixed)
8+
from tenacity import retry as rerun
9+
from tenacity import retry_if_exception_type, stop_after_attempt, wait_fixed
10+
11+
from semanticscholar.SemanticScholarException import (
12+
BadQueryParametersException, ObjectNotFoundException)
913

1014
from semanticscholar.SemanticScholarException import \
1115
BadQueryParametersException, ObjectNotFoundException
1216

17+
logger = logging.getLogger('semanticscholar')
18+
1319

1420
class ApiRequester:
1521

16-
def __init__(self, timeout, debug, retry: bool = True) -> None:
22+
def __init__(self, timeout, retry: bool = True) -> None:
1723
'''
1824
:param float timeout: an exception is raised \
1925
if the server has not issued a response for timeout seconds.
20-
:param bool debug: enable debug mode.
2126
:param bool retry: enable retry mode.
2227
'''
2328
self.timeout = timeout
24-
self.debug = debug
2529
self.retry = retry
2630

2731
@property
@@ -38,20 +42,6 @@ def timeout(self, timeout: int) -> None:
3842
'''
3943
self._timeout = timeout
4044

41-
@property
42-
def debug(self) -> bool:
43-
'''
44-
:type: :class:`bool`
45-
'''
46-
return self._debug
47-
48-
@debug.setter
49-
def debug(self, debug: bool) -> None:
50-
'''
51-
:param bool debug:
52-
'''
53-
self._debug = debug
54-
5545
@property
5646
def retry(self) -> bool:
5747
'''
@@ -81,15 +71,6 @@ def _curl_cmd(
8171
curl_cmd += f' {url}'
8272
return curl_cmd
8373

84-
def _print_debug(self, url, headers, payload, method) -> None:
85-
print('-' * 80)
86-
print(f'Method: {method}\n')
87-
print(f'URL:\n{url}\n')
88-
print(f'Headers:\n{headers}\n')
89-
print(f'Payload:\n{payload}\n')
90-
print(f'cURL command:\n{self._curl_cmd(url, method, headers, payload)}')
91-
print('-' * 80)
92-
9374
async def get_data_async(
9475
self,
9576
url: str,
@@ -129,8 +110,10 @@ async def _get_data_async(
129110
url = f'{url}?{parameters.lstrip("&")}'
130111
method = 'POST' if payload else 'GET'
131112

132-
if self.debug:
133-
self._print_debug(url, headers, payload, method)
113+
logger.debug(f'HTTP Request: {method} {url}')
114+
logger.debug(f'Headers: {headers}')
115+
logger.debug(f'Payload: {payload}')
116+
logger.debug(f'cURL command: {self._curl_cmd(url, method, headers, payload)}')
134117

135118
async with httpx.AsyncClient() as client:
136119
r = await client.request(

semanticscholar/AsyncSemanticScholar.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
import logging
12
import re
2-
from typing import List, Literal, Tuple, Union
33
import warnings
4+
from typing import List, Literal, Tuple, Union
45

56
from semanticscholar.ApiRequester import ApiRequester
67
from semanticscholar.Author import Author
@@ -10,6 +11,8 @@
1011
from semanticscholar.Paper import Paper
1112
from semanticscholar.Reference import Reference
1213

14+
logger = logging.getLogger('semanticscholar')
15+
1316

1417
class AsyncSemanticScholar:
1518
'''
@@ -40,6 +43,12 @@ def __init__(
4043
:param bool retry: enable retry mode.
4144
'''
4245

46+
if debug:
47+
warnings.warn(
48+
'The debug parameter is deprecated and will be removed in a \
49+
future release. Use Python\'s standard logging in DEBUG level \
50+
instead.')
51+
4352
if api_url:
4453
self.api_url = api_url
4554
else:
@@ -49,9 +58,9 @@ def __init__(
4958
self.auth_header = {'x-api-key': api_key}
5059

5160
self._timeout = timeout
52-
self._debug = debug
5361
self._retry = retry
54-
self._requester = ApiRequester(self._timeout, self._debug, self._retry)
62+
self._requester = ApiRequester(self._timeout, self._retry)
63+
self.debug = debug
5564

5665
@property
5766
def timeout(self) -> int:
@@ -81,7 +90,10 @@ def debug(self, debug: bool) -> None:
8190
:param bool debug:
8291
'''
8392
self._debug = debug
84-
self._requester.debug = debug
93+
if self._debug:
94+
logging.basicConfig(level=logging.DEBUG)
95+
else:
96+
logging.basicConfig(level=logging.WARNING)
8597

8698
@property
8799
def retry(self) -> bool:
@@ -190,7 +202,7 @@ async def get_papers(
190202
not_found_ids = self._get_not_found_ids(paper_ids, papers)
191203

192204
if not_found_ids:
193-
warnings.warn(f"IDs not found: {not_found_ids}")
205+
logger.warning(f"IDs not found: {not_found_ids}")
194206

195207
return papers if not return_not_found else (papers, not_found_ids)
196208

@@ -554,7 +566,7 @@ async def get_authors(
554566
not_found_ids = list(set(author_ids) - set(found_ids))
555567

556568
if not_found_ids:
557-
warnings.warn(f"IDs not found: {not_found_ids}")
569+
logger.warning(f"IDs not found: {not_found_ids}")
558570

559571
return authors if not return_not_found else (authors, not_found_ids)
560572

semanticscholar/SemanticScholar.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ def __init__(
3131
'''
3232
nest_asyncio.apply()
3333
self._timeout = timeout
34-
self._debug = debug
3534
self._retry = retry
3635
self._AsyncSemanticScholar = AsyncSemanticScholar(
3736
timeout=timeout,
@@ -40,6 +39,7 @@ def __init__(
4039
debug=debug,
4140
retry=retry
4241
)
42+
self.debug = debug
4343

4444
@property
4545
def timeout(self) -> int:

tests/data/debug_output.txt

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,4 @@
1-
--------------------------------------------------------------------------------
2-
Method: POST
3-
4-
URL:
5-
https://api.semanticscholar.org/graph/v1/paper/batch?fields=abstract,authors,citationCount,citationStyles,corpusId,externalIds,fieldsOfStudy,influentialCitationCount,isOpenAccess,journal,openAccessPdf,paperId,publicationDate,publicationTypes,publicationVenue,referenceCount,s2FieldsOfStudy,title,url,venue,year
6-
7-
Headers:
8-
{'x-api-key': 'F@k3K3y'}
9-
10-
Payload:
11-
{'ids': ['CorpusId:470667', '10.2139/ssrn.2250500', '0f40b1f08821e22e859c6050916cec3667778613']}
12-
13-
cURL command:
14-
curl -X POST -H 'x-api-key: F@k3K3y' -d '{"ids": ["CorpusId:470667", "10.2139/ssrn.2250500", "0f40b1f08821e22e859c6050916cec3667778613"]}' https://api.semanticscholar.org/graph/v1/paper/batch?fields=abstract,authors,citationCount,citationStyles,corpusId,externalIds,fieldsOfStudy,influentialCitationCount,isOpenAccess,journal,openAccessPdf,paperId,publicationDate,publicationTypes,publicationVenue,referenceCount,s2FieldsOfStudy,title,url,venue,year
15-
--------------------------------------------------------------------------------
1+
DEBUG:semanticscholar:HTTP Request: POST https://api.semanticscholar.org/graph/v1/paper/batch?fields=abstract,authors,citationCount,citationStyles,corpusId,externalIds,fieldsOfStudy,influentialCitationCount,isOpenAccess,journal,openAccessPdf,paperId,publicationDate,publicationTypes,publicationVenue,referenceCount,s2FieldsOfStudy,title,url,venue,year
2+
DEBUG:semanticscholar:Headers: {'x-api-key': 'F@k3K3y'}
3+
DEBUG:semanticscholar:Payload: {'ids': ['CorpusId:470667', '10.2139/ssrn.2250500', '0f40b1f08821e22e859c6050916cec3667778613']}
4+
DEBUG:semanticscholar:cURL command: curl -X POST -H 'x-api-key: F@k3K3y' -d '{"ids": ["CorpusId:470667", "10.2139/ssrn.2250500", "0f40b1f08821e22e859c6050916cec3667778613"]}' https://api.semanticscholar.org/graph/v1/paper/batch?fields=abstract,authors,citationCount,citationStyles,corpusId,externalIds,fieldsOfStudy,influentialCitationCount,isOpenAccess,journal,openAccessPdf,paperId,publicationDate,publicationTypes,publicationVenue,referenceCount,s2FieldsOfStudy,title,url,venue,year

tests/test_semanticscholar.py

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,31 @@
1+
import asyncio
12
import io
23
import json
4+
import logging
35
import sys
46
import unittest
5-
import asyncio
67
from datetime import datetime
7-
from httpx import TimeoutException
8+
89
import vcr
10+
from httpx import TimeoutException
911

10-
from semanticscholar.Author import Author
1112
from semanticscholar.AsyncSemanticScholar import AsyncSemanticScholar
13+
from semanticscholar.Author import Author
1214
from semanticscholar.Citation import Citation
1315
from semanticscholar.Journal import Journal
1416
from semanticscholar.Paper import Paper
1517
from semanticscholar.PublicationVenue import PublicationVenue
1618
from semanticscholar.Reference import Reference
1719
from semanticscholar.SemanticScholar import SemanticScholar
1820
from semanticscholar.SemanticScholarException import (
19-
BadQueryParametersException, ObjectNotFoundException, NoMorePagesException)
21+
BadQueryParametersException, NoMorePagesException, ObjectNotFoundException)
2022
from semanticscholar.Tldr import Tldr
2123

2224
test_vcr = vcr.VCR(
2325
cassette_library_dir='tests/data',
2426
path_transformer=vcr.VCR.ensure_suffix('.yaml'),
2527
record_mode=['new_episodes'],
26-
match_on=['uri', 'method', 'body'],
28+
match_on=['uri', 'method', 'raw_body'],
2729
drop_unused_requests=True
2830
)
2931

@@ -196,8 +198,9 @@ def test_get_papers_not_found_warning(self):
196198
'CorpusId:470667',
197199
'10.2139/ssrn.2250500',
198200
'0f40b1f08821e22e859c6050916cec3667778613']
199-
with self.assertWarns(UserWarning):
201+
with self.assertLogs(level='WARNING') as log:
200202
self.sch.get_papers(list_of_paper_ids)
203+
self.assertIn('IDs not found: [\'CorpusId:211530585\']', log.output[0])
201204

202205
@test_vcr.use_cassette
203206
def test_get_papers_return_not_found(self):
@@ -275,8 +278,9 @@ def test_get_authors_list_empty(self):
275278
@test_vcr.use_cassette
276279
def test_get_authors_not_found_warning(self):
277280
list_of_author_ids = ['0', '3234559', '1726629', '1711844']
278-
with self.assertWarns(UserWarning):
281+
with self.assertLogs(level='WARNING') as log:
279282
self.sch.get_authors(list_of_author_ids)
283+
self.assertIn('IDs not found: [\'0\']', log.output[0])
280284

281285
@test_vcr.use_cassette
282286
def test_get_authors_return_not_found(self):
@@ -563,21 +567,18 @@ def test_empty_paginated_results(self):
563567

564568
@test_vcr.use_cassette
565569
def test_debug(self):
570+
logging.basicConfig(level=logging.DEBUG)
566571
with open('tests/data/debug_output.txt', 'r') as file:
567572
expected_output = file.read()
568-
captured_stdout = io.StringIO()
569-
sys.stdout = captured_stdout
570-
self.sch = SemanticScholar(debug=True, api_key='F@k3K3y')
571-
self.assertEqual(self.sch.debug, True)
573+
self.sch = SemanticScholar(api_key='F@k3K3y')
572574
list_of_paper_ids = [
573575
'CorpusId:470667',
574576
'10.2139/ssrn.2250500',
575577
'0f40b1f08821e22e859c6050916cec3667778613']
576-
with self.assertRaises(PermissionError):
578+
with self.assertLogs(level='DEBUG') as log, \
579+
self.assertRaises(PermissionError):
577580
self.sch.get_papers(list_of_paper_ids)
578-
sys.stdout = sys.__stdout__
579-
self.assertEqual(captured_stdout.getvalue().strip(),
580-
expected_output.strip())
581+
self.assertIn(expected_output, log.output)
581582

582583
class AsyncSemanticScholarTest(unittest.IsolatedAsyncioTestCase):
583584

@@ -621,8 +622,9 @@ async def test_get_papers_not_found_warning_async(self):
621622
'CorpusId:470667',
622623
'10.2139/ssrn.2250500',
623624
'0f40b1f08821e22e859c6050916cec3667778613']
624-
with self.assertWarns(UserWarning):
625+
with self.assertLogs(level='WARNING') as log:
625626
await self.sch.get_papers(list_of_paper_ids)
627+
self.assertIn('IDs not found: [\'CorpusId:211530585\']', log.output[0])
626628

627629
@test_vcr.use_cassette
628630
async def test_get_papers_return_not_found_async(self):
@@ -689,8 +691,9 @@ async def test_get_authors_list_empty_async(self):
689691
@test_vcr.use_cassette
690692
async def test_get_authors_not_found_warning_async(self):
691693
list_of_author_ids = ['0', '3234559', '1726629', '1711844']
692-
with self.assertWarns(UserWarning):
694+
with self.assertLogs(level='WARNING') as log:
693695
await self.sch.get_authors(list_of_author_ids)
696+
self.assertIn('IDs not found: [\'0\']', log.output[0])
694697

695698
@test_vcr.use_cassette
696699
async def test_get_authors_return_not_found_async(self):

0 commit comments

Comments
 (0)