API reference

This page documents the public surface exposed at the top of the restgdf namespace: the FeatureLayer and Directory entry points, the ArcGISTokenSession wrapper, and the migration helpers in restgdf.compat.

The pydantic response models (LayerMetadata, FeaturesResponse, CrawlReport and friends) live on Pydantic models. Internal utility modules are on Utilities.

The base pip install restgdf surface covers metadata/query helpers, raw-row iteration, crawl/auth utilities, and all pydantic models. Install restgdf[geo] for GeoDataFrame and pandas-backed APIs such as FeatureLayer.get_gdf(), sample_gdf(), head_gdf(), fieldtypes, get_value_counts(), and get_nested_count().

FeatureLayer

class restgdf.FeatureLayer(url, session, where='1=1', token=None, **kwargs)[source]

Bases: object

A class for interacting with an ArcGIS REST FeatureLayer.

Variables:
  • metadata (restgdf.LayerMetadata) – Pydantic-validated layer metadata (name, fields, max record count, advanced query capabilities, …). Replaces the pre-2.0 raw dict. Extra keys sent by the server are preserved via extra="allow" and reachable through metadata.model_extra.

  • name (str) – Convenience alias for metadata.name.

  • fields (tuple[str, ]) – Field names consumed by restgdf.

  • object_id_field (str) – Resolved object-id field name ("OBJECTID" when the server omits it).

  • count (int) – Feature count, validated via CountResponse at prep time.

async prep()[source]

Fetch and validate layer metadata from the server.

Populates metadata, name, fields, object_id_field, and count. Must be called before accessing any metadata attributes unless the instance was created via from_url() (which calls this method automatically).

Raises:

ValueError – If the URL does not point to a Feature Layer (e.g. a Table or Map Service root).

See also

from_url

Recommended constructor that calls prep automatically.

property fieldtypes: pandas.DataFrame

Return field metadata as a DataFrame when pandas is available.

async classmethod from_url(url, **kwargs)[source]

Create a prepared FeatureLayer from a URL.

This is the recommended constructor. It instantiates the class and calls prep() so all metadata attributes are immediately available on the returned instance.

Parameters:
  • url (str) – ArcGIS REST FeatureLayer endpoint URL (must end with a numeric layer ID).

  • **kwargs – Forwarded to __init__() — accepts session, where, token, and any extra HTTP request arguments.

Returns:

A fully prepared instance with metadata loaded.

Return type:

FeatureLayer

Raises:

ValueError – If url does not end with a number, conflicting tokens are provided, or the endpoint is not a Feature Layer.

See also

__init__

Low-level constructor (requires a manual prep call).

async classmethod from_config(url, config, **kwargs)[source]

Opt-in constructor that applies an explicit Config’s auth token.

This is the FeatureLayer-side consume seam for the config hybrid (CONFIG-02 / W5-14). It is opt-in and explicit: the caller passes a config object; from_config never reads the process-global get_config() on its own, and neither this method nor __init__() implicitly sources configuration at construction time. Pass config=get_config() if the process config is wanted.

The one setting consumed is config.auth.token – forwarded as the layer token (the only AuthConfig field FeatureLayer natively accepts) – then delegated to from_url(). Every other request/transport setting still resolves through the process-global config at request time (the designated W2-10 seam); a directly built Config is not otherwise threaded into the request path (CONFIG-03, see restgdf.Config).

Transport note: the forwarded token rides the standard FeatureLayer(token=...) body/query path (AUTH-01 forces POST so it is never serialized into the URL). For header-transport secured services, prefer building an ArcGISTokenSession.from_config(...) and passing it as session= instead.

Parameters:
  • url (str) – ArcGIS REST FeatureLayer endpoint URL (must end with a numeric id).

  • config (restgdf.Config) – An explicit configuration instance (required – no implicit process-global default).

  • **kwargs – Forwarded to from_url() (session, where, extra HTTP request args). Do not also pass token= – it is sourced from config.auth.token.

Returns:

A fully prepared instance.

Return type:

FeatureLayer

See also

from_url

The underlying constructor this delegates to.

async get_oids()[source]

Return all object IDs matching the current WHERE filter.

