diff --git a/requirements.txt b/requirements.txt index 8a38e59..0150282 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,2 @@ tenacity httpx -nest_asyncio diff --git a/semanticscholar/PaginatedResults.py b/semanticscholar/PaginatedResults.py index bf622af..2addb29 100644 --- a/semanticscholar/PaginatedResults.py +++ b/semanticscholar/PaginatedResults.py @@ -1,5 +1,6 @@ from typing import Any, Union, List import asyncio +import concurrent.futures from semanticscholar.ApiRequester import ApiRequester from semanticscholar.SemanticScholarException import NoMorePagesException @@ -156,8 +157,13 @@ def _get_next_page(self) -> list: self._build_params() - loop = asyncio.get_event_loop() - results = loop.run_until_complete(self._request_data()) + try: + asyncio.get_running_loop() + with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool: + future = pool.submit(asyncio.run, self._request_data()) + results = future.result() + except RuntimeError: + results = asyncio.run(self._request_data()) return self._update_params(results) diff --git a/semanticscholar/SemanticScholar.py b/semanticscholar/SemanticScholar.py index 809f2cf..19b2841 100644 --- a/semanticscholar/SemanticScholar.py +++ b/semanticscholar/SemanticScholar.py @@ -1,6 +1,6 @@ from typing import List, Literal, Tuple, Union import asyncio -import nest_asyncio +import concurrent.futures from semanticscholar.PaginatedResults import PaginatedResults from semanticscholar.AsyncSemanticScholar import AsyncSemanticScholar @@ -12,6 +12,18 @@ from semanticscholar.Autocomplete import Autocomplete +def _run_async(coro): + '''Run an async coroutine from synchronous code, even if an event + loop is already running (e.g. Jupyter notebooks).''' + try: + asyncio.get_running_loop() + with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool: + future = pool.submit(asyncio.run, coro) + return future.result() + except RuntimeError: + return asyncio.run(coro) + + class SemanticScholar(): ''' Main class to retrieve data from Semantic Scholar Graph API synchronously. @@ -33,7 +45,6 @@ def __init__( :param bool debug: (optional) enable debug mode. :param bool retry: enable retry mode. ''' - nest_asyncio.apply() self._timeout = timeout self._retry = retry self._AsyncSemanticScholar = AsyncSemanticScholar( @@ -126,8 +137,7 @@ def get_paper( :raises: ObjectNotFoundException: if Paper ID not found. ''' - loop = asyncio.get_event_loop() - paper = loop.run_until_complete( + paper = _run_async( self._AsyncSemanticScholar.get_paper( paper_id=paper_id, fields=fields @@ -169,8 +179,7 @@ def get_papers( :raises: BadQueryParametersException: if no paper was found. ''' - loop = asyncio.get_event_loop() - papers = loop.run_until_complete( + papers = _run_async( self._AsyncSemanticScholar.get_papers( paper_ids=paper_ids, fields=fields, @@ -207,8 +216,7 @@ def get_paper_authors( (must be <= 1000). ''' - loop = asyncio.get_event_loop() - results = loop.run_until_complete( + results = _run_async( self._AsyncSemanticScholar.get_paper_authors( paper_id=paper_id, fields=fields, @@ -245,8 +253,7 @@ def get_paper_citations( (must be <= 1000). ''' - loop = asyncio.get_event_loop() - results = loop.run_until_complete( + results = _run_async( self._AsyncSemanticScholar.get_paper_citations( paper_id=paper_id, fields=fields, @@ -283,8 +290,7 @@ def get_paper_references( (must be <= 1000). ''' - loop = asyncio.get_event_loop() - results = loop.run_until_complete( + results = _run_async( self._AsyncSemanticScholar.get_paper_references( paper_id=paper_id, fields=fields, @@ -358,8 +364,7 @@ def search_paper( :class:`semanticscholar.Paper.Paper` ''' - loop = asyncio.get_event_loop() - results = loop.run_until_complete( + results = _run_async( self._AsyncSemanticScholar.search_paper( query=query, year=year, @@ -397,8 +402,7 @@ def get_author( :raises: ObjectNotFoundException: if Author ID not found. ''' - loop = asyncio.get_event_loop() - author = loop.run_until_complete( + author = _run_async( self._AsyncSemanticScholar.get_author( author_id=author_id, fields=fields @@ -429,8 +433,7 @@ def get_authors( :raises: BadQueryParametersException: if no author was found. ''' - loop = asyncio.get_event_loop() - authors = loop.run_until_complete( + authors = _run_async( self._AsyncSemanticScholar.get_authors( author_ids=author_ids, fields=fields, @@ -467,8 +470,7 @@ def get_author_papers( (must be <= 1000). ''' - loop = asyncio.get_event_loop() - results = loop.run_until_complete( + results = _run_async( self._AsyncSemanticScholar.get_author_papers( author_id=author_id, fields=fields, @@ -499,8 +501,7 @@ def search_author( :rtype: :class:`semanticscholar.PaginatedResults.PaginatedResults` ''' - loop = asyncio.get_event_loop() - results = loop.run_until_complete( + results = _run_async( self._AsyncSemanticScholar.search_author( query=query, fields=fields, @@ -542,8 +543,7 @@ def get_recommended_papers( :rtype: :class:`List` of :class:`semanticscholar.Paper.Paper` ''' - loop = asyncio.get_event_loop() - papers = loop.run_until_complete( + papers = _run_async( self._AsyncSemanticScholar.get_recommended_papers( paper_id=paper_id, fields=fields, @@ -579,8 +579,7 @@ def get_recommended_papers_from_lists( :rtype: :class:`List` of :class:`semanticscholar.Paper.Paper` ''' - loop = asyncio.get_event_loop() - papers = loop.run_until_complete( + papers = _run_async( self._AsyncSemanticScholar.get_recommended_papers_from_lists( positive_paper_ids=positive_paper_ids, negative_paper_ids=negative_paper_ids, @@ -605,8 +604,7 @@ def get_autocomplete(self, query: str) -> List[Autocomplete]: :class:`semanticscholar.Autocomplete.Autocomplete` """ - loop = asyncio.get_event_loop() - results = loop.run_until_complete( + results = _run_async( self._AsyncSemanticScholar.get_autocomplete(query=query) ) @@ -624,8 +622,7 @@ def get_available_releases(self) -> List[str]: :rtype: :class:`List` of :class:`str` """ - loop = asyncio.get_event_loop() - releases = loop.run_until_complete( + releases = _run_async( self._AsyncSemanticScholar.get_available_releases() ) @@ -644,8 +641,7 @@ def get_release(self, release_id: str) -> Release: :rtype: :class:`semanticscholar.Release.Release` """ - loop = asyncio.get_event_loop() - release = loop.run_until_complete( + release = _run_async( self._AsyncSemanticScholar.get_release(release_id=release_id) ) @@ -669,8 +665,7 @@ def get_dataset_download_links( :rtype: :class:`semanticscholar.Dataset.Dataset` """ - loop = asyncio.get_event_loop() - dataset = loop.run_until_complete( + dataset = _run_async( self._AsyncSemanticScholar.get_dataset_download_links( release_id=release_id, dataset_name=dataset_name @@ -703,8 +698,7 @@ def get_dataset_diffs( :rtype: :class:`semanticscholar.DatasetDiff.DatasetDiff` """ - loop = asyncio.get_event_loop() - result = loop.run_until_complete( + result = _run_async( self._AsyncSemanticScholar.get_dataset_diffs( dataset_name=dataset_name, start_release_id=start_release_id, diff --git a/setup.py b/setup.py index b63e1c2..2c1ded9 100644 --- a/setup.py +++ b/setup.py @@ -20,7 +20,7 @@ license='MIT', packages=['semanticscholar'], python_requires='>=3.10', - install_requires=['tenacity', 'httpx', 'nest_asyncio'], + install_requires=['tenacity', 'httpx'], test_suite='tests', tests_require=['vcrpy>=8.0'], classifiers=[