Skip to content

Commit a75398f

Browse files
committed
Identify Python SDK requests consistently
Assisted-by: Codex:GPT-6
1 parent bb5b024 commit a75398f

5 files changed

Lines changed: 80 additions & 3 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
### Changed
6+
7+
- Use `socket-sdk-python/<version>` as the default User-Agent. Explicit caller
8+
overrides continue to identify the calling application.
9+
310
## 3.6.2
411

512
### Fixed: reject retired API credentials before use

‎README.rst‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Initializing the module
2727
- **token (str)** - The Socket API Key for your Organization
2828
- **timeout (int)** - The number of seconds to wait before failing the connection
2929
- **allow_unverified (bool)** - Whether to skip SSL certificate verification (default: False). Set to True for testing with self-signed certificates.
30-
- **user_agent (str, optional)** - Custom User-Agent string to use in API requests. If not provided, defaults to "SocketSDKPython/{version}"
30+
- **user_agent (str, optional)** - Custom User-Agent string to use in API requests. If not provided, defaults to "socket-sdk-python/{version}". Applications such as the Socket CLI can override this with their own product name and version.
3131

3232
Supported Functions
3333
-------------------

‎socketdev/core/api.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
APIInsufficientPermissions,
1717
APIOrganizationNotAllowed,
1818
)
19-
from socketdev.version import __version__
19+
from socketdev.user_agent import USER_AGENT
2020
from requests.exceptions import Timeout, ConnectionError
2121
import time
2222

@@ -54,7 +54,7 @@ def do_request(
5454
raise APIKeyMissing
5555

5656
if headers is None:
57-
user_agent_string = self.user_agent if self.user_agent is not None else f"SocketSDKPython/{__version__}"
57+
user_agent_string = self.user_agent if self.user_agent is not None else USER_AGENT
5858
headers = {
5959
"Authorization": f"Basic {self.encoded_key}",
6060
"User-Agent": user_agent_string,

‎socketdev/user_agent.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from socketdev.version import __version__
2+
3+
USER_AGENT_NAME = "socket-sdk-python"
4+
USER_AGENT = f"{USER_AGENT_NAME}/{__version__}"

‎tests/unit/test_user_agent.py‎

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import pytest
2+
import requests
3+
4+
from socketdev import socketdev
5+
from socketdev.version import __version__
6+
7+
8+
@pytest.mark.parametrize("method", ["GET", "POST"])
9+
@pytest.mark.parametrize(
10+
"user_agent",
11+
[
12+
None,
13+
"SocketPythonCLI/2.11.7",
14+
"socket-python-cli/2.11.7",
15+
"socket-basics/3.4.1",
16+
"custom-client/1.0.0",
17+
"",
18+
],
19+
)
20+
def test_prepared_requests_use_sdk_identity_or_explicit_caller(
21+
monkeypatch, method, user_agent
22+
):
23+
captured = []
24+
25+
def send(session, request, **kwargs):
26+
captured.append(request)
27+
response = requests.Response()
28+
response.status_code = 200
29+
response._content = b"{}"
30+
return response
31+
32+
monkeypatch.setattr(requests.sessions.Session, "send", send)
33+
sdk = socketdev(token="synthetic-test-token", user_agent=user_agent)
34+
files = [("manifest", ("package.json", b"{}"))] if method == "POST" else None
35+
36+
sdk.api.do_request("organizations", method=method, files=files)
37+
38+
assert len(captured) == 1
39+
expected = user_agent if user_agent is not None else f"socket-sdk-python/{__version__}"
40+
assert captured[0].headers["User-Agent"] == expected
41+
assert captured[0].headers["Authorization"].startswith("Basic ")
42+
if files:
43+
assert captured[0].headers["Content-Type"].startswith("multipart/form-data;")
44+
45+
46+
def test_endpoint_wrappers_share_the_sdk_identity(monkeypatch):
47+
captured = []
48+
49+
def send(session, request, **kwargs):
50+
captured.append(request)
51+
response = requests.Response()
52+
response.status_code = 200
53+
response._content = b"{}"
54+
return response
55+
56+
monkeypatch.setattr(requests.sessions.Session, "send", send)
57+
sdk = socketdev(token="synthetic-test-token")
58+
59+
sdk.org.get()
60+
sdk.basics.get_config("test-org")
61+
62+
assert len(captured) == 2
63+
assert all(
64+
request.headers["User-Agent"] == f"socket-sdk-python/{__version__}"
65+
for request in captured
66+
)

0 commit comments

Comments
 (0)