Delegates to get_unique_values() using the resolved object_id_field.

Returns:

Sorted list of object ID values for the filtered feature set.

Return type:

list[int]

async sample_gdf(n=10)[source]

Get n random features as a GeoDataFrame.

async head_gdf(n=10)[source]

Get the n first features as a GeoDataFrame.

async get_gdf()[source]

Get a GeoDataFrame from an ArcGIS FeatureLayer.

The returned GeoDataFrame carries gdf.attrs["spatial_reference"] populated from the layer’s metadata envelope (R-65) when the layer advertises a spatial reference via extent.spatialReference or top-level spatialReference.

Each call returns an independent copy of the cached frame (W5-1) — mutating the returned GeoDataFrame in place does not affect the cache or any other call’s result. This copy-on-return only protects the read side: the cache population (if self.gdf is None: ...) is not itself concurrency-safe — concurrent awaiters via asyncio.gather can still both observe a cache miss and double-fetch. Do not rely on this fix for task-safety.

async iter_pages(*, order='request', max_concurrent_pages=None, on_truncation='raise', **kwargs)[source]

Yield raw ArcGIS query-page envelopes from this FeatureLayer.

Parameters:
  • order"request" (default) yields pages in submit order. "completion" yields pages as the underlying fetches complete (may reorder relative to the pagination plan).

  • max_concurrent_pages – Upper bound on concurrent in-flight page fetches. Pass a positive int to bound both concurrency and peak memory. None (the default) does not bound them: every page in the pagination plan is scheduled up front and each fetched page is buffered, so both grow with the page count. order alone does not bound anything — only max_concurrent_pages does. Note that on_truncation="split" issues additional uncounted sub-fetches per truncated page, so even with a bound of K the worst-case in-flight count is roughly K + 1.

  • on_truncation – Behavior when a page reports exceededTransferLimit=true:

    • "raise" (default) — raise restgdf.errors.RestgdfResponseError with context='exceededTransferLimit'.

    • "ignore" — log a restgdf.pagination warning and yield the truncated page anyway.

    • "split" — bisect the predicate’s OID list and recurse (max depth 32; irreducible partitions raise).

Yields:

dict – The full raw response envelope for each page (features, objectIdFieldName, exceededTransferLimit, etc.).

Notes

When telemetry is enabled, emits exactly ONE INTERNAL parent span named feature_layer.stream wrapping the per-page loop (R-61). No per-page restgdf child spans are emitted.

async iter_features(*, order='request', max_concurrent_pages=None, on_truncation='raise', **kwargs)[source]

Yield one raw ArcGIS feature dict at a time.

Thin wrapper over iter_pages() that flattens each page’s features list. See iter_pages() for parameter semantics.

async stream_features(*, order='request', max_concurrent_pages=None, on_truncation='raise', **kwargs)

Yield one raw ArcGIS feature dict at a time.

Thin wrapper over iter_pages() that flattens each page’s features list. See iter_pages() for parameter semantics.

async stream_feature_batches(*, order='request', max_concurrent_pages=None, on_truncation='raise', **kwargs)[source]

Yield one list of raw feature dicts per page.

See iter_pages() for parameter semantics.

async stream_rows(*, order='request', max_concurrent_pages=None, on_truncation='raise', **kwargs)[source]

Yield row-shaped dicts (attributes plus raw geometry).

Each row is the layer feature’s attributes merged with a geometry key holding the ArcGIS geometry dict verbatim. See iter_pages() for parameter semantics.

async stream_gdf_chunks(**kwargs)[source]

Yield GeoDataFrame chunks; each chunk’s attrs carries spatial_reference (R-65).

Requires the optional geo stack (geopandas / pyogrio).

async get_df(resolve_domains=False)[source]

Get a pandas DataFrame from an ArcGIS FeatureLayer.

Tabular row view: attributes plus any raw geometry dict returned by the server, with no geopandas/pyogrio dependency. Raises restgdf.errors.OptionalDependencyError when pandas is not installed.

This is the pandas-only counterpart to get_gdf() — prefer it when callers only need tabular access and want to avoid the full geo dependency stack.

Parameters:

