|
6 | 6 | import platform |
7 | 7 | import sys |
8 | 8 | import urllib.parse |
9 | | -from typing import Dict, Iterable, Optional |
| 9 | +from typing import Dict, Iterable, List, Optional, Tuple |
10 | 10 |
|
11 | 11 | import requests |
12 | 12 |
|
@@ -44,6 +44,32 @@ def __repr__(self) -> str: |
44 | 44 | return f"<ConfigAttribute '{self.name}' {self.transform.__name__}>" |
45 | 45 |
|
46 | 46 |
|
| 47 | +_DEFAULT_PRODUCT_NAME = 'unknown' |
| 48 | +_DEFAULT_PRODUCT_VERSION = '0.0.0' |
| 49 | +_STATIC_USER_AGENT: Tuple[str, str, List[str]] = (_DEFAULT_PRODUCT_NAME, _DEFAULT_PRODUCT_VERSION, []) |
| 50 | + |
| 51 | + |
| 52 | +def with_product(product: str, product_version: str): |
| 53 | + """[INTERNAL API] Change the product name and version used in the User-Agent header.""" |
| 54 | + global _STATIC_USER_AGENT |
| 55 | + prev_product, prev_version, prev_other_info = _STATIC_USER_AGENT |
| 56 | + logger.debug(f'Changing product from {prev_product}/{prev_version} to {product}/{product_version}') |
| 57 | + _STATIC_USER_AGENT = product, product_version, prev_other_info |
| 58 | + |
| 59 | + |
| 60 | +def with_user_agent_extra(key: str, value: str): |
| 61 | + """[INTERNAL API] Add extra metadata to the User-Agent header when developing a library.""" |
| 62 | + global _STATIC_USER_AGENT |
| 63 | + product_name, product_version, other_info = _STATIC_USER_AGENT |
| 64 | + for item in other_info: |
| 65 | + if item.startswith(f"{key}/"): |
| 66 | + # ensure that we don't have duplicates |
| 67 | + other_info.remove(item) |
| 68 | + break |
| 69 | + other_info.append(f"{key}/{value}") |
| 70 | + _STATIC_USER_AGENT = product_name, product_version, other_info |
| 71 | + |
| 72 | + |
47 | 73 | class Config: |
48 | 74 | host: str = ConfigAttribute(env='DATABRICKS_HOST') |
49 | 75 | account_id: str = ConfigAttribute(env='DATABRICKS_ACCOUNT_ID') |
@@ -85,12 +111,21 @@ def __init__(self, |
85 | 111 | # Deprecated. Use credentials_strategy instead. |
86 | 112 | credentials_provider: CredentialsStrategy = None, |
87 | 113 | credentials_strategy: CredentialsStrategy = None, |
88 | | - product="unknown", |
89 | | - product_version="0.0.0", |
| 114 | + product=_DEFAULT_PRODUCT_NAME, |
| 115 | + product_version=_DEFAULT_PRODUCT_VERSION, |
90 | 116 | clock: Clock = None, |
91 | 117 | **kwargs): |
92 | 118 | self._header_factory = None |
93 | 119 | self._inner = {} |
| 120 | + # as in SDK for Go, pull information from global static user agent context, |
| 121 | + # so that we can track additional metadata for mid-stream libraries, as well |
| 122 | + # as for cases, when the downstream product is used as a library and is not |
| 123 | + # configured with a proper product name and version. |
| 124 | + static_product, static_version, _ = _STATIC_USER_AGENT |
| 125 | + if product == _DEFAULT_PRODUCT_NAME: |
| 126 | + product = static_product |
| 127 | + if product_version == _DEFAULT_PRODUCT_VERSION: |
| 128 | + product_version = static_version |
94 | 129 | self._user_agent_other_info = [] |
95 | 130 | if credentials_strategy and credentials_provider: |
96 | 131 | raise ValueError( |
@@ -234,6 +269,12 @@ def user_agent(self): |
234 | 269 | ] |
235 | 270 | if len(self._user_agent_other_info) > 0: |
236 | 271 | ua.append(' '.join(self._user_agent_other_info)) |
| 272 | + # as in SDK for Go, pull information from global static user agent context, |
| 273 | + # so that we can track additional metadata for mid-stream libraries. this value |
| 274 | + # is shared across all instances of Config objects intentionally. |
| 275 | + _, _, static_info = _STATIC_USER_AGENT |
| 276 | + if len(static_info) > 0: |
| 277 | + ua.append(' '.join(static_info)) |
237 | 278 | if len(self._upstream_user_agent) > 0: |
238 | 279 | ua.append(self._upstream_user_agent) |
239 | 280 | if 'DATABRICKS_RUNTIME_VERSION' in os.environ: |
|
0 commit comments