Skip to content

Commit

Permalink
feat: replace debug parameter with Python logging (DEBUG)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
danielnsilva committed Jun 1, 2024
1 parent 53015d2 commit d5bfa38
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 72 deletions.
49 changes: 16 additions & 33 deletions semanticscholar/ApiRequester.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
import asyncio
import json
import logging
import warnings
from typing import List, Union

import httpx
import asyncio
import warnings
import json
from tenacity import (retry as rerun, retry_if_exception_type,
stop_after_attempt, wait_fixed)
from tenacity import retry as rerun
from tenacity import retry_if_exception_type, stop_after_attempt, wait_fixed

from semanticscholar.SemanticScholarException import (
BadQueryParametersException, ObjectNotFoundException)

from semanticscholar.SemanticScholarException import \
BadQueryParametersException, ObjectNotFoundException

logger = logging.getLogger('semanticscholar')


class ApiRequester:

def __init__(self, timeout, debug, retry: bool = True) -> None:
def __init__(self, timeout, retry: bool = True) -> None:
'''
:param float timeout: an exception is raised \
if the server has not issued a response for timeout seconds.
:param bool debug: enable debug mode.
:param bool retry: enable retry mode.
'''
self.timeout = timeout
self.debug = debug
self.retry = retry

@property
Expand All @@ -38,20 +42,6 @@ def timeout(self, timeout: int) -> None:
'''
self._timeout = timeout

@property
def debug(self) -> bool:
'''
:type: :class:`bool`
'''
return self._debug

@debug.setter
def debug(self, debug: bool) -> None:
'''
:param bool debug:
'''
self._debug = debug

@property
def retry(self) -> bool:
'''
Expand Down Expand Up @@ -81,15 +71,6 @@ def _curl_cmd(
curl_cmd += f' {url}'
return curl_cmd

def _print_debug(self, url, headers, payload, method) -> None:
print('-' * 80)
print(f'Method: {method}\n')
print(f'URL:\n{url}\n')
print(f'Headers:\n{headers}\n')
print(f'Payload:\n{payload}\n')
print(f'cURL command:\n{self._curl_cmd(url, method, headers, payload)}')
print('-' * 80)

async def get_data_async(
self,
url: str,
Expand Down Expand Up @@ -129,8 +110,10 @@ async def _get_data_async(
url = f'{url}?{parameters.lstrip("&")}'
method = 'POST' if payload else 'GET'

if self.debug:
self._print_debug(url, headers, payload, method)
logger.debug(f'HTTP Request: {method} {url}')
logger.debug(f'Headers: {headers}')
logger.debug(f'Payload: {payload}')
logger.debug(f'cURL command: {self._curl_cmd(url, method, headers, payload)}')

async with httpx.AsyncClient() as client:
r = await client.request(
Expand Down
24 changes: 18 additions & 6 deletions semanticscholar/AsyncSemanticScholar.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import logging
import re
from typing import List, Literal, Tuple, Union
import warnings
from typing import List, Literal, Tuple, Union

from semanticscholar.ApiRequester import ApiRequester
from semanticscholar.Author import Author
Expand All @@ -10,6 +11,8 @@
from semanticscholar.Paper import Paper
from semanticscholar.Reference import Reference

logger = logging.getLogger('semanticscholar')


class AsyncSemanticScholar:
'''
Expand Down Expand Up @@ -40,6 +43,12 @@ def __init__(
:param bool retry: enable retry mode.
'''

if debug:
warnings.warn(
'The debug parameter is deprecated and will be removed in a \
future release. Use Python\'s standard logging in DEBUG level \
instead.')

if api_url:
self.api_url = api_url
else:
Expand All @@ -49,9 +58,9 @@ def __init__(
self.auth_header = {'x-api-key': api_key}

self._timeout = timeout
self._debug = debug
self._retry = retry
self._requester = ApiRequester(self._timeout, self._debug, self._retry)
self._requester = ApiRequester(self._timeout, self._retry)
self.debug = debug

@property
def timeout(self) -> int:
Expand Down Expand Up @@ -81,7 +90,10 @@ def debug(self, debug: bool) -> None:
:param bool debug:
'''
self._debug = debug
self._requester.debug = debug
if self._debug:
logging.basicConfig(level=logging.DEBUG)
else:
logging.basicConfig(level=logging.WARNING)

@property
def retry(self) -> bool:
Expand Down Expand Up @@ -190,7 +202,7 @@ async def get_papers(
not_found_ids = self._get_not_found_ids(paper_ids, papers)

if not_found_ids:
warnings.warn(f"IDs not found: {not_found_ids}")
logger.warning(f"IDs not found: {not_found_ids}")

return papers if not return_not_found else (papers, not_found_ids)

Expand Down Expand Up @@ -554,7 +566,7 @@ async def get_authors(
not_found_ids = list(set(author_ids) - set(found_ids))

if not_found_ids:
warnings.warn(f"IDs not found: {not_found_ids}")
logger.warning(f"IDs not found: {not_found_ids}")

return authors if not return_not_found else (authors, not_found_ids)

Expand Down
2 changes: 1 addition & 1 deletion semanticscholar/SemanticScholar.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ def __init__(
'''
nest_asyncio.apply()
self._timeout = timeout
self._debug = debug
self._retry = retry
self._AsyncSemanticScholar = AsyncSemanticScholar(
timeout=timeout,
Expand All @@ -40,6 +39,7 @@ def __init__(
debug=debug,
retry=retry
)
self.debug = debug

@property
def timeout(self) -> int:
Expand Down
19 changes: 4 additions & 15 deletions tests/data/debug_output.txt
Original file line number Diff line number Diff line change
@@ -1,15 +1,4 @@
--------------------------------------------------------------------------------
Method: POST

URL:
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

Headers:
{'x-api-key': 'F@k3K3y'}

Payload:
{'ids': ['CorpusId:470667', '10.2139/ssrn.2250500', '0f40b1f08821e22e859c6050916cec3667778613']}

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
--------------------------------------------------------------------------------
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
DEBUG:semanticscholar:Headers: {'x-api-key': 'F@k3K3y'}
DEBUG:semanticscholar:Payload: {'ids': ['CorpusId:470667', '10.2139/ssrn.2250500', '0f40b1f08821e22e859c6050916cec3667778613']}
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
37 changes: 20 additions & 17 deletions tests/test_semanticscholar.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
import asyncio
import io
import json
import logging
import sys
import unittest
import asyncio
from datetime import datetime
from httpx import TimeoutException

import vcr
from httpx import TimeoutException

from semanticscholar.Author import Author
from semanticscholar.AsyncSemanticScholar import AsyncSemanticScholar
from semanticscholar.Author import Author
from semanticscholar.Citation import Citation
from semanticscholar.Journal import Journal
from semanticscholar.Paper import Paper
from semanticscholar.PublicationVenue import PublicationVenue
from semanticscholar.Reference import Reference
from semanticscholar.SemanticScholar import SemanticScholar
from semanticscholar.SemanticScholarException import (
BadQueryParametersException, ObjectNotFoundException, NoMorePagesException)
BadQueryParametersException, NoMorePagesException, ObjectNotFoundException)
from semanticscholar.Tldr import Tldr

test_vcr = vcr.VCR(
cassette_library_dir='tests/data',
path_transformer=vcr.VCR.ensure_suffix('.yaml'),
record_mode=['new_episodes'],
match_on=['uri', 'method', 'body'],
match_on=['uri', 'method', 'raw_body'],
drop_unused_requests=True
)

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

@test_vcr.use_cassette
def test_get_papers_return_not_found(self):
Expand Down Expand Up @@ -275,8 +278,9 @@ def test_get_authors_list_empty(self):
@test_vcr.use_cassette
def test_get_authors_not_found_warning(self):
list_of_author_ids = ['0', '3234559', '1726629', '1711844']
with self.assertWarns(UserWarning):
with self.assertLogs(level='WARNING') as log:
self.sch.get_authors(list_of_author_ids)
self.assertIn('IDs not found: [\'0\']', log.output[0])

@test_vcr.use_cassette
def test_get_authors_return_not_found(self):
Expand Down Expand Up @@ -563,21 +567,18 @@ def test_empty_paginated_results(self):

@test_vcr.use_cassette
def test_debug(self):
logging.basicConfig(level=logging.DEBUG)
with open('tests/data/debug_output.txt', 'r') as file:
expected_output = file.read()
captured_stdout = io.StringIO()
sys.stdout = captured_stdout
self.sch = SemanticScholar(debug=True, api_key='F@k3K3y')
self.assertEqual(self.sch.debug, True)
self.sch = SemanticScholar(api_key='F@k3K3y')
list_of_paper_ids = [
'CorpusId:470667',
'10.2139/ssrn.2250500',
'0f40b1f08821e22e859c6050916cec3667778613']
with self.assertRaises(PermissionError):
with self.assertLogs(level='DEBUG') as log, \
self.assertRaises(PermissionError):
self.sch.get_papers(list_of_paper_ids)
sys.stdout = sys.__stdout__
self.assertEqual(captured_stdout.getvalue().strip(),
expected_output.strip())
self.assertIn(expected_output, log.output)

class AsyncSemanticScholarTest(unittest.IsolatedAsyncioTestCase):

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

@test_vcr.use_cassette
async def test_get_papers_return_not_found_async(self):
Expand Down Expand Up @@ -689,8 +691,9 @@ async def test_get_authors_list_empty_async(self):
@test_vcr.use_cassette
async def test_get_authors_not_found_warning_async(self):
list_of_author_ids = ['0', '3234559', '1726629', '1711844']
with self.assertWarns(UserWarning):
with self.assertLogs(level='WARNING') as log:
await self.sch.get_authors(list_of_author_ids)
self.assertIn('IDs not found: [\'0\']', log.output[0])

@test_vcr.use_cassette
async def test_get_authors_return_not_found_async(self):
Expand Down

0 comments on commit d5bfa38

Please sign in to comment.