resolve_domains – When True, coded-value domain fields are post-processed so the DataFrame contains the human-readable name rather than the raw code. Codes absent from the domain’s codedValues table pass through unchanged. Range domains are not validated or coerced. Defaults to False — the historical behavior where the DataFrame faithfully mirrors the server payload. No additional HTTP traffic is issued; resolution uses the already-loaded FeatureLayer.metadata fetched during prep().

Examples

>>> df = await layer.get_df(resolve_domains=True)
>>> df["STATUS"].head().tolist()
['Active', 'Inactive', 'Active', ...]
async row_dict_generator(**kwargs)[source]

Asynchronously yield rows from a GeoDataFrame as dictionaries.

Deprecated since version 2.0: Use stream_rows() instead. This method emits a DeprecationWarning and continues to delegate to the module-level row_dict_generator helper for backwards compatibility with existing unittest.mock.patch targets. Scheduled for removal in a future release.

async get_unique_values(fields, sortby=None)[source]

Get unique values for one or more fields.

Results are cached per (fields, sortby) key for the lifetime of this instance.

Parameters:
  • fields (str or tuple of str) – A single field name (returns a list) or a tuple of field names (returns a DataFrame; requires the geo extra).

  • sortby (str or None, optional) – Field name to sort results by. When None, the server’s default ordering is used.

Returns:

A plain list when fields is a single string, or a DataFrame when fields is a tuple. Each call returns an independent copy (W5-1) — mutating the returned value does not affect the cache or any other call’s result.

Return type:

list or DataFrame

Raises:

FieldDoesNotExistError – If any requested field is not present in the layer schema.

async get_value_counts(field)[source]

Get value counts for a single field.

Results are cached per field for the lifetime of this instance.

Parameters:

field (str) – The field name to compute value counts for.

Returns:

A pandas DataFrame with one row per distinct value and an associated count column. Each call returns an independent copy (W5-1) — mutating the returned frame does not affect the cache or any other call’s result.

Return type:

DataFrame

Raises:

FieldDoesNotExistError – If field is not present in the layer schema.

async get_nested_count(fields)[source]

Get nested (cross-tabulated) value counts for multiple fields.

Results are cached per fields tuple for the lifetime of this instance.

Parameters:

fields (tuple of str) – Exactly two field names to cross-tabulate.

Returns:

A pandas DataFrame with one row per unique combination of values across the requested fields and an associated count. Each call returns an independent copy (W5-1) — mutating the returned frame does not affect the cache or any other call’s result.

Return type:

DataFrame

Raises:
  • ValueError – If fields does not contain exactly two field names (W5-3).

  • FieldDoesNotExistError – If any field in fields is not present in the layer schema.

async getoids()[source]

Deprecated alias for get_oids().

async samplegdf(n=10)[source]

Deprecated alias for sample_gdf().

async headgdf(n=10)[source]

Deprecated alias for head_gdf().

async getgdf()[source]

Deprecated alias for get_gdf().

async getuniquevalues(fields, sortby=None)[source]

Deprecated alias for get_unique_values().

async getvaluecounts(field)[source]

Deprecated alias for get_value_counts().

async getnestedcount(fields)[source]

Deprecated alias for get_nested_count().

async where(wherestr)[source]

Create a refined FeatureLayer bound to wherestr.

BL-46: when the current instance has already resolved its schema via prep(), the refined child reuses the parent’s cached metadata / name / fields / object_id_field so the expensive metadata GET (?f=json) is not re-issued. The feature-count POST is still issued, but scoped to the refined where_clause so refined.count is correct for the refined filter.

Directory

class restgdf.Directory(url, session, token=None)[source]

Bases: object

A class for interacting with ArcGIS Server directories.

Variables:
  • metadata (Optional[restgdf.LayerMetadata]) – Pydantic-validated root metadata populated by prep(). None until prep (or from_url()) has run.

  • services (Optional[list[restgdf.CrawlServiceEntry]]) – Services discovered by the most recent crawl() call. Each entry carries name, url, type, and a parsed metadata (LayerMetadata or None if that service’s metadata call failed).

  • services_with_feature_count (Optional[list[restgdf.CrawlServiceEntry]]) – Same as services but populated with feature counts when crawl() was invoked with return_feature_count=True.

  • report (Optional[restgdf.CrawlReport]) – The full crawl report (services + per-stage errors + root metadata) from the most recent crawl() call. Use this when you need to inspect failures that were silently captured instead of raised.

