"""Bound the upstream work requested by a single search.""" MAX_SEARCH_TERMS = 20 MAX_TEXT_TERM_CHARS = 2_000 MAX_TOTAL_TEXT_CHARS = 10_000 class SearchLimitError(ValueError): """A search exceeds the public per-search limits.""" def validate_search_size(text_queries=None, image_queries=None): """Reject the whole search before any terms are sent upstream. Count Python Unicode characters, consistently for direct and shared searches. This intentionally imposes no per-user or per-IP search quota. """ text_queries = [] if text_queries is None else text_queries image_queries = [] if image_queries is None else image_queries if not isinstance(text_queries, (list, tuple)) or not isinstance(image_queries, (list, tuple)): raise SearchLimitError("Search terms must be supplied as lists.") if len(text_queries) + len(image_queries) > MAX_SEARCH_TERMS: raise SearchLimitError(f"Use at most {MAX_SEARCH_TERMS} text and image terms per search.") total_chars = 0 for query in text_queries: if not isinstance(query, str): raise SearchLimitError("Each text term must be text.") if len(query) > MAX_TEXT_TERM_CHARS: raise SearchLimitError(f"Use at most {MAX_TEXT_TERM_CHARS:,} characters per text term.") total_chars += len(query) if total_chars > MAX_TOTAL_TEXT_CHARS: raise SearchLimitError(f"Use at most {MAX_TOTAL_TEXT_CHARS:,} text characters per search.")