0
# Core Framework
1
2
Foundation classes for authorization, HTTP clients, response handling, scope management, and error handling that provide consistent patterns across all Globus services. The core framework establishes the fundamental patterns and infrastructure used throughout the Globus SDK for reliable service integration.
3
4
## Capabilities
5
6
### Base Client Architecture
7
8
Foundation client class providing consistent HTTP operations, authentication, and response handling across all Globus services.
9
10
```python { .api }
11
class BaseClient:
12
"""
13
Abstract base class for all Globus API clients.
14
15
Provides consistent HTTP operations, authentication handling, error processing,
16
and response wrapping across all Globus service clients with standardized
17
initialization patterns and request/response lifecycle management.
18
"""
19
20
def __init__(
21
self,
22
*,
23
app: GlobusApp | None = None,
24
app_scopes: list[Scope] | None = None,
25
authorizer: GlobusAuthorizer | None = None,
26
app_name: str | None = None,
27
base_url: str | None = None,
28
environment: str | None = None,
29
transport_params: dict[str, Any] | None = None
30
) -> None: ...
31
32
@property
33
def default_scope_requirements(self) -> list[Scope]:
34
"""
35
Default scopes required for client operations.
36
37
Returns the scopes that will automatically be added to the
38
client's app scope requirements during initialization.
39
"""
40
41
@property
42
def resource_server(self) -> str | None:
43
"""
44
Resource server identifier for this client.
45
46
Returns the resource server name associated with this client's
47
scopes and authentication requirements.
48
"""
49
50
@property
51
def app_name(self) -> str | None:
52
"""Application name for User-Agent headers."""
53
54
def attach_globus_app(
55
self,
56
app: GlobusApp,
57
app_scopes: list[Scope] | None = None
58
) -> None:
59
"""
60
Attach a GlobusApp to this client.
61
62
Registers the client with the app and adds scope requirements
63
for automatic authentication and token management.
64
65
Parameters:
66
- app: GlobusApp instance to attach
67
- app_scopes: Optional custom scopes instead of defaults
68
"""
69
70
def add_app_scope(
71
self,
72
scope_collection: ScopeCollectionType
73
) -> BaseClient:
74
"""
75
Add additional scopes to the client's app requirements.
76
77
Extends the scope requirements beyond default scopes for
78
accessing additional resources or permissions.
79
80
Parameters:
81
- scope_collection: Scopes to add to requirements
82
83
Returns:
84
Self for method chaining
85
"""
86
87
def get(
88
self,
89
path: str,
90
*,
91
query_params: dict[str, Any] | None = None,
92
headers: dict[str, str] | None = None,
93
automatic_authorization: bool = True
94
) -> GlobusHTTPResponse:
95
"""
96
Make a GET request to the specified path.
97
98
Parameters:
99
- path: Request path (relative to base_url)
100
- query_params: URL query parameters
101
- headers: Additional HTTP headers
102
- automatic_authorization: Use client's authorizer
103
104
Returns:
105
GlobusHTTPResponse with response data and metadata
106
"""
107
108
def post(
109
self,
110
path: str,
111
*,
112
query_params: dict[str, Any] | None = None,
113
data: Any = None,
114
headers: dict[str, str] | None = None,
115
encoding: str | None = None,
116
automatic_authorization: bool = True
117
) -> GlobusHTTPResponse:
118
"""
119
Make a POST request to the specified path.
120
121
Parameters:
122
- path: Request path
123
- query_params: URL query parameters
124
- data: Request body data
125
- headers: Additional headers
126
- encoding: Data encoding (json, form, text)
127
- automatic_authorization: Use client's authorizer
128
129
Returns:
130
GlobusHTTPResponse with response data
131
"""
132
133
def put(
134
self,
135
path: str,
136
*,
137
query_params: dict[str, Any] | None = None,
138
data: Any = None,
139
headers: dict[str, str] | None = None,
140
encoding: str | None = None,
141
automatic_authorization: bool = True
142
) -> GlobusHTTPResponse:
143
"""Make a PUT request to the specified path."""
144
145
def patch(
146
self,
147
path: str,
148
*,
149
query_params: dict[str, Any] | None = None,
150
data: Any = None,
151
headers: dict[str, str] | None = None,
152
encoding: str | None = None,
153
automatic_authorization: bool = True
154
) -> GlobusHTTPResponse:
155
"""Make a PATCH request to the specified path."""
156
157
def delete(
158
self,
159
path: str,
160
*,
161
query_params: dict[str, Any] | None = None,
162
headers: dict[str, str] | None = None,
163
automatic_authorization: bool = True
164
) -> GlobusHTTPResponse:
165
"""Make a DELETE request to the specified path."""
166
167
def request(
168
self,
169
method: str,
170
path: str,
171
*,
172
query_params: dict[str, Any] | None = None,
173
data: Any = None,
174
headers: dict[str, str] | None = None,
175
encoding: str | None = None,
176
allow_redirects: bool = True,
177
stream: bool = False,
178
automatic_authorization: bool = True
179
) -> GlobusHTTPResponse:
180
"""
181
Send an HTTP request with full parameter control.
182
183
Provides complete control over HTTP request parameters with
184
consistent error handling and response processing.
185
186
Parameters:
187
- method: HTTP method (GET, POST, PUT, PATCH, DELETE)
188
- path: Request path
189
- query_params: URL query parameters
190
- data: Request body data
191
- headers: HTTP headers
192
- encoding: Data encoding method
193
- allow_redirects: Follow redirects automatically
194
- stream: Stream response without immediate download
195
- automatic_authorization: Use client authentication
196
197
Returns:
198
GlobusHTTPResponse wrapping the response
199
200
Raises:
201
GlobusAPIError for 4xx/5xx HTTP status codes
202
"""
203
```
204
205
### Authorization Framework
206
207
Comprehensive authorization system supporting multiple authentication flows and token management patterns with consistent interfaces.
208
209
```python { .api }
210
class GlobusAuthorizer(metaclass=ABCMeta):
211
"""
212
Abstract base class for all authorization handlers.
213
214
Defines the interface for generating Authorization headers and
215
handling authorization failures with consistent patterns across
216
different authentication methods.
217
"""
218
219
@abstractmethod
220
def get_authorization_header(self) -> str | None:
221
"""
222
Generate Authorization header value.
223
224
Returns the complete Authorization header value to be included
225
in HTTP requests, or None if no authorization should be used.
226
227
Returns:
228
Authorization header string or None
229
"""
230
231
def handle_missing_authorization(self) -> bool:
232
"""
233
Handle 401 Unauthorized responses.
234
235
Called when a request returns 401 status to allow the authorizer
236
to attempt recovery (e.g., token refresh). Returns True if the
237
authorizer believes it has fixed the issue.
238
239
Returns:
240
True if authorization was updated, False otherwise
241
"""
242
243
class AccessTokenAuthorizer(StaticGlobusAuthorizer):
244
"""
245
Authorization using a single access token.
246
247
Provides Bearer token authorization for requests using a static
248
access token without refresh capabilities.
249
"""
250
251
def __init__(self, access_token: str) -> None: ...
252
253
@property
254
def access_token(self) -> str:
255
"""The access token used for authorization."""
256
257
@property
258
def access_token_hash(self) -> str:
259
"""SHA256 hash of the access token for logging."""
260
261
class RefreshTokenAuthorizer(GlobusAuthorizer):
262
"""
263
Authorization using refresh tokens for automatic token renewal.
264
265
Automatically refreshes access tokens when they expire using
266
stored refresh tokens with configurable retry and caching.
267
"""
268
269
def __init__(
270
self,
271
refresh_token: str,
272
auth_client: AuthClient,
273
access_token: str | None = None,
274
expires_at: int | None = None,
275
on_refresh: Callable[[OAuthTokenResponse], None] | None = None
276
) -> None: ...
277
278
def ensure_valid_token(self) -> str:
279
"""Ensure access token is valid, refreshing if necessary."""
280
281
class ClientCredentialsAuthorizer(GlobusAuthorizer):
282
"""
283
Authorization using OAuth2 Client Credentials flow.
284
285
Automatically obtains and refreshes access tokens using client
286
credentials (client ID and secret) for service-to-service authentication.
287
"""
288
289
def __init__(
290
self,
291
confidential_client: ConfidentialAppAuthClient,
292
scopes: str | Iterable[str] | None = None,
293
access_token: str | None = None,
294
expires_at: int | None = None
295
) -> None: ...
296
297
class BasicAuthorizer(StaticGlobusAuthorizer):
298
"""
299
HTTP Basic authentication authorizer.
300
301
Provides HTTP Basic authentication using username and password
302
with base64 encoding according to RFC 7617.
303
"""
304
305
def __init__(self, username: str, password: str) -> None: ...
306
307
class NullAuthorizer(GlobusAuthorizer):
308
"""
309
No-authentication authorizer.
310
311
Ensures no Authorization header is sent with requests, useful
312
for public APIs or when authentication is handled elsewhere.
313
"""
314
315
def get_authorization_header(self) -> None:
316
"""Always returns None (no authorization)."""
317
```
318
319
### HTTP Response Framework
320
321
Comprehensive response handling with dictionary-like access, iteration support, and metadata preservation for consistent data access patterns.
322
323
```python { .api }
324
class GlobusHTTPResponse:
325
"""
326
Response wrapper providing dict-like access to API response data.
327
328
Wraps HTTP responses with convenient data access methods, error handling,
329
and metadata preservation while maintaining access to raw response data.
330
"""
331
332
def __init__(
333
self,
334
response: Response | GlobusHTTPResponse,
335
client: BaseClient | None = None
336
) -> None: ...
337
338
@property
339
def data(self) -> Any:
340
"""
341
Parsed JSON response data.
342
343
Returns the JSON-parsed response body, or None if the response
344
was not valid JSON or contained no data.
345
"""
346
347
@property
348
def text(self) -> str:
349
"""Raw response text content."""
350
351
@property
352
def binary_content(self) -> bytes:
353
"""Raw response binary content."""
354
355
@property
356
def http_status(self) -> int:
357
"""HTTP status code from the response."""
358
359
@property
360
def http_reason(self) -> str:
361
"""HTTP reason phrase from the response."""
362
363
@property
364
def headers(self) -> dict[str, str]:
365
"""HTTP response headers as a dictionary."""
366
367
@property
368
def content_type(self) -> str | None:
369
"""Content-Type header value."""
370
371
def __getitem__(self, key: str) -> Any:
372
"""
373
Dictionary-style access to response data.
374
375
Provides convenient access to JSON response fields using
376
bracket notation for dict-like usage patterns.
377
"""
378
379
def __contains__(self, key: str) -> bool:
380
"""Check if key exists in response data."""
381
382
def __bool__(self) -> bool:
383
"""Response truthiness based on HTTP success."""
384
385
def get(self, key: str, default: Any = None) -> Any:
386
"""
387
Get response data field with default fallback.
388
389
Parameters:
390
- key: Field name to retrieve
391
- default: Default value if key not found
392
393
Returns:
394
Field value or default
395
"""
396
397
class IterableResponse(GlobusHTTPResponse):
398
"""
399
Response class for paginated data with iteration support.
400
401
Provides iteration over response data arrays with automatic
402
key resolution and consistent iteration patterns across services.
403
"""
404
405
@property
406
def default_iter_key(self) -> str | None:
407
"""Default key name for iteration data."""
408
409
@property
410
def iter_key(self) -> str:
411
"""Key name used for iteration."""
412
413
def __iter__(self) -> Iterator[Any]:
414
"""
415
Iterate over response data items.
416
417
Automatically resolves the appropriate data array from the
418
response and provides iteration over individual items.
419
"""
420
421
class ArrayResponse(GlobusHTTPResponse):
422
"""
423
Response class for JSON array data with enhanced array operations.
424
425
Specialized for responses that contain JSON arrays at the top level
426
with length operations and efficient iteration.
427
"""
428
429
def __iter__(self) -> Iterator[Any]:
430
"""Iterate over array elements."""
431
432
def __len__(self) -> int:
433
"""Get number of elements in the array."""
434
```
435
436
### Scope Management System
437
438
Type-safe scope construction and management with service-specific builders and dependency handling for complex permission requirements.
439
440
```python { .api }
441
class Scope:
442
"""
443
Representation of a Globus scope with dependency management.
444
445
Represents a single scope string with support for dependent scopes,
446
optional scopes, and scope relationships for complex permission structures.
447
"""
448
449
def __init__(
450
self,
451
scope: str,
452
*,
453
optional: bool = False,
454
dependencies: Iterable[Scope] | None = None
455
) -> None: ...
456
457
@property
458
def scope_string(self) -> str:
459
"""The scope string value."""
460
461
@property
462
def optional(self) -> bool:
463
"""Whether this scope is optional."""
464
465
@property
466
def dependencies(self) -> list[Scope]:
467
"""List of dependent scopes."""
468
469
def add_dependency(self, scope: Scope) -> None:
470
"""
471
Add a dependent scope.
472
473
Creates a dependency relationship where this scope requires
474
the dependent scope to be granted as well.
475
476
Parameters:
477
- scope: Scope that this scope depends on
478
"""
479
480
def serialize(self) -> str:
481
"""
482
Serialize scope and dependencies to string format.
483
484
Converts the scope and its dependency tree to the string
485
representation used in OAuth2 flows and API requests.
486
487
Returns:
488
Serialized scope string with dependencies
489
"""
490
491
class ScopeBuilder:
492
"""
493
Base class for service-specific scope builders.
494
495
Provides a foundation for constructing scopes with service-specific
496
patterns and relationships while maintaining type safety.
497
"""
498
499
@property
500
def resource_server(self) -> str:
501
"""Resource server identifier for this scope builder."""
502
503
class MutableScope:
504
"""
505
Mutable scope object for dynamic scope construction.
506
507
Allows modification of scope properties and dependencies after
508
creation for complex scope building scenarios.
509
"""
510
511
def __init__(
512
self,
513
scope_string: str,
514
*,
515
optional: bool = False,
516
dependencies: list[MutableScope] | None = None
517
) -> None: ...
518
519
def add_dependency(self, scope: MutableScope) -> None:
520
"""Add a dependency to this scope."""
521
522
def freeze(self) -> Scope:
523
"""
524
Convert to immutable Scope object.
525
526
Freezes the mutable scope into an immutable Scope instance
527
with all current dependencies and properties preserved.
528
529
Returns:
530
Immutable Scope representation
531
"""
532
533
# Service-specific scope builders
534
class AuthScopes(ScopeBuilder):
535
"""Scope builder for Auth service scopes."""
536
537
openid: str = "openid"
538
profile: str = "profile"
539
email: str = "email"
540
view_identity_set: str = "urn:globus:auth:scope:auth.globus.org:view_identity_set"
541
542
class TransferScopes(ScopeBuilder):
543
"""Scope builder for Transfer service scopes."""
544
545
all: str = "urn:globus:auth:scope:transfer.api.globus.org:all"
546
547
class ComputeScopes(ScopeBuilder):
548
"""Scope builder for Compute service scopes."""
549
550
all: str = "urn:globus:auth:scope:compute.api.globus.org:all"
551
552
class FlowsScopes(ScopeBuilder):
553
"""Scope builder for Flows service scopes."""
554
555
all: str = "urn:globus:auth:scope:flows.api.globus.org:all"
556
manage_flows: str = "urn:globus:auth:scope:flows.api.globus.org:manage_flows"
557
run_manage: str = "urn:globus:auth:scope:flows.api.globus.org:run_manage"
558
559
# Dynamic scope builders for resource-specific scopes
560
class GCSCollectionScopeBuilder(ScopeBuilder):
561
"""Dynamic scope builder for GCS collection-specific scopes."""
562
563
def __init__(self, collection_id: str) -> None: ...
564
565
@property
566
def data_access(self) -> str:
567
"""Data access scope for this collection."""
568
569
class GCSEndpointScopeBuilder(ScopeBuilder):
570
"""Dynamic scope builder for GCS endpoint-specific scopes."""
571
572
def __init__(self, endpoint_id: str) -> None: ...
573
574
@property
575
def manage_collections(self) -> str:
576
"""Collection management scope for this endpoint."""
577
578
# Utility functions
579
def scopes_to_str(scopes: ScopeCollectionType) -> str:
580
"""
581
Convert scope collection to space-separated string.
582
583
Parameters:
584
- scopes: Collection of scopes to convert
585
586
Returns:
587
Space-separated scope string for OAuth2 flows
588
"""
589
590
def scopes_to_scope_list(scopes: ScopeCollectionType) -> list[Scope]:
591
"""
592
Normalize scope collection to list of Scope objects.
593
594
Parameters:
595
- scopes: Mixed collection of scopes to normalize
596
597
Returns:
598
Normalized list of Scope objects
599
"""
600
```
601
602
### Error Handling Framework
603
604
Comprehensive error hierarchy with service-specific errors, detailed error information, and consistent error handling patterns.
605
606
```python { .api }
607
class GlobusError(Exception):
608
"""
609
Base exception class for all Globus SDK errors.
610
611
Root of the Globus SDK exception hierarchy providing common
612
error handling patterns and metadata preservation.
613
"""
614
615
class GlobusSDKUsageError(GlobusError):
616
"""
617
Exception for SDK misuse and configuration errors.
618
619
Raised when the SDK is used incorrectly, such as invalid
620
parameter combinations or missing required configuration.
621
"""
622
623
class ValidationError(GlobusSDKUsageError):
624
"""
625
Exception for input validation failures.
626
627
Raised when user-provided data fails validation checks
628
before being sent to Globus services.
629
"""
630
631
class GlobusAPIError(GlobusError):
632
"""
633
Base exception for HTTP 4xx and 5xx API errors.
634
635
Provides access to HTTP response data, error details, and
636
structured error information from Globus services.
637
"""
638
639
def __init__(self, response: Response) -> None: ...
640
641
@property
642
def http_status(self) -> int:
643
"""HTTP status code from the error response."""
644
645
@property
646
def http_reason(self) -> str:
647
"""HTTP reason phrase from the error response."""
648
649
@property
650
def headers(self) -> dict[str, str]:
651
"""HTTP headers from the error response."""
652
653
@property
654
def text(self) -> str:
655
"""Raw error response text."""
656
657
@property
658
def binary_content(self) -> bytes:
659
"""Raw error response binary content."""
660
661
@property
662
def data(self) -> Any:
663
"""Parsed JSON error data if available."""
664
665
@property
666
def code(self) -> str:
667
"""Globus error code from the response."""
668
669
@property
670
def message(self) -> str:
671
"""Human-readable error message."""
672
673
def __getitem__(self, key: str) -> Any:
674
"""Dictionary-style access to error data."""
675
676
def __contains__(self, key: str) -> bool:
677
"""Check if key exists in error data."""
678
679
def get(self, key: str, default: Any = None) -> Any:
680
"""Get error data field with default."""
681
682
class ErrorSubdocument:
683
"""
684
Container for detailed error information from API responses.
685
686
Represents individual error details within structured error
687
responses with code, message, and additional metadata.
688
"""
689
690
def __init__(self, data: dict[str, Any]) -> None: ...
691
692
@property
693
def code(self) -> str:
694
"""Error code identifier."""
695
696
@property
697
def message(self) -> str:
698
"""Human-readable error message."""
699
700
@property
701
def detail(self) -> str:
702
"""Detailed error description."""
703
704
# Network-related errors
705
class NetworkError(GlobusError):
706
"""Base class for network connectivity errors."""
707
708
class GlobusTimeoutError(NetworkError):
709
"""Request timeout errors."""
710
711
class GlobusConnectionError(NetworkError):
712
"""Connection establishment errors."""
713
714
class GlobusConnectionTimeoutError(GlobusConnectionError):
715
"""Connection timeout errors."""
716
717
# Error information containers
718
class ErrorInfo:
719
"""
720
Container for structured error information.
721
722
Provides access to error details, codes, and additional
723
context information from API error responses.
724
"""
725
726
class ErrorInfoContainer:
727
"""
728
Collection of ErrorInfo objects.
729
730
Manages multiple error information objects with iteration
731
and lookup capabilities for complex error scenarios.
732
"""
733
734
class ConsentRequiredInfo(ErrorInfo):
735
"""
736
Specialized error info for consent required errors.
737
738
Provides structured information about required consents
739
and authorization parameters for resolving access issues.
740
"""
741
742
@property
743
def required_scopes(self) -> list[str]:
744
"""Scopes requiring user consent."""
745
746
@property
747
def authorization_parameters(self) -> dict[str, Any]:
748
"""OAuth2 parameters for consent flow."""
749
750
# Warnings
751
class RemovedInV4Warning(UserWarning):
752
"""
753
Warning for features that will be removed in SDK v4.0.
754
755
Indicates deprecated functionality that should be migrated
756
to newer alternatives before the next major version.
757
"""
758
```
759
760
### Utility Classes and Functions
761
762
Helper classes and utility functions providing common functionality across the SDK with consistent patterns and type safety.
763
764
```python { .api }
765
class PayloadWrapper:
766
"""
767
Base class for API request payload helpers.
768
769
Provides a foundation for building structured request data with
770
validation, serialization, and convenience methods for API operations.
771
"""
772
773
def __init__(self) -> None: ...
774
775
def __getitem__(self, key: str) -> Any:
776
"""Dictionary-style access to payload data."""
777
778
def __setitem__(self, key: str, value: Any) -> None:
779
"""Dictionary-style assignment to payload data."""
780
781
def __contains__(self, key: str) -> bool:
782
"""Check if key exists in payload data."""
783
784
def get(self, key: str, default: Any = None) -> Any:
785
"""Get payload field with default fallback."""
786
787
class MissingType:
788
"""
789
Sentinel type for distinguishing missing values from None.
790
791
Used throughout the SDK to represent truly missing/unset values
792
as distinct from explicit None values.
793
"""
794
795
# Sentinel instance
796
MISSING: MissingType = MissingType()
797
798
# Utility functions
799
def filter_missing(data: dict[str, Any]) -> dict[str, Any]:
800
"""
801
Remove MISSING values from dictionary.
802
803
Filters out all keys with MISSING sentinel values to produce
804
clean dictionaries for API requests.
805
806
Parameters:
807
- data: Dictionary potentially containing MISSING values
808
809
Returns:
810
Dictionary with MISSING values removed
811
"""
812
813
def slash_join(*parts: str) -> str:
814
"""
815
Join URL path components with proper slash handling.
816
817
Joins path segments ensuring single slashes between components
818
and proper handling of leading/trailing slashes.
819
820
Parameters:
821
- *parts: Path components to join
822
823
Returns:
824
Properly formatted URL path
825
"""
826
827
def sha256_string(data: str) -> str:
828
"""
829
Compute SHA256 hash of a string.
830
831
Parameters:
832
- data: String to hash
833
834
Returns:
835
Hexadecimal SHA256 hash
836
"""
837
838
def b64str(data: str) -> str:
839
"""
840
Base64 encode a string.
841
842
Parameters:
843
- data: String to encode
844
845
Returns:
846
Base64 encoded string
847
"""
848
```
849
850
## Common Usage Patterns
851
852
### Basic Client Initialization
853
854
```python
855
from globus_sdk import TransferClient, AccessTokenAuthorizer
856
857
# Simple token-based authorization
858
authorizer = AccessTokenAuthorizer("your_access_token")
859
client = TransferClient(authorizer=authorizer)
860
861
# Application-based initialization
862
from globus_sdk import UserApp
863
864
app = UserApp("my-app", client_id="your_client_id")
865
client = TransferClient(app=app)
866
```
867
868
### Custom Authorization Patterns
869
870
```python
871
from globus_sdk import RefreshTokenAuthorizer, AuthClient
872
873
# Refresh token authorization with callback
874
def token_refresh_callback(token_response):
875
# Save new tokens to persistent storage
876
save_tokens(token_response.by_resource_server)
877
878
auth_client = AuthClient()
879
authorizer = RefreshTokenAuthorizer(
880
refresh_token="your_refresh_token",
881
auth_client=auth_client,
882
on_refresh=token_refresh_callback
883
)
884
885
client = TransferClient(authorizer=authorizer)
886
```
887
888
### Scope Management
889
890
```python
891
from globus_sdk import TransferScopes, ComputeScopes, Scope
892
893
# Using service scope builders
894
base_scopes = [
895
Scope(TransferScopes.all),
896
Scope(ComputeScopes.all)
897
]
898
899
# Dynamic collection scopes
900
from globus_sdk import GCSCollectionScopeBuilder
901
902
collection_scope_builder = GCSCollectionScopeBuilder("collection-uuid")
903
data_access_scope = Scope(collection_scope_builder.data_access, optional=True)
904
905
# Scope with dependencies
906
transfer_scope = Scope(TransferScopes.all)
907
transfer_scope.add_dependency(data_access_scope)
908
```
909
910
### Error Handling Patterns
911
912
```python
913
from globus_sdk import GlobusAPIError, ConsentRequiredInfo
914
915
try:
916
response = client.submit_transfer(transfer_data)
917
except GlobusAPIError as e:
918
if e.http_status == 403:
919
# Check for consent required errors
920
if e.info.consent_required:
921
consent_info = ConsentRequiredInfo(e)
922
print(f"Consent required for scopes: {consent_info.required_scopes}")
923
924
# Handle consent flow
925
auth_params = consent_info.authorization_parameters
926
# Redirect user to consent URL...
927
else:
928
print(f"API Error {e.http_status}: {e.message}")
929
# Handle other API errors
930
except Exception as e:
931
print(f"Unexpected error: {e}")
932
```
933
934
### Response Handling
935
936
```python
937
# Dictionary-style access
938
response = client.get_endpoint("endpoint-uuid")
939
print(f"Endpoint name: {response['display_name']}")
940
941
# Iterate over paginated results
942
endpoints = client.endpoint_search("tutorial")
943
for endpoint in endpoints:
944
print(f"Found: {endpoint['display_name']}")
945
946
# Access response metadata
947
print(f"Status: {response.http_status}")
948
print(f"Headers: {response.headers}")
949
```
950
951
### Advanced Client Configuration
952
953
```python
954
from globus_sdk import BaseClient
955
956
class CustomClient(BaseClient):
957
service_name = "my_service"
958
error_class = MyCustomAPIError
959
960
@property
961
def default_scope_requirements(self):
962
return [Scope("https://my-service.org/scope")]
963
964
def custom_operation(self, data):
965
return self.post("/custom", data=data)
966
967
# Use custom transport parameters
968
custom_client = CustomClient(
969
authorizer=authorizer,
970
transport_params={
971
"timeout": 60,
972
"retries": 3,
973
"user_agent_suffix": "MyApp/1.0"
974
}
975
)
976
```