async prep()[source]

Fetch and validate directory metadata from the server.

Populates metadata with a LayerMetadata instance. Must be called before accessing metadata unless the instance was created via from_url().

async classmethod from_url(url, **kwargs)[source]

Create a prepared Directory from a URL.

This is the recommended constructor. It instantiates the class and calls prep() so metadata is immediately available.

Parameters:
  • url (str) – ArcGIS Server directory endpoint URL.

  • **kwargs – Forwarded to __init__() — accepts session and token.

Returns:

A fully prepared instance with metadata loaded.

Return type:

Directory

async classmethod from_config(url, config, **kwargs)[source]

Opt-in constructor applying an explicit Config’s auth token.

Mirrors restgdf.FeatureLayer.from_config() (CONFIG-02 / W5-14): opt-in and explicit – the caller passes config; nothing here or in __init__() implicitly reads the process-global get_config() at construction. config.auth.token is unwrapped and forwarded as the directory token, then delegated to from_url(). Other transport settings still resolve through the process-global config at request time; a directly built Config is not otherwise threaded into the request path (CONFIG-03).

Parameters:
  • url (str) – ArcGIS Server directory endpoint URL.

  • config (restgdf.Config) – An explicit configuration instance (required).

  • **kwargs – Forwarded to from_url() (session). Do not also pass token= – it is sourced from config.auth.token.

async crawl(return_feature_count=False)[source]

Discover all services under this directory recursively.

Results are cached — subsequent calls with the same return_feature_count value return the cached list without additional HTTP traffic.

Parameters:

return_feature_count (bool, default False) – When True, also fetches the feature count for each discovered layer (requires extra HTTP requests per layer).

Returns:

List of discovered service entries, each carrying name, url, type, and parsed metadata.

Return type:

list[CrawlServiceEntry]

Notes

After a successful call, the following instance attributes are populated:

  • services — the returned list.

  • report — the full CrawlReport.

  • services_with_feature_count — same as services when return_feature_count was True.

filter_directory_layers(layer_type)[source]

Filter discovered layers by type.

Parameters:

layer_type (str) – The layer type string to match (e.g. "Feature Layer", "Raster Layer").

Returns:

Metadata entries for layers whose type matches layer_type.

Return type:

list[LayerMetadata]

Raises:

ValueError – If crawl() has not been called yet.

See also

feature_layers

Shortcut for filter_directory_layers("Feature Layer").

rasters

Shortcut for filter_directory_layers("Raster Layer").

feature_layers()[source]

Return all Feature Layer metadata from the crawl.

Convenience wrapper around filter_directory_layers("Feature Layer").

Returns:

Metadata for every discovered Feature Layer.

Return type:

list[LayerMetadata]

rasters()[source]

Return all Raster Layer metadata from the crawl.

Convenience wrapper around filter_directory_layers("Raster Layer").

Returns:

Metadata for every discovered Raster Layer.

Return type:

list[LayerMetadata]

Token session

class restgdf.ArcGISTokenSession(session, credentials=None, token_url='https://www.arcgis.com/sharing/rest/generateToken', token_refresh_threshold=60, token=None, expires=None, verify_ssl=True, config=None)[source]

Bases: object

Wrap an aiohttp session with ArcGIS token refresh behavior.

Construction knobs (token_url, token_refresh_threshold, credentials) are validated via TokenSessionConfig in __post_init__() so a bogus scheme or zero-length username fails fast with RestgdfResponseError rather than surfacing as a 401 or an aiohttp error deep in the request path.

__post_init__ never reads the process-global get_config: a plain ArcGISTokenSession(session, credentials) keeps its dataclass defaults (e.g. token_refresh_threshold=60). The AuthConfig refresh knobs are config holders, not auto-applied; opt in explicitly with from_config() when you want the RESTGDF_AUTH_* namespace to drive a session’s refresh timing / transport.

