@@ -274,299 +274,8 @@ def replace_client_path(client: Client, base_path: str) -> Client:
274
274
parsed = urllib .parse .urlparse (client .base_url )
275
275
# _replace is not private, it's part of the NamedTuple API but prefixed _ to avoid conflicts
276
276
updated_url = parsed ._replace (path = base_path )
277
- return client .with_base_url (updated_url .geturl ())
278
-
279
-
280
- def v3_stable_client (client : Client ) -> Client :
281
- """Override a client's base URL with a v2 stable path."""
282
- return replace_client_path (client , "api/v3-draft" )
283
-
284
-
285
- def v3_alpha_client (client : Client ) -> Client :
286
- """Override a client's base URL with a v2-alpha path."""
287
- return replace_client_path (client , "api/v3-alpha" )
288
-
289
-
290
- def v3_beta_client (client : Client ) -> Client :
291
- """Override a client's base URL with a v2-beta path."""
292
- return replace_client_path (client , "api/v3-beta" )
293
- import ssl
294
- from typing import Any , Optional , Union
295
-
296
- import httpx
297
- from attrs import define , evolve , field
298
- import urllib .parse
299
-
300
-
301
- @define
302
- class Client :
303
- """A class for keeping track of data related to the API
304
-
305
- The following are accepted as keyword arguments and will be used to construct httpx Clients internally:
306
-
307
- ``base_url``: The base URL for the API, all requests are made to a relative path to this URL
308
-
309
- ``cookies``: A dictionary of cookies to be sent with every request
310
-
311
- ``headers``: A dictionary of headers to be sent with every request
312
-
313
- ``timeout``: The maximum amount of a time a request can take. API functions will raise
314
- httpx.TimeoutException if this is exceeded.
315
-
316
- ``verify_ssl``: Whether or not to verify the SSL certificate of the API server. This should be True in production,
317
- but can be set to False for testing purposes.
318
-
319
- ``follow_redirects``: Whether or not to follow redirects. Default value is False.
320
-
321
- ``httpx_args``: A dictionary of additional arguments to be passed to the ``httpx.Client`` and ``httpx.AsyncClient`` constructor.
322
-
323
-
324
- Attributes:
325
- raise_on_unexpected_status: Whether or not to raise an errors.UnexpectedStatus if the API returns a
326
- status code that was not documented in the source OpenAPI document. Can also be provided as a keyword
327
- argument to the constructor.
328
- """
329
-
330
- raise_on_unexpected_status : bool = field (default = False , kw_only = True )
331
- _base_url : str = field (alias = "base_url" )
332
- _cookies : dict [str , str ] = field (factory = dict , kw_only = True , alias = "cookies" )
333
- _headers : dict [str , str ] = field (factory = dict , kw_only = True , alias = "headers" )
334
- _timeout : Optional [httpx .Timeout ] = field (default = None , kw_only = True , alias = "timeout" )
335
- _verify_ssl : Union [str , bool , ssl .SSLContext ] = field (default = True , kw_only = True , alias = "verify_ssl" )
336
- _follow_redirects : bool = field (default = False , kw_only = True , alias = "follow_redirects" )
337
- _httpx_args : dict [str , Any ] = field (factory = dict , kw_only = True , alias = "httpx_args" )
338
- _client : Optional [httpx .Client ] = field (default = None , init = False )
339
- _async_client : Optional [httpx .AsyncClient ] = field (default = None , init = False )
340
-
341
- def with_headers (self , headers : dict [str , str ]) -> "Client" :
342
- """Get a new client matching this one with additional headers"""
343
- if self ._client is not None :
344
- self ._client .headers .update (headers )
345
- if self ._async_client is not None :
346
- self ._async_client .headers .update (headers )
347
- return evolve (self , headers = {** self ._headers , ** headers })
348
-
349
- def with_cookies (self , cookies : dict [str , str ]) -> "Client" :
350
- """Get a new client matching this one with additional cookies"""
351
- if self ._client is not None :
352
- self ._client .cookies .update (cookies )
353
- if self ._async_client is not None :
354
- self ._async_client .cookies .update (cookies )
355
- return evolve (self , cookies = {** self ._cookies , ** cookies })
356
-
357
- def with_timeout (self , timeout : httpx .Timeout ) -> "Client" :
358
- """Get a new client matching this one with a new timeout (in seconds)"""
359
- if self ._client is not None :
360
- self ._client .timeout = timeout
361
- if self ._async_client is not None :
362
- self ._async_client .timeout = timeout
363
- return evolve (self , timeout = timeout )
364
-
365
- def set_httpx_client (self , client : httpx .Client ) -> "Client" :
366
- """Manually set the underlying httpx.Client
367
-
368
- **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout.
369
- """
370
- self ._client = client
371
- return self
372
-
373
- def get_httpx_client (self ) -> httpx .Client :
374
- """Get the underlying httpx.Client, constructing a new one if not previously set"""
375
- if self ._client is None :
376
- self ._client = httpx .Client (
377
- base_url = self ._base_url ,
378
- cookies = self ._cookies ,
379
- headers = self ._headers ,
380
- timeout = self ._timeout ,
381
- verify = self ._verify_ssl ,
382
- follow_redirects = self ._follow_redirects ,
383
- ** self ._httpx_args ,
384
- )
385
- return self ._client
386
-
387
- def __enter__ (self ) -> "Client" :
388
- """Enter a context manager for self.client—you cannot enter twice (see httpx docs)"""
389
- self .get_httpx_client ().__enter__ ()
390
- return self
391
-
392
- def __exit__ (self , * args : Any , ** kwargs : Any ) -> None :
393
- """Exit a context manager for internal httpx.Client (see httpx docs)"""
394
- self .get_httpx_client ().__exit__ (* args , ** kwargs )
395
-
396
- def set_async_httpx_client (self , async_client : httpx .AsyncClient ) -> "Client" :
397
- """Manually the underlying httpx.AsyncClient
398
-
399
- **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout.
400
- """
401
- self ._async_client = async_client
402
- return self
403
-
404
- def get_async_httpx_client (self ) -> httpx .AsyncClient :
405
- """Get the underlying httpx.AsyncClient, constructing a new one if not previously set"""
406
- if self ._async_client is None :
407
- self ._async_client = httpx .AsyncClient (
408
- base_url = self ._base_url ,
409
- cookies = self ._cookies ,
410
- headers = self ._headers ,
411
- timeout = self ._timeout ,
412
- verify = self ._verify_ssl ,
413
- follow_redirects = self ._follow_redirects ,
414
- ** self ._httpx_args ,
415
- )
416
- return self ._async_client
417
-
418
- async def __aenter__ (self ) -> "Client" :
419
- """Enter a context manager for underlying httpx.AsyncClient—you cannot enter twice (see httpx docs)"""
420
- await self .get_async_httpx_client ().__aenter__ ()
421
- return self
422
-
423
- async def __aexit__ (self , * args : Any , ** kwargs : Any ) -> None :
424
- """Exit a context manager for underlying httpx.AsyncClient (see httpx docs)"""
425
- await self .get_async_httpx_client ().__aexit__ (* args , ** kwargs )
426
-
427
-
428
- @define
429
- class AuthenticatedClient :
430
- """A Client which has been authenticated for use on secured endpoints
431
-
432
- The following are accepted as keyword arguments and will be used to construct httpx Clients internally:
433
-
434
- ``base_url``: The base URL for the API, all requests are made to a relative path to this URL
435
-
436
- ``cookies``: A dictionary of cookies to be sent with every request
437
-
438
- ``headers``: A dictionary of headers to be sent with every request
439
-
440
- ``timeout``: The maximum amount of a time a request can take. API functions will raise
441
- httpx.TimeoutException if this is exceeded.
442
-
443
- ``verify_ssl``: Whether or not to verify the SSL certificate of the API server. This should be True in production,
444
- but can be set to False for testing purposes.
445
-
446
- ``follow_redirects``: Whether or not to follow redirects. Default value is False.
447
-
448
- ``httpx_args``: A dictionary of additional arguments to be passed to the ``httpx.Client`` and ``httpx.AsyncClient`` constructor.
449
-
450
-
451
- Attributes:
452
- raise_on_unexpected_status: Whether or not to raise an errors.UnexpectedStatus if the API returns a
453
- status code that was not documented in the source OpenAPI document. Can also be provided as a keyword
454
- argument to the constructor.
455
- token: The token to use for authentication
456
- prefix: The prefix to use for the Authorization header
457
- auth_header_name: The name of the Authorization header
458
- """
459
-
460
- raise_on_unexpected_status : bool = field (default = False , kw_only = True )
461
- _base_url : str = field (alias = "base_url" )
462
- _cookies : dict [str , str ] = field (factory = dict , kw_only = True , alias = "cookies" )
463
- _headers : dict [str , str ] = field (factory = dict , kw_only = True , alias = "headers" )
464
- _timeout : Optional [httpx .Timeout ] = field (default = None , kw_only = True , alias = "timeout" )
465
- _verify_ssl : Union [str , bool , ssl .SSLContext ] = field (default = True , kw_only = True , alias = "verify_ssl" )
466
- _follow_redirects : bool = field (default = False , kw_only = True , alias = "follow_redirects" )
467
- _httpx_args : dict [str , Any ] = field (factory = dict , kw_only = True , alias = "httpx_args" )
468
- _client : Optional [httpx .Client ] = field (default = None , init = False )
469
- _async_client : Optional [httpx .AsyncClient ] = field (default = None , init = False )
470
-
471
- token : str
472
- prefix : str = "Bearer"
473
- auth_header_name : str = "Authorization"
474
-
475
- def with_headers (self , headers : dict [str , str ]) -> "AuthenticatedClient" :
476
- """Get a new client matching this one with additional headers"""
477
- if self ._client is not None :
478
- self ._client .headers .update (headers )
479
- if self ._async_client is not None :
480
- self ._async_client .headers .update (headers )
481
- return evolve (self , headers = {** self ._headers , ** headers })
482
-
483
- def with_cookies (self , cookies : dict [str , str ]) -> "AuthenticatedClient" :
484
- """Get a new client matching this one with additional cookies"""
485
- if self ._client is not None :
486
- self ._client .cookies .update (cookies )
487
- if self ._async_client is not None :
488
- self ._async_client .cookies .update (cookies )
489
- return evolve (self , cookies = {** self ._cookies , ** cookies })
490
-
491
- def with_timeout (self , timeout : httpx .Timeout ) -> "AuthenticatedClient" :
492
- """Get a new client matching this one with a new timeout (in seconds)"""
493
- if self ._client is not None :
494
- self ._client .timeout = timeout
495
- if self ._async_client is not None :
496
- self ._async_client .timeout = timeout
497
- return evolve (self , timeout = timeout )
498
-
499
- def set_httpx_client (self , client : httpx .Client ) -> "AuthenticatedClient" :
500
- """Manually set the underlying httpx.Client
501
-
502
- **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout.
503
- """
504
- self ._client = client
505
- return self
506
-
507
- def get_httpx_client (self ) -> httpx .Client :
508
- """Get the underlying httpx.Client, constructing a new one if not previously set"""
509
- if self ._client is None :
510
- self ._headers [self .auth_header_name ] = f"{ self .prefix } { self .token } " if self .prefix else self .token
511
- self ._client = httpx .Client (
512
- base_url = self ._base_url ,
513
- cookies = self ._cookies ,
514
- headers = self ._headers ,
515
- timeout = self ._timeout ,
516
- verify = self ._verify_ssl ,
517
- follow_redirects = self ._follow_redirects ,
518
- ** self ._httpx_args ,
519
- )
520
- return self ._client
521
-
522
- def __enter__ (self ) -> "AuthenticatedClient" :
523
- """Enter a context manager for self.client—you cannot enter twice (see httpx docs)"""
524
- self .get_httpx_client ().__enter__ ()
525
- return self
526
-
527
- def __exit__ (self , * args : Any , ** kwargs : Any ) -> None :
528
- """Exit a context manager for internal httpx.Client (see httpx docs)"""
529
- self .get_httpx_client ().__exit__ (* args , ** kwargs )
530
-
531
- def set_async_httpx_client (self , async_client : httpx .AsyncClient ) -> "AuthenticatedClient" :
532
- """Manually the underlying httpx.AsyncClient
533
-
534
- **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout.
535
- """
536
- self ._async_client = async_client
537
- return self
538
-
539
- def get_async_httpx_client (self ) -> httpx .AsyncClient :
540
- """Get the underlying httpx.AsyncClient, constructing a new one if not previously set"""
541
- if self ._async_client is None :
542
- self ._headers [self .auth_header_name ] = f"{ self .prefix } { self .token } " if self .prefix else self .token
543
- self ._async_client = httpx .AsyncClient (
544
- base_url = self ._base_url ,
545
- cookies = self ._cookies ,
546
- headers = self ._headers ,
547
- timeout = self ._timeout ,
548
- verify = self ._verify_ssl ,
549
- follow_redirects = self ._follow_redirects ,
550
- ** self ._httpx_args ,
551
- )
552
- return self ._async_client
553
-
554
- async def __aenter__ (self ) -> "AuthenticatedClient" :
555
- """Enter a context manager for underlying httpx.AsyncClient—you cannot enter twice (see httpx docs)"""
556
- await self .get_async_httpx_client ().__aenter__ ()
557
- return self
558
-
559
- async def __aexit__ (self , * args : Any , ** kwargs : Any ) -> None :
560
- """Exit a context manager for underlying httpx.AsyncClient (see httpx docs)"""
561
- await self .get_async_httpx_client ().__aexit__ (* args , ** kwargs )
562
-
563
-
564
- def replace_client_path (client : Client , base_path : str ) -> Client :
565
- """Override a client's base URL with a new path. Does not update scheme, host, or other URL parts."""
566
- parsed = urllib .parse .urlparse (client .base_url )
567
- # _replace is not private, it's part of the NamedTuple API but prefixed _ to avoid conflicts
568
- updated_url = parsed ._replace (path = base_path )
569
- return client .with_base_url (updated_url .geturl ())
277
+ client .base_url = updated_url .geturl ()
278
+ return client
570
279
571
280
572
281
def v3_stable_client (client : Client ) -> Client :
0 commit comments