Imported from gencau/test-practices-agent-configurations (
dataset/repos/deedy5§ddgs/AGENTS.md). Install upstream withnpx skills add gencau/test-practices-agent-configurations --skill deedy5§ddgs. Copyright stays with the author.
AGENTS.md — Guide for Coding Agents
Project Overview
ddgs (Dux Distributed Global Search) is a Python metasearch library that aggregates results from diverse web search services. It provides a DDGS class with methods like text(), images(), news(), videos(), books(), and extract().
- Python: >=3.10 (tested up to 3.14)
- Dependencies:
click,primp,lxml - Package layout:
ddgs/(source),tests/(tests), optionalapiextras (FastAPI + MCP)
Build / Lint / Test Commands
# Setup (creates .venv, installs dev deps)
python3 -m venv .venv
.venv/bin/pip install -e .[dev]
# Lint (ruff check + mypy type-check)
.venv/bin/ruff check --fix
.venv/bin/mypy --install-types --non-interactive .
# Format
.venv/bin/ruff format
# Run all tests
.venv/bin/pytest
# Run a single test file
.venv/bin/pytest tests/ddgs_test.py
# Run a single test function
.venv/bin/pytest tests/ddgs_test.py::test_text_search
# Make targets (use .venv/bin/python internally)
make lint
make format
make test
Important: Tests make real HTTP requests and include a 2-second pause between tests (autouse fixture). Tests are slow by nature.
Engine Architecture
Each search engine is a subclass of BaseSearchEngine[T] in ddgs/engines/. Key concepts:
ENGINESdict inddgs/engines/__init__.pymaps category → {name → engine class}.DDGS._search()spawns threads viaThreadPoolExecutor, callingengine.search()in parallel.- Results are deduplicated and ranked by
ResultsAggregatorandSimpleFilterRanker. BaseSearchEnginehandles HTTP requests, HTML parsing (lxml), and XPath-based result extraction.- Engine instances are cached per
DDGSinstance in_engines_cache.
Pre-commit Hooks
Pre-commit is managed via prek (not pre-commit). Install with prek install. Hooks run on commit:
- Standard checks (large files, AST, trailing whitespace, etc.)
ruff check --fixruff formatmypy --install-types --non-interactive .
Run all hooks manually: prek
Code Style Guidelines
Imports
- Standard library first, then third-party, then local (enforced by ruff
Irules / isort). - Use relative imports within the
ddgspackage (e.g.,from .base import BaseSearchEngine). - Do not use
from __future__ import annotations; use Python 3.10+ union syntax directly:str | None. - Guard type-only imports with
if TYPE_CHECKING:to avoid runtime imports.
Typing
- mypy strict mode is enabled. All functions must have full type annotations (parameters and return types).
- Use
ClassVarfor class-level attributes on classes. - Use
Anysparingly; prefer concrete types. Use# noqa: ANN401whenAnyis intentional. - Use generics (
Generic[T]) for result-type polymorphism (seeBaseSearchEngine[T]). - Union types:
str | None(notOptional[str]),bool | str(notUnion[bool, str]). - Use
Literalfor constrained string values (e.g.,Literal["text", "images"]).
Naming
- Classes: PascalCase (e.g.,
BaseSearchEngine,HttpClient,Duckduckgo). - Functions/methods: snake_case (e.g.,
build_payload,extract_results). - Constants: UPPER_SNAKE_CASE (e.g.,
ENGINES). - Private: prefix with
_(e.g.,_search,_get_engines,_normalize_url). - Test files:
*_test.py(nottest_*.py). - Test functions:
test_*(e.g.,test_text_search).
Formatting
- Line length: 120 characters max (
ruffsetting). - Quotes: double quotes preferred (ruff formatter default).
- Trailing commas: handled by formatter.
- Docstrings: Google style, required on all public classes/methods (
pydocstyle Drules enabled). Exception:D107(missing__init__docstring) is ignored.
Error Handling
- Raise custom exceptions from
ddgs/exceptions.py:DDGSException(base),RatelimitException,TimeoutException. - Pattern: define a local
msgvariable, thenraise SomeException(msg). - Use
raise ... from exwhen re-raising to preserve exception chains. - Use
logger.info()/logger.warning()for non-fatal errors (notprint(); ruffT20bans print). - Bare
exceptis forbidden; useexcept Exception as ex:with# noqa: BLE001if catching all.
General Patterns
- Keyword-only args after
*(e.g.,*, verify: bool | str = True). - Use
@classmethodand@staticmethodwhere appropriate. - Use
@dataclassfor result data structures (seeTextResult,ImagesResult, etc.). - Use
__slots__for lightweight wrapper classes (seeResponse). - Use
cached_propertyfor expensive lazy computations. - Suppress ruff rules inline with
# noqa: RULEwhen justified (document why implicitly by context).
Adding a New Search Engine
- Create
ddgs/engines/<engine_name>.py. - Subclass
BaseSearchEngine[<ResultType>]. - Set class variables:
name,category,provider,search_url,search_method,items_xpath,elements_xpath. - Implement
build_payload(). - Register in
ddgs/engines/__init__.pyunder the correct categoryENGINESdict. - Add integration tests in
tests/.
CLI
The CLI is built with Click (ddgs/cli.py). Entry point: ddgs command via ddgs.cli:safe_entry_point.
# Text search
.venv/bin/ddgs text -q "python" --max-results 5
# Images, news, videos, books
.venv/bin/ddgs images -q "cats"
.venv/bin/ddgs news -q "tech" --timelimit d
# Save results
.venv/bin/ddgs text -q "dogs" -o results.json --format json
.venv/bin/ddgs text -q "dogs" -o results.csv --format csv
Adding Tests
- Test files live in
tests/and are named*_test.py. - Use
pytestfixtures. Thepause_between_testsautouse fixture adds a 2s delay between tests. - Tests are integration tests that make real network calls — do not mock HTTP.
- Use
CliRunnerfrom Click for CLI tests.
Things to Avoid
- Do not use
print()— uselogginginstead (ruffT20bans print). - Do not use
Optional[T]— useT | None. - Do not import
TYPE_CHECKING-guarded modules at runtime. - Do not add
.gitignore-listed or generated files (.mypy_cache/,build/,*.egg-info/). - Do not commit without running
make lintandmake testfirst. - Do not skip the 2s test pause — it prevents rate-limiting from engines.
- Do not create
test_*files — use*_test.pynaming convention.
Using ddgs as a Search Tool
If you need to search the web, extract URLs, or find images/news/videos/books,
see skills/ddgs/SKILL.md for Python API, CLI, and MCP integration guidance.