session: ClientSession
credentials: AGOLUserPass | None = None
token_url: str = 'https://www.arcgis.com/sharing/rest/generateToken'
token_refresh_threshold: int = 60
token: str | None = None
expires: int | float | None = None
verify_ssl: bool = True
config: TokenSessionConfig | None = None
classmethod from_config(session, credentials, *, config=None)[source]

Build a token session from an AuthConfig (opt-in).

W3-3 (CONFIG-02, hybrid decision): this classmethod is the ONLY sanctioned path by which an AuthConfig namespace flows into a session. It is strictly opt-in — nothing constructs a session this way implicitly, and __post_init__ never reaches into the process-global config.

When config is None the process-global get_config().auth is read, but only here at call time, never at import time. The AuthConfig refresh knobs are projected onto a validated TokenSessionConfig via from_auth_config(), so the effective refresh window is refresh_leeway_s + clock_skew_s (default 150), not the bare dataclass default of 60.

property token_request_payload: dict

Return the payload for the token request.

property expires_at: datetime | None

Return the token expiry as a tz-aware UTC datetime.

ArcGIS returns expires in either seconds or milliseconds since the Unix epoch — values above 1e11 are treated as milliseconds and divided by 1000. Returns None when no expiry is set.

property auth_headers: dict[str, str]

Return authentication headers with the token if available.

