Skip to content

Commit 2e9f9c8

Browse files
authored
perf(server): reuse HTTP session across requests (jxmorris12#203)
1 parent cf0cf4c commit 2e9f9c8

2 files changed

Lines changed: 19 additions & 8 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/2.0.0/),
1414
- Added `language_tool_python.config_file.ConfigValue` public type alias representing all accepted value types for `LanguageToolConfig`.
1515

1616
### Changed
17+
- HTTP requests to the LanguageTool server now reuse a persistent `requests.Session`, reducing TCP connection overhead for repeated calls to `.check()`.
1718
- **Breaking:** Dropped Python 3.9 support, now supports Python 3.10-3.15.
1819
- **Breaking:** Moved internal utilities to a private `_internals` subpackage:
1920
- `language_tool_python.safe_zip` -> `language_tool_python._internals.safe_zip`

src/language_tool_python/server.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,10 @@ class LanguageTool:
210210
_proxies: dict[str, str] | None
211211
"""A dictionary of proxies for network requests (used in requests to the server)."""
212212

213+
_session: requests.Session
214+
"""The HTTP session used for all requests, enabling connection reuse across
215+
calls."""
216+
213217
_premium_username: str | None
214218
"""The premium API username for the LanguageTool API."""
215219

@@ -255,6 +259,9 @@ def __init__( # noqa: PLR0913 # Too many arguments, but they are all necessary
255259
self._port = self._available_ports.pop()
256260
self._server = None
257261
self._proxies = proxies
262+
self._session = requests.Session()
263+
if proxies:
264+
self._session.proxies.update(proxies)
258265

259266
if remote_server and config is not None:
260267
err = "Cannot use both remote_server and config parameters."
@@ -363,10 +370,12 @@ def close(self) -> None:
363370
"""Close the server and perform necessary cleanup operations.
364371
365372
This method performs the following actions:
366-
1. Checks if the server is alive, not remote and terminates it if necessary.
367-
2. If new spellings are not set to persist and there are new spellings,
373+
1. Closes the HTTP session to release connection pool resources cleanly.
374+
2. Checks if the server is alive, not remote and terminates it if necessary.
375+
3. If new spellings are not set to persist and there are new spellings,
368376
it unregisters the spellings and clears the list of new spellings.
369377
"""
378+
self._session.close()
370379
if self._remote is not True and self._server_is_alive():
371380
self._terminate_server()
372381
if not self._new_spellings_persist and self._new_spellings:
@@ -439,7 +448,7 @@ def proxies(self, proxies: dict[str, str] | None) -> None:
439448
"""Set the proxies for server requests.
440449
441450
Proxies can only be used with remote servers. Local LanguageTool servers do not
442-
support proxy configuration.
451+
support proxy configuration. The underlying HTTP session is updated accordingly.
443452
444453
:param proxies: A dictionary of proxies (e.g., {'http': 'http://proxy:port'}),
445454
or None to unset.
@@ -453,6 +462,9 @@ def proxies(self, proxies: dict[str, str] | None) -> None:
453462
)
454463
raise ValueError(err)
455464
self._proxies = proxies
465+
self._session.proxies.clear()
466+
if proxies:
467+
self._session.proxies.update(proxies)
456468

457469
@property
458470
def disabled_rules(self) -> set[str]:
@@ -1030,18 +1042,16 @@ def _query_server(
10301042
for n in range(num_tries):
10311043
try:
10321044
if method == "post":
1033-
response_context = requests.post(
1045+
response_context = self._session.post(
10341046
url,
10351047
data=params,
10361048
timeout=self._TIMEOUT,
1037-
proxies=self._proxies,
10381049
)
10391050
else:
1040-
response_context = requests.get(
1051+
response_context = self._session.get(
10411052
url,
10421053
params=params,
10431054
timeout=self._TIMEOUT,
1044-
proxies=self._proxies,
10451055
)
10461056
with response_context as response:
10471057
try:
@@ -1182,7 +1192,7 @@ def _wait_for_server_ready(self, timeout: int = 15) -> None:
11821192

11831193
# Attempt to connect
11841194
with contextlib.suppress(requests.RequestException):
1185-
r = requests.get(url, timeout=2)
1195+
r = self._session.get(url, timeout=2)
11861196
if r.ok:
11871197
return
11881198

0 commit comments

Comments
 (0)