dataquery-sdk 1.2.2__tar.gz → 1.2.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/CHANGELOG.md +5 -0
  2. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/PKG-INFO +1 -1
  3. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/__init__.py +10 -16
  4. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/cli.py +2 -17
  5. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/config/env.py +5 -34
  6. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/config/logging.py +3 -30
  7. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/constants/__init__.py +1 -13
  8. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/constants/api.py +1 -5
  9. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/constants/download.py +0 -9
  10. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/core/_mixins.py +92 -113
  11. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/core/_sync.py +2 -21
  12. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/core/client.py +91 -290
  13. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/dataquery.py +116 -529
  14. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/download/__init__.py +1 -2
  15. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/download/parallel.py +7 -61
  16. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/download/utils.py +3 -19
  17. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/export.py +2 -10
  18. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/function_registry.py +1 -9
  19. dataquery_sdk-1.2.3/dataquery/mcp_proxy.py +265 -0
  20. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/sse/__init__.py +1 -2
  21. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/sse/client.py +14 -146
  22. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/sse/event_store.py +8 -56
  23. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/sse/subscriber.py +12 -185
  24. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/transport/__init__.py +1 -2
  25. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/transport/auth.py +9 -39
  26. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/transport/connection_pool.py +3 -23
  27. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/transport/rate_limiter.py +4 -41
  28. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/transport/retry.py +5 -48
  29. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/types/exceptions.py +14 -3
  30. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/types/models.py +43 -56
  31. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/utils.py +21 -232
  32. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery_sdk.egg-info/PKG-INFO +1 -1
  33. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery_sdk.egg-info/SOURCES.txt +1 -0
  34. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/CONTRIBUTING.md +0 -0
  35. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/LICENSE +0 -0
  36. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/MANIFEST.in +0 -0
  37. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/README.md +0 -0
  38. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/config/__init__.py +0 -0
  39. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/constants/rate_limit.py +0 -0
  40. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/constants/sse.py +0 -0
  41. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/core/__init__.py +0 -0
  42. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/data/function.json +0 -0
  43. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/py.typed +0 -0
  44. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery/types/__init__.py +0 -0
  45. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery_sdk.egg-info/dependency_links.txt +0 -0
  46. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery_sdk.egg-info/entry_points.txt +0 -0
  47. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery_sdk.egg-info/requires.txt +0 -0
  48. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/dataquery_sdk.egg-info/top_level.txt +0 -0
  49. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/pyproject.toml +0 -0
  50. {dataquery_sdk-1.2.2 → dataquery_sdk-1.2.3}/setup.cfg +0 -0
@@ -46,5 +46,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
46
46
  - Extraction is Zip Slip-guarded, skips current-day archives, and surfaces failures via `extraction_errors` (downgrading overall status to `partial`); date windows with no available files no longer mark a multi-window run as `partial`
47
47
  ## [1.2.2] - 2026-07-18
48
48
  - MCP: new `mcp-connect` CLI command
49
+ ## [1.2.3] - 2026-07-23
50
+ - Pagination: new client-driven `get_next_page_async(page)` helper — read `next_link` off any paged response and hand the page back to fetch the next one (manual counterpart to the SDK-driven `iter_pages`); next-page links are resolved against the surface the page came from and never sent off-host
51
+ - Pagination models: `items`, `page-size`, and `info` fields, plus the `next_link` property and `get_self_link()` accessor, moved onto the shared `Paginated` mixin; `FileList` is now paginated; list fields default to empty instead of being required so partial/empty envelopes parse cleanly
52
+ - New `APIResponseError` raised when a 2xx response carries an `errors`/`error` envelope (e.g. `498 Unrecognized Page Token`); an `info` `204` "no content" envelope now yields an empty page so pagination stops cleanly, while any other unrecognized body fails loudly
53
+ - Exports: `Paginated`, `Link`, `APIResponseError` are now part of the public API
49
54
 
50
55
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dataquery-sdk
3
- Version: 1.2.2
3
+ Version: 1.2.3
4
4
  Summary: Python SDK for DATAQUERY Data API - Query, download, and check availability of economic data files
5
5
  Author-email: DATAQUERY SDK Team <dataquery_support@jpmorgan.com>
6
6
  Project-URL: Homepage, https://github.com/jpmorganchase/dataquery-sdk