For a referer-bound session (AGOLUserPass(referer=...) / TokenSessionConfig.referer) a matching Referer header is attached so the client="referer" token is honoured on data requests, not only at mint time (#175 NOTE-1). No Referer is attached for a client="requestip" token (no referer leak).

update_headers(headers=None)[source]

Return headers merged with the active token.

update_dict(input_dict=None)[source]

Return a request payload/query dict merged with the active token.

async update_token()[source]

Update the token by making a request to the token URL.

The /generateToken payload is validated against TokenResponse (strict tier) so malformed/error envelopes raise RestgdfResponseError instead of KeyError deep in caller code paths.

Retries up to _MAX_TOKEN_RETRIES times with exponential backoff (base _BASE_BACKOFF_S) on transient network errors. Deterministic errors (bad credentials, content-type mismatches, validation failures) are re-raised immediately. After exhausting retries, raises TokenRefreshFailedError.

Emits structured log events: * auth.refresh.start — before the POST * auth.refresh.success — after successful token update * auth.refresh.failure — on any exception

token_needs_update()[source]

Check if the token needs to be updated.

async update_token_if_needed()[source]

Ensure the token is valid and refresh if necessary.

BL-03: concurrent callers racing on an expired token collapse onto a single /generateToken POST via a lazily-initialized per-instance asyncio.Lock with a double-checked token_needs_update() inside the lock (plan.md §3c R-18, kickoff phase-1a §10.4). The lock is created here — not in __post_init__ — so instances constructed outside a running event loop (e.g. at import time or inside a sync test) never trigger DeprecationWarning: There is no current event loop.

async get(url, params=None, headers=None, **kwargs)[source]

Make a GET request to the specified URL with the token.

async post(url, data=None, headers=None, **kwargs)[source]

Make a POST request to the specified URL with the token.

property closed: bool

Return True when the underlying aiohttp.ClientSession is closed.

Delegating lets ArcGISTokenSession satisfy the internal AsyncHTTPSession transport Protocol uniformly with aiohttp.ClientSession (R-71).

async close()[source]

Close the underlying aiohttp.ClientSession.

Mirrors aiohttp.ClientSession.close() so token sessions and raw aiohttp sessions are interchangeable through the internal AsyncHTTPSession transport Protocol. Idempotent: closing an already-closed session is a no-op.

Errors

exception restgdf.RestgdfResponseError(message, *, model_name='', context='', raw=None, url=None, status_code=None, request_id=None)[source]

Bases: RestgdfError, ValueError

Raised when validated ArcGIS response handling must fail fast.

Variables:
  • model_name – The pydantic model class name associated with the failed parse (for example "CountResponse" or "LayerMetadata").

  • context – A short identifier describing where the response came from (for example the request URL or a helper name). Used by operators triaging ArcGIS vendor variance.

  • raw – The raw JSON-decoded payload that failed validation. Kept on the exception so callers can log or re-raise without re-reading the response body.

Canonical exception taxonomy for restgdf 3.0.

This module defines the public exception hierarchy used by every public entry point in restgdf. All domain-specific exceptions derive from RestgdfError so callers can catch “any restgdf failure” with a single except RestgdfError block while still being able to discriminate between transport, schema, auth, pagination, configuration, and conversion failures via the more specific subclasses.

Several classes multi-inherit from a builtin exception in addition to their restgdf-specific parent. This is deliberate and preserves backward-compat:

  • ConfigurationError(RestgdfError, ValueError) keeps except ValueError callers working while restgdf 3.x stabilizes (R-09).

  • OptionalDependencyError(ConfigurationError, ModuleNotFoundError) keeps except ImportError / except ModuleNotFoundError working when optional pandas/geopandas/pyogrio dependencies are missing.

  • RestgdfResponseError(RestgdfError, ValueError) keeps the 2.x except ValueError contract around typed response validation.

  • PaginationError(ArcGISServiceError, IndexError) preserves the 2.x “looks like an IndexError” contract around cursor exhaustion.

  • AuthenticationError(RestgdfResponseError, PermissionError) lets callers treat auth failures as PermissionError when appropriate.

  • RestgdfTimeoutError(TransportError, TimeoutError) keeps except TimeoutError callers working on request timeouts without shadowing the builtin TimeoutError symbol.

Hierarchy:

RestgdfError(Exception)
+-- ConfigurationError(RestgdfError, ValueError)
|   +-- OptionalDependencyError(ConfigurationError, ModuleNotFoundError)
+-- RestgdfResponseError(RestgdfError, ValueError)
|   +-- SchemaValidationError(RestgdfResponseError)
|   |   +-- FieldDoesNotExistError(SchemaValidationError)
|   +-- ArcGISServiceError(RestgdfResponseError)
|   |   +-- PaginationError(ArcGISServiceError, IndexError)
|   +-- AuthenticationError(RestgdfResponseError, PermissionError)
|       +-- InvalidCredentialsError(AuthenticationError)
|       +-- TokenExpiredError(AuthenticationError)
|       +-- TokenRequiredError(AuthenticationError)
|       +-- TokenRefreshFailedError(AuthenticationError)
|       +-- AuthNotAttachedError(AuthenticationError)
+-- TransportError(RestgdfError)
|   +-- RestgdfTimeoutError(TransportError, TimeoutError)
|   +-- RateLimitError(TransportError)
+-- OutputConversionError(RestgdfError)
exception restgdf.errors.ArcGISServiceError(message, *, model_name='', context='', raw=None, url=None, status_code=None, request_id=None)[source]

Bases: RestgdfResponseError

Raised when the ArcGIS service returns an explicit {"error": ...} envelope.

exception restgdf.errors.AuthNotAttachedError(message, *, context=None, attempt=None, cause=None, model_name='AuthenticationError', raw=None)[source]

Bases: _AuthSubtypeBase

Raised when a 499 is observed — the library did not attach auth to the request.

Per R-14 no retry is attempted; the error propagates immediately to the caller. This is semantically distinct from TokenExpiredError (498) which does trigger a single-flight refresh.

exception restgdf.errors.AuthenticationError(message, *, model_name='', context='', raw=None, url=None, status_code=None, request_id=None)[source]

Bases: RestgdfResponseError, PermissionError

Raised when ArcGIS auth (token, creds, scope) is invalid or expired.

Multi-inherits PermissionError so except PermissionError in application code can treat restgdf auth failures uniformly with local-filesystem permission failures.

exception restgdf.errors.ConfigurationError[source]

Bases: RestgdfError, ValueError

Raised when restgdf configuration (env vars, Settings, kwargs) is invalid.

Multi-inherits ValueError through 3.x so existing except ValueError callers continue to catch misconfiguration. The ValueError base is a back-compat shim and will only be removed in a future major release (>=4.0), preceded by a DeprecationWarning.

exception restgdf.errors.FieldDoesNotExistError(field_name=None, *, context=None, message=None)[source]

Bases: SchemaValidationError

A referenced field is absent from the ArcGIS layer metadata (BL-09).

Replaces the 2.x FIELDDOESNOTEXIST IndexError singleton per plan.md §3c R-02. Callers previously catching IndexError must migrate to except FieldDoesNotExistError (or the parent SchemaValidationError / RestgdfResponseError). No compat shim.

Variables:
  • field_name (:py:class:`str | tuple[str`, :py:class:`...] | None`) – The field(s) that failed resolution.

  • context (str) – Call-site identifier (e.g. "FeatureLayer.get_unique_values"); defaults to "FieldDoesNotExistError" when the caller passes None, matching the parent RestgdfResponseError contract where context is always a non-None string.

exception restgdf.errors.InvalidCredentialsError(message, *, context=None, attempt=None, cause=None, model_name='AuthenticationError', raw=None)[source]

Bases: _AuthSubtypeBase

Raised on a 4xx credential rejection from /generateToken.

A true-HTTP 400/401/403 from the token endpoint is mapped here (W2-2) instead of escaping as a raw aiohttp.ClientResponseError; the originating aiohttp error is chained as __cause__. Other non-2xx token responses surface as RestgdfResponseError. The HTTP-200 {"error": {...}} bad-credentials envelope is handled separately by the strict TokenResponse tier (also a RestgdfResponseError).

Inherits AuthenticationErrorPermissionError.

exception restgdf.errors.OptionalDependencyError[source]

Bases: ConfigurationError, ModuleNotFoundError

Raised when an optional dependency (pandas/geopandas/pyogrio) is absent.

Multi-inherits ModuleNotFoundError so existing except ImportError / except ModuleNotFoundError call sites keep working when restgdf[geo] is not installed.

exception restgdf.errors.OutputConversionError[source]

Bases: RestgdfError

Raised when converting validated ArcGIS data to a GeoDataFrame / DataFrame fails.

exception restgdf.errors.PaginationInconsistencyWarning[source]

Bases: UserWarning

Emitted when an ArcGIS batch response is self-inconsistent (R-73).

Specifically: a page that returns zero features and sets exceededTransferLimit=true is an ArcGIS-side pagination bug — the cursor cannot advance (offset-based pagination needs at least one row per page to progress) but the service simultaneously insists more rows exist. Left un-flagged this produces a silently-truncated result set.

Multi-inherits UserWarning so callers can silence or escalate it via warnings (e.g. warnings.filterwarnings("error", category=PaginationInconsistencyWarning)).

exception restgdf.errors.PaginationError(*args, batch_index=None, page_size=None)[source]

Bases: ArcGISServiceError, IndexError

Raised when cursor-based pagination cannot advance or exceeds limits.

Multi-inherits IndexError so legacy call sites that used except IndexError around pagination exhaustion keep working.

Variables:
  • batch_index – The zero-based batch index at which pagination failed, if known.

  • page_size – The page size in effect when pagination failed, if known.

exception restgdf.errors.RateLimitError(*args, retry_after=None, url=None, status_code=None)[source]

Bases: TransportError

Raised when the ArcGIS service signals a rate limit / throttle.

Variables:

retry_after – Optional seconds to wait before retrying, parsed from a Retry-After header or service envelope. None when the service did not supply a hint.

exception restgdf.errors.RestgdfError[source]

Bases: Exception

Base class for every exception raised by restgdf.

exception restgdf.errors.RestgdfResponseError(message, *, model_name='', context='', raw=None, url=None, status_code=None, request_id=None)[source]

Bases: RestgdfError, ValueError

Raised when validated ArcGIS response handling must fail fast.

Variables:
  • model_name – The pydantic model class name associated with the failed parse (for example "CountResponse" or "LayerMetadata").

  • context – A short identifier describing where the response came from (for example the request URL or a helper name). Used by operators triaging ArcGIS vendor variance.

  • raw – The raw JSON-decoded payload that failed validation. Kept on the exception so callers can log or re-raise without re-reading the response body.

exception restgdf.errors.RestgdfTimeoutError(*args, url=None, status_code=None, timeout_kind=None)[source]

Bases: TransportError, TimeoutError

Raised when a request times out.

Named RestgdfTimeoutError (not TimeoutError) to avoid shadowing the builtin. Multi-inherits TimeoutError so except TimeoutError callers continue to match.

Variables:

timeout_kind – One of "total", "connect", or "read", indicating which timeout budget was exceeded. None when unknown.

exception restgdf.errors.SchemaValidationError(message, *, model_name='', context='', raw=None, url=None, status_code=None, request_id=None)[source]

Bases: RestgdfResponseError

Raised when an ArcGIS response envelope fails schema validation.

Does not multi-inherit IndexError: R-02 explicitly forbids the transitional SchemaValidationError(IndexError, ...) shim that earlier drafts of the plan proposed.

exception restgdf.errors.TokenExpiredError(message='Token expired (Esri 498)', *, code=498, context=None, attempt=None, cause=None)[source]

Bases: _AuthSubtypeBase

Raised when ArcGIS returns error code 498 (Invalid Token).

Variables:

code (int) – Always 498.

exception restgdf.errors.TokenRefreshFailedError(message, *, context=None, attempt=None, cause=None, model_name='AuthenticationError', raw=None)[source]

Bases: _AuthSubtypeBase

Raised after the bounded-retry ladder for /generateToken is exhausted.

Variables:

attempt (int | None) – The final attempt number at which the refresh was abandoned.

exception restgdf.errors.TokenRequiredError(message, *, context=None, attempt=None, cause=None, model_name='AuthenticationError', raw=None)[source]

Bases: _AuthSubtypeBase

Reserved auth subtype — not currently raised (W2-2 demotion).

Semantically this names the “service demands a token but the request carried none (or the wrong transport)” condition, i.e. Esri 499. In practice a 499 surfaces as AuthNotAttachedError — that is the single live 499 raise site (see ArcGISTokenSession._call_with_auth_retry) and is semantically precise. This class is retained as a back-compat / placeholder type in the taxonomy; there is deliberately no second 499 raise site. Catch AuthNotAttachedError (or the parent AuthenticationError) for the 499 condition.

exception restgdf.errors.TransportError(*args, url=None, status_code=None)[source]

Bases: RestgdfError

Raised for network/HTTP transport-layer failures (connection, DNS, …).

Variables:
  • url – The URL that was being requested when the failure occurred.

  • status_code – The HTTP status code, if one was received before the transport failure. None for pre-connect failures (DNS, refused, …).

Runtime settings

See Pydantic models for the Settings model; the helpers below read it from the environment.

restgdf.get_settings()[source]

Return the process-wide cached Settings instance.

Deprecated since version phase-2a: Use restgdf.get_config() and restgdf.Config. This shim constructs a Settings from the cached Config and emits a single DeprecationWarning per process.

Migration helpers

Compatibility helpers for the 1.x → 2.x migration.

Downstream code that indexed the legacy dict-based public surface (for example metadata["name"], crawl_result["services"]) needs a short-term way to keep working during its own upgrade window. These helpers convert the 2.x pydantic models back into plain Python structures on demand.

They are migration aids, not the primary API — prefer direct attribute access (metadata.name) and model_dump() in new code. These helpers will stay available for the 2.x series but may be removed in 3.x.

restgdf.compat.as_dict(obj)[source]

Return a plain Python dict view of a restgdf pydantic model.

If obj is a pydantic.BaseModel instance, returns obj.model_dump(mode="python", by_alias=False) — a dict keyed by the model’s Python (snake_case) field names, with nested models also recursively dumped.

If obj is anything else (already-a-dict, None, primitive, list), it is returned unchanged. This lets migration code wrap heterogeneous values uniformly:

for entry in report.services:
    row = as_dict(entry)   # dict whether entry is model or already dict
    save(row["name"], row.get("url"))

This helper is intentionally conservative: it does not recurse into containers of models and does not coerce by_alias=True. Callers that need the ArcGIS-native camelCase round-trip should call model_dump() explicitly.

restgdf.compat.as_json_dict(obj)[source]

Return a JSON-safe dict view of a restgdf pydantic model.

Like as_dict(), but uses model_dump(mode="json") so every nested value is a JSON-serializable primitive (SecretStr"**********" placeholder, datetime → ISO string, etc.). Handy for structured logging of a model without carrying unserializable objects into the log record.

Non-model values are returned unchanged.