Skip to content

Commit da821c9

Browse files
committed
Address review comments
Signed-off-by: Casper Beyer <casper@synadia.com>
1 parent 44fb982 commit da821c9

File tree

4 files changed

+47
-36
lines changed

4 files changed

+47
-36
lines changed

.github/workflows/test.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,25 +52,25 @@ jobs:
5252
strategy:
5353
matrix:
5454
python-version: ["3.11", "3.12", "3.13"]
55-
os: ["ubuntu-latest", "macos-latest"]
55+
os: ["ubuntu-latest", "macos-latest", "windows-latest"]
5656
nats-server-version: ["latest"]
5757
project: ["nats-server"]
5858
steps:
5959
- name: Checkout repository
60-
uses: actions/checkout@v2
60+
uses: actions/checkout@v5
6161

6262
- name: Set up Go
63-
uses: actions/setup-go@v2
63+
uses: actions/setup-go@v6
6464
with:
65-
go-version: "1.23.4"
65+
go-version: "stable"
6666

6767
- name: Install NATS Server
6868
run: |
6969
go install github.com/nats-io/nats-server/v2@${{ matrix.nats-server-version }}
7070
shell: bash
7171

7272
- name: Set up Python ${{ matrix.python-version }}
73-
uses: actions/setup-python@v2
73+
uses: actions/setup-python@v6
7474
with:
7575
python-version: ${{ matrix.python-version }}
7676

nats-server/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Manage NATS server instances from python.
1212
pip install nats-server
1313
```
1414

15-
Requires Python 3.8+ and the NATS server binary in your PATH.
15+
Requires Python 3.11+ and the NATS server binary in your PATH.
1616

1717
## Usage
1818

@@ -26,6 +26,7 @@ async def main():
2626
print(f"Server running on {server.host}:{server.port}")
2727

2828
# Do something with the server...
29+
# e.g connect with server.client_url
2930

3031
# Clean shutdown
3132
await server.shutdown()

nats-server/src/nats/server/__init__.py

Lines changed: 26 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@
2828
logger = logging.getLogger(__name__)
2929

3030
# Regex patterns for parsing server output
31+
FATAL_PATTERN = re.compile(r"\[FTL\]\s+(.*)")
32+
INFO_PATTERN = re.compile(r"\[INF\]\s+(.*)")
3133
READY_PATTERN = re.compile(r"Server is ready")
3234
LISTENING_PATTERN = re.compile(
3335
r"Listening for client connections on ([^:]+):(\d+)"
3436
)
35-
FATAL_PATTERN = re.compile(r"\[FTL\]\s+(.*)")
36-
INFO_PATTERN = re.compile(r"INFO\s+({.*})")
3737

3838

3939
class ServerError(Exception):
@@ -66,7 +66,6 @@ def __init__(
6666
self._port = port
6767
self._jetstream = jetstream
6868
self._connect_urls = connect_urls or []
69-
self._is_running = True
7069

7170
@property
7271
def host(self) -> str:
@@ -190,41 +189,38 @@ async def run(
190189
)
191190

192191
async def wait_ready() -> tuple[str, int]:
193-
host = None
194-
port = None
192+
"""Clean version using early returns and better error handling"""
193+
host = port = None
195194
error_lines = []
196195

197-
while True:
198-
line = await process.stderr.readline()
199-
if not line:
200-
# EOF reached, process stderr for errors
201-
await process.wait()
196+
async for stderr_line in process.stderr:
197+
stderr_line = stderr_line.decode().strip()
202198

203-
# If the process exited with an error code and we have stderr content
204-
if process.returncode != 0:
205-
if error_lines:
206-
raise ServerError("\n".join(error_lines))
199+
if READY_PATTERN.search(stderr_line):
200+
return host, port
207201

208-
msg = f"Server exited with code {process.returncode}"
209-
raise ServerError(msg)
202+
if match := LISTENING_PATTERN.search(stderr_line):
203+
host, port = match.group(1), int(match.group(2))
210204

211-
line_str = line.decode()
212-
# Store all stderr lines for potential error reporting
213-
error_lines.append(line_str.strip())
205+
if INFO_PATTERN.search(stderr_line):
206+
continue
214207

215-
# Check for fatal errors
216-
if match := FATAL_PATTERN.search(line_str):
217-
raise ServerError(match.group(1))
208+
if match := FATAL_PATTERN.search(stderr_line):
209+
error_lines.append(match.group(1))
218210

219-
# Get host and port from listening message
220-
if match := LISTENING_PATTERN.search(line_str):
221-
host = match.group(1)
222-
port = int(match.group(2))
211+
error_lines.append(stderr_line)
223212

224-
# Check if server is ready
225-
if READY_PATTERN.search(line_str):
226-
error_lines.clear()
227-
return host, port
213+
# Process ended without becoming being ready
214+
returncode = await process.wait()
215+
216+
if returncode != 0:
217+
msg = f"Server exited with code {returncode}"
218+
if error_lines:
219+
msg += f"\nErrors:\n{''.join(error_lines)}"
220+
221+
raise ServerError(msg)
222+
223+
raise ServerError("Server ended without becoming ready")
228224

229225
assigned_host, assigned_port = await asyncio.wait_for(
230226
wait_ready(), timeout=timeout

nats-server/tests/test_server.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,3 +201,17 @@ async def test_run_with_jetstream_and_store_dir(tmp_path):
201201
finally:
202202
# Always shutdown the server
203203
await server.shutdown()
204+
205+
206+
async def test_run_with_store_dir_as_file(tmp_path):
207+
"""Test the run function fails when store_dir points to a file instead of a directory."""
208+
# Create a file instead of a directory
209+
store_file = tmp_path / "not_a_directory"
210+
store_file.write_text("this is a file, not a directory")
211+
212+
# Try to start server with JetStream using a file as store_dir
213+
with pytest.raises(ServerError) as exc_info:
214+
await run(port=0, jetstream=True, store_dir=str(store_file), timeout=2.0)
215+
216+
# Verify the error message indicates the storage directory issue
217+
assert "storage directory is not a directory" in str(exc_info.value).lower()

0 commit comments

Comments
 (0)