@@ -1,20 +1,6 @@
1
- """
2
- DATAQUERY SDK - Python SDK for DATAQUERY Data API
1
+ """DATAQUERY SDK - Python SDK for DATAQUERY Data API."""
3
2
 
4
- A high-performance Python SDK for the DATAQUERY Data API, providing seamless access
5
- to economic data files with advanced features like querying, downloading, availability
6
- checking, rate limiting, retry logic, connection pool monitoring, and comprehensive logging.
7
-
8
- Quick Start:
9
- >>> from dataquery import DataQuery
10
- >>> async with DataQuery() as dq:
11
- ... groups = await dq.list_groups_async()
12
- ... print(f"Found {len(groups)} groups")
13
-
14
- For more information, visit: https://github.com/jpmorganchase/dataquery-sdk
15
- """
16
-
17
- __version__ = "1.2.2"
3
+ __version__ = "1.2.3"
18
4
  __author__ = "DATAQUERY SDK Team"
19
5
  __email__ = "dataquery_support@jpmorgan.com"
20
6
  __license__ = "MIT"
@@ -34,6 +20,7 @@ from .function_registry import (
34
20
  from .sse.client import SSEClient, SSEEvent
35
21
  from .sse.subscriber import NotificationDownloadManager
36
22
  from .types.exceptions import (
23
+ APIResponseError,
37
24
  AuthenticationError,
38
25
  AvailabilityError,
39
26
  ConfigurationError,
@@ -45,6 +32,7 @@ from .types.exceptions import (
45
32
  GroupNotFoundError,
46
33
  NetworkError,
47
34
  NotFoundError,
35
+ PaginationError,
48
36
  RateLimitError,
49
37
  ValidationError,
50
38
  WorkflowError,
@@ -62,6 +50,8 @@ from .types.models import (
62
50
  FileList,
63
51
  Group,
64
52
  GroupList,
53
+ Link,
54
+ Paginated,
65
55
  )
66
56
  from .utils import download_zip_async
67
57
 
@@ -71,6 +61,8 @@ __all__ = [
71
61
  "ClientConfig",
72
62
  "Group",
73
63
  "GroupList",
64
+ "Paginated",
65
+ "Link",
74
66
  "FileInfo",
75
67
  "FileList",
76
68
  "AvailabilityInfo",
@@ -81,9 +73,11 @@ __all__ = [
81
73
  "DownloadProgress",
82
74
  "DateRange",
83
75
  "DataQueryError",
76
+ "APIResponseError",
84
77
  "AuthenticationError",
85
78
  "ValidationError",
86
79
  "NotFoundError",
80
+ "PaginationError",
87
81
  "RateLimitError",
88
82
  "NetworkError",
89
83
  "ConfigurationError",
@@ -11,8 +11,6 @@ from typing import Any, Dict, List, Optional
11
11
  from dataquery import DataQuery
12
12
  from dataquery.types.exceptions import DataQueryError
13
13
 
14
- # ── Output helpers (legacy-CLI "summary + --- JSON ---" format) ────────────
15
-
16
14
 
17
15
  def _to_dict(payload: Any) -> Dict[str, Any]:
18
16
  """Normalize a Pydantic model or dict into a plain dict for JSON dump."""
@@ -204,8 +202,6 @@ def create_parser() -> argparse.ArgumentParser:
204
202
  p_fn.add_argument("--list", action="store_true", help="List all available functions")
205
203
  p_fn.add_argument("--json", action="store_true", help="Output raw JSON")
206
204
 
207
- # ── DataQuery API v2 endpoints (skill-facing surface) ────────────────
208
-
209
205
  def _ts_args(p: argparse.ArgumentParser) -> None:
210
206
  p.add_argument("--data", choices=["REFERENCE_DATA", "NO_REFERENCE_DATA", "ALL"], default=None)
211
207
  p.add_argument("--start-date", help="YYYYMMDD or TODAY-Nx (x=D/W/M/Y). Default: TODAY-1D")
@@ -518,9 +514,6 @@ async def cmd_search(args: argparse.Namespace) -> int:
518
514
  return 0
519
515
 
520
516
 
521
- # ── DataQuery API v2 command handlers ────────────────────────────────────
522
-
523
-
524
517
  async def cmd_groups_search(args: argparse.Namespace) -> int:
525
518
  async with DataQuery(args.env_file) as dq:
526
519
  items = await dq.search_groups_async(args.keywords, page=args.page)
@@ -732,14 +725,7 @@ def cmd_function_help(args: argparse.Namespace) -> int:
732
725
 
733
726
 
734
727
  async def cmd_mcp_connect(args: argparse.Namespace) -> int:
735
- """Bridge a desktop MCP client (stdio) to a remote streamable-HTTP MCP server.
736
-
737
- Mints an OAuth client-credentials (AuthE) token with the SDK's own
738
- TokenManager from the ``DATAQUERY_*`` environment and attaches a fresh bearer
739
- token to every upstream request. Launched by an MCP client as its stdio
740
- ``command``; stdout carries only the MCP JSON-RPC stream, so all diagnostics
741
- go to stderr.
742
- """
728
+ """Bridge a desktop MCP client (stdio) to a remote streamable-HTTP MCP server."""
743
729
  try:
744
730
  import httpx
745
731
  from fastmcp import FastMCP
@@ -764,7 +750,7 @@ async def cmd_mcp_connect(args: argparse.Namespace) -> int:
764
750
  """Stamp a fresh AuthE bearer token (from the SDK TokenManager) per request."""
765
751
 
766
752
  async def async_auth_flow(self, request: httpx.Request) -> AsyncGenerator[httpx.Request, httpx.Response]:
767
- header = await token_manager.get_valid_token() # "Bearer <jwt>"
753
+ header = await token_manager.get_valid_token()
768
754
  if not header:
769
755
  raise DataQueryError(
770
756
  "Could not obtain an OAuth token \u2014 check DATAQUERY_CLIENT_ID, "
@@ -776,7 +762,6 @@ async def cmd_mcp_connect(args: argparse.Namespace) -> int:
776
762
 
777
763
  transport = StreamableHttpTransport(args.url, auth=_AutheAuth())
778
764
  proxy = FastMCP.as_proxy(transport, name=args.name)
779
- # stdout is reserved for the MCP JSON-RPC stream; keep the banner off it.
780
765
  await proxy.run_async(transport="stdio", show_banner=False)
781
766
  return 0
782
767
 
@@ -1,15 +1,4 @@
1
- """
2
- Environment-based configuration for the DATAQUERY SDK.
3
-
4
- Loads settings from environment variables (or a ``.env`` file) into a
5
- :class:`ClientConfig` Pydantic model. Field declarations live on
6
- ``ClientConfig`` — this module only handles the env-var → field projection,
7
- type coercion, and cross-field validation.
8
-
9
- Single source of truth: :data:`ClientConfig.model_fields`. The
10
- ``DEFAULTS`` table here is derived from the model at import time so the two
11
- cannot drift.
12
- """
1
+ """Environment-based configuration for the DATAQUERY SDK."""
13
2
 
14
3
  from __future__ import annotations
15
4
 
@@ -57,12 +46,7 @@ def _unwrap_optional(annotation: Any) -> Any:
57
46
 
58
47
 
59
48
  def _build_defaults() -> Dict[str, Optional[str]]:
60
- """Compute the ``EnvConfig.DEFAULTS`` table from the model.
61
-
62
- Booleans become ``"true"``/``"false"`` (lowercase, matching the
63
- tokens :meth:`EnvConfig.get_bool` accepts). Numeric / string defaults
64
- are stringified directly. ``None`` defaults stay ``None``.
65
- """
49
+ """Compute the ``EnvConfig.DEFAULTS`` table from the model."""
66
50
  defaults: Dict[str, Optional[str]] = {}
67
51
  for field_name, field in ClientConfig.model_fields.items():
68
52
  env_key = _env_name_for(field_name)
@@ -79,12 +63,7 @@ def _build_defaults() -> Dict[str, Optional[str]]:
79
63
 
80
64
 
81
65
  class EnvConfig:
82
- """Environment-based configuration loader for the DataQuery SDK.
83
-
84
- All methods are class methods — this class is a namespace, never
85
- instantiated. Field definitions, types, and defaults all originate from
86
- :class:`dataquery.types.models.ClientConfig`.
87
- """
66
+ """Environment-based configuration loader for the DataQuery SDK."""
88
67
 
89
68
  PREFIX = "DATAQUERY_"
90
69
  DEFAULTS: Dict[str, Optional[str]] = _build_defaults()
@@ -146,11 +125,7 @@ class EnvConfig:
146
125
  config_data: Optional[Dict[str, Any]] = None,
147
126
  env_file: Optional[Path] = None,
148
127
  ) -> ClientConfig:
149
- """Build a :class:`ClientConfig` from env vars or an explicit dict.
150
-
151
- When ``config_data`` is provided the env-var path is skipped entirely
152
- — the dict is passed straight to :class:`ClientConfig`.
153
- """
128
+ """Build a :class:`ClientConfig` from env vars or an explicit dict."""
154
129
  if config_data is not None:
155
130
  return ClientConfig(**config_data)
156
131
 
@@ -275,11 +250,7 @@ class EnvConfig:
275
250
 
276
251
  @classmethod
277
252
  def create_env_template(cls, output_path: Optional[Path] = None) -> Path:
278
- """Write a ``.env`` template listing every supported variable.
279
-
280
- Lines are auto-generated from :data:`ClientConfig.model_fields` so a
281
- new field shows up here automatically (no separate maintenance).
282
- """
253
+ """Write a ``.env`` template listing every supported variable."""
283
254
  if output_path is None:
284
255
  output_path = Path(".env.template")
285
256
  if not isinstance(output_path, Path):
@@ -1,9 +1,4 @@
1
- """
2
- Enhanced logging configuration for the DATAQUERY SDK.
3
-
4
- Provides structured logging, performance metrics, request/response logging,
5
- and configurable log levels and formats.
6
- """
1
+ """Enhanced logging configuration for the DATAQUERY SDK."""
7
2
 
8
3
  import logging
9
4
  from dataclasses import dataclass, field
@@ -353,21 +348,7 @@ def create_logging_config(
353
348
  enable_request_logging: bool = False,
354
349
  enable_performance_logging: bool = True,
355
350
  ) -> LoggingConfig:
356
- """
357
- Create logging configuration.
358
-
359
- Args:
360
- level: Log level
361
- format: Log format
362
- enable_console: Whether to enable console logging
363
- enable_file: Whether to enable file logging
364
- log_file: Path to log file
365
- enable_request_logging: Whether to log HTTP requests/responses
366
- enable_performance_logging: Whether to log performance metrics
367
-
368
- Returns:
369
- Logging configuration
370
- """
351
+ """Create logging configuration."""
371
352
  return LoggingConfig(
372
353
  level=level,
373
354
  format=format,
@@ -380,13 +361,5 @@ def create_logging_config(
380
361
 
381
362
 
382
363
  def create_logging_manager(config: LoggingConfig) -> LoggingManager:
383
- """
384
- Create a logging manager with the specified configuration.
385
-
386
- Args:
387
- config: Logging configuration
388
-
389
- Returns:
390
- Configured logging manager
391
- """
364
+ """Create a logging manager with the specified configuration."""
392
365
  return LoggingManager(config)
@@ -1,16 +1,4 @@
1
- """Internal tunable constants used across the SDK.
2
-
3
- Centralizing these values here avoids drift between code paths that must agree
4
- (e.g. the single-stream and parallel downloaders share thresholds) and gives a
5
- single place to adjust tunables. Pydantic model field defaults and validation
6
- bounds are intentionally NOT hoisted here — those are part of the public
7
- schema.
8
-
9
- Constants are organized into topical submodules; this ``__init__`` re-exports
10
- them so existing call sites using ``from . import constants as C`` keep
11
- working without change. New code may also import from a specific submodule,
12
- e.g. ``from dataquery.constants.api import API_GROUPS``.
13
- """
1
+ """Internal tunable constants used across the SDK."""
14
2
 
15
3
  from __future__ import annotations
16
4
 
@@ -1,8 +1,4 @@
1
- """API endpoint paths.
2
-
3
- Paths are relative to the main API base URL (``_build_api_url``) or the files
4
- API base URL (``_build_files_api_url``) unless otherwise noted.
5
- """
1
+ """API endpoint paths."""
6
2
 
7
3
  from __future__ import annotations
8
4
 
@@ -26,15 +26,6 @@ TEMP_SUFFIX = ".part"
26
26
  MBPS_TO_BYTES_PER_SECOND = 125_000
27
27
 
28
28
 
29
- # Default size, in days, of each chunk when splitting a date range so that
30
- # the available-files endpoint is queried over a smaller window per call.
31
- # Used by ``split_date_range_into_chunks``. The endpoint caps each call at
32
- # one calendar month; smaller windows just keep responses lighter. (The old
33
- # group-level limits, e.g. 14 days for RESEARCH_EQUITY_ALL, were lifted
34
- # server-side.)
35
29
  DEFAULT_WRITTEN_RESEARCH_CHUNK_DAYS: int = 7
36
30
 
37
- # Error string set on the OperationReport when the available-files endpoint
38
- # returns nothing for a date window. Chunked workflows match on it to tell
39
- # a quiet window apart from a real failure.
40
31
  NO_FILES_FOUND_ERROR: str = "No available files found for date range"