Skip to content

Commit

Permalink
Add mypy type-checking, recode to comply (#193)
Browse files Browse the repository at this point in the history
  • Loading branch information
deedy5 authored Mar 7, 2024
1 parent 6b46884 commit 21d4774
Show file tree
Hide file tree
Showing 8 changed files with 247 additions and 274 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ jobs:
run: |
ruff check .
ruff format . --check --target-version py38
- name: Mypy
run: |
python -m pip install orjson
mypy --install-types .
- name: Pytest
run: |
pytest
40 changes: 17 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,17 +190,11 @@ Here is an example of initializing the AsyncDDGS class:
```python3
import asyncio
import logging
import sys

from duckduckgo_search import AsyncDDGS

# bypass curl-cffi NotImplementedError in windows https://curl-cffi.readthedocs.io/en/latest/faq/
if sys.platform.lower().startswith("win"):
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())

async def aget_results(word):
addgs = AsyncDDGS(proxies=None)
results = await addgs.text(word, max_results=100)
results = await AsyncDDGS(proxies=None).text(word, max_results=100)
return results

async def main():
Expand Down Expand Up @@ -257,7 +251,7 @@ def text(
timelimit: Optional[str] = None,
backend: str = "api",
max_results: Optional[int] = None,
) -> Optional[List[Dict[str, Optional[str]]]]:
) -> List[Dict[str, str]]:
"""DuckDuckGo text search generator. Query params: https://duckduckgo.com/params.
Args:
Expand All @@ -272,7 +266,7 @@ def text(
max_results: max number of results. If None, returns results only from the first response. Defaults to None.
Returns:
List of dictionaries with search results, or None if there was an error.
List of dictionaries with search results.
Raises:
DuckDuckGoSearchException: Raised when there is a generic exception during the API request.
Expand All @@ -296,14 +290,14 @@ results = await AsyncDDGS().text('sun', region='wt-wt', safesearch='off', timeli
## 2. answers() - instant answers by duckduckgo.com

```python
def answers(keywords: str) -> Optional[List[Dict[str, Optional[str]]]]:
def answers(keywords: str) -> List[Dict[str, str]]:
"""DuckDuckGo instant answers. Query params: https://duckduckgo.com/params.
Args:
keywords: keywords for query,
Returns:
List of dictionaries with instant answers results, or None if there was an error.
List of dictionaries with instant answers results.
Raises:
DuckDuckGoSearchException: Raised when there is a generic exception during the API request.
Expand Down Expand Up @@ -335,7 +329,7 @@ def images(
layout: Optional[str] = None,
license_image: Optional[str] = None,
max_results: Optional[int] = None,
) -> Optional[List[Dict[str, Optional[str]]]]:
) -> List[Dict[str, str]]:
"""DuckDuckGo images search. Query params: https://duckduckgo.com/params.
Args:
Expand All @@ -356,7 +350,7 @@ def images(
max_results: max number of results. If None, returns results only from the first response. Defaults to None.
Returns:
List of dictionaries with images search results, or None if there was an error.
List of dictionaries with images search results.
Raises:
DuckDuckGoSearchException: Raised when there is a generic exception during the API request.
Expand Down Expand Up @@ -396,7 +390,7 @@ def videos(
duration: Optional[str] = None,
license_videos: Optional[str] = None,
max_results: Optional[int] = None,
) -> Optional[List[Dict[str, Optional[str]]]]:
) -> List[Dict[str, str]]:
"""DuckDuckGo videos search. Query params: https://duckduckgo.com/params.
Args:
Expand All @@ -410,7 +404,7 @@ def videos(
max_results: max number of results. If None, returns results only from the first response. Defaults to None.
Returns:
List of dictionaries with videos search results, or None if there was an error.
List of dictionaries with videos search results.
Raises:
DuckDuckGoSearchException: Raised when there is a generic exception during the API request.
Expand Down Expand Up @@ -445,7 +439,7 @@ def news(
safesearch: str = "moderate",
timelimit: Optional[str] = None,
max_results: Optional[int] = None,
) -> Optional[List[Dict[str, Optional[str]]]]:
) -> List[Dict[str, str]]:
"""DuckDuckGo news search. Query params: https://duckduckgo.com/params.
Args:
Expand All @@ -456,7 +450,7 @@ def news(
max_results: max number of results. If None, returns results only from the first response. Defaults to None.
Returns:
List of dictionaries with news search results, or None if there was an error.
List of dictionaries with news search results.
Raises:
DuckDuckGoSearchException: Raised when there is a generic exception during the API request.
Expand Down Expand Up @@ -490,7 +484,7 @@ def maps(
longitude: Optional[str] = None,
radius: int = 0,
max_results: Optional[int] = None,
) -> Optional[List[Dict[str, Optional[str]]]]:
) -> List[Dict[str, str]]:
"""DuckDuckGo maps search. Query params: https://duckduckgo.com/params.
Args:
Expand All @@ -509,7 +503,7 @@ def maps(
max_results: max number of results. If None, returns results only from the first response. Defaults to None.
Returns:
List of dictionaries with maps search results, or None if there was an error.
List of dictionaries with maps search results.
Raises:
DuckDuckGoSearchException: Raised when there is a generic exception during the API request.
Expand All @@ -535,7 +529,7 @@ def translate(
keywords: str,
from_: Optional[str] = None,
to: str = "en",
) -> Optional[List[Dict[str, Optional[str]]]]:
) -> List[Dict[str, str]]:
"""DuckDuckGo translate.
Args:
Expand All @@ -544,7 +538,7 @@ def translate(
to: what language to translate. Defaults to "en".
Returns:
List od dictionaries with translated keywords, or None if there was an error.
List od dictionaries with translated keywords.
Raises:
DuckDuckGoSearchException: Raised when there is a generic exception during the API request.
Expand All @@ -571,15 +565,15 @@ results = await AsyncDDGS().translate('sun', to="de")
def suggestions(
keywords,
region: str = "wt-wt",
) -> Optional[List[Dict[str, Optional[str]]]]:
) -> List[Dict[str, str]]:
"""DuckDuckGo suggestions. Query params: https://duckduckgo.com/params.
Args:
keywords: keywords for query.
region: wt-wt, us-en, uk-en, ru-ru, etc. Defaults to "wt-wt".
Returns:
List of dictionaries with suggestions results, or None if there was an error.
List of dictionaries with suggestions results.
Raises:
DuckDuckGoSearchException: Raised when there is a generic exception during the API request.
Expand Down
51 changes: 32 additions & 19 deletions duckduckgo_search/duckduckgo_search.py
Original file line number Diff line number Diff line change
@@ -1,51 +1,64 @@
import asyncio
from concurrent.futures import Future
from threading import Thread
from typing import Coroutine, Dict, List, Optional
from types import TracebackType
from typing import Any, Awaitable, Dict, Optional, Type, Union

from .duckduckgo_search_async import AsyncDDGS

# Create an event loop and run it in a separate thread.
_SHARED_LOOP = asyncio.new_event_loop()
_SHARED_THREAD = Thread(target=_SHARED_LOOP.run_forever, daemon=True)
_SHARED_LOOP: asyncio.AbstractEventLoop = asyncio.new_event_loop()
_SHARED_THREAD: Thread = Thread(target=_SHARED_LOOP.run_forever, daemon=True)
_SHARED_THREAD.start()


class DDGS(AsyncDDGS):
def __init__(self, headers=None, proxies=None, timeout=10) -> None:
super().__init__(headers, proxies, timeout)
def __init__(
self,
headers: Optional[Dict[str, str]] = None,
proxies: Union[Dict[str, str], str, None] = None,
timeout: Optional[int] = 10,
) -> None:
super().__init__(headers=headers, proxies=proxies, timeout=timeout)
self._loop = _SHARED_LOOP

def __enter__(self):
def __enter__(self) -> "DDGS":
return self

def __exit__(self, exc_type, exc_val, exc_tb):
pass
def __exit__(
self,
exc_type: Optional[Type[BaseException]],
exc_val: Optional[BaseException],
exc_tb: Optional[TracebackType],
) -> bool:
return True

def _run_async_in_thread(self, coro: Coroutine) -> Optional[List[Dict[str, Optional[str]]]]:
def _run_async_in_thread(self, coro: Awaitable[Any]) -> Any:
"""Runs an async coroutine in a separate thread."""
future = asyncio.run_coroutine_threadsafe(coro, self._loop)
return future.result()
future: Future[Any] = asyncio.run_coroutine_threadsafe(coro, self._loop)
result = future.result()
return result

def text(self, *args, **kwargs) -> Optional[List[Dict[str, Optional[str]]]]:
def text(self, *args: Any, **kwargs: Any) -> Any:
return self._run_async_in_thread(super().text(*args, **kwargs))

def images(self, *args, **kwargs) -> Optional[List[Dict[str, Optional[str]]]]:
def images(self, *args: Any, **kwargs: Any) -> Any:
return self._run_async_in_thread(super().images(*args, **kwargs))

def videos(self, *args, **kwargs) -> Optional[List[Dict[str, Optional[str]]]]:
def videos(self, *args: Any, **kwargs: Any) -> Any:
return self._run_async_in_thread(super().videos(*args, **kwargs))

def news(self, *args, **kwargs) -> Optional[List[Dict[str, Optional[str]]]]:
def news(self, *args: Any, **kwargs: Any) -> Any:
return self._run_async_in_thread(super().news(*args, **kwargs))

def answers(self, *args, **kwargs) -> Optional[List[Dict[str, Optional[str]]]]:
def answers(self, *args: Any, **kwargs: Any) -> Any:
return self._run_async_in_thread(super().answers(*args, **kwargs))

def suggestions(self, *args, **kwargs) -> Optional[List[Dict[str, Optional[str]]]]:
def suggestions(self, *args: Any, **kwargs: Any) -> Any:
return self._run_async_in_thread(super().suggestions(*args, **kwargs))

def maps(self, *args, **kwargs) -> Optional[List[Dict[str, Optional[str]]]]:
def maps(self, *args: Any, **kwargs: Any) -> Any:
return self._run_async_in_thread(super().maps(*args, **kwargs))

def translate(self, *args, **kwargs) -> Optional[List[Dict[str, Optional[str]]]]:
def translate(self, *args: Any, **kwargs: Any) -> Any:
return self._run_async_in_thread(super().translate(*args, **kwargs))
Loading

0 comments on commit 21d4774

Please sign in to comment.