singleserver 0.1.0__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {singleserver-0.1.0 → singleserver-0.2.0}/.idea/workspace.xml +51 -35
- {singleserver-0.1.0 → singleserver-0.2.0}/PKG-INFO +1 -1
- {singleserver-0.1.0 → singleserver-0.2.0}/pyproject.toml +1 -1
- {singleserver-0.1.0 → singleserver-0.2.0}/src/singleserver/__init__.py +1 -1
- {singleserver-0.1.0 → singleserver-0.2.0}/src/singleserver/server.py +14 -5
- {singleserver-0.1.0 → singleserver-0.2.0}/tests/test_server.py +58 -0
- {singleserver-0.1.0 → singleserver-0.2.0}/.claude/settings.local.json +0 -0
- {singleserver-0.1.0 → singleserver-0.2.0}/.github/workflows/ci.yml +0 -0
- {singleserver-0.1.0 → singleserver-0.2.0}/.gitignore +0 -0
- {singleserver-0.1.0 → singleserver-0.2.0}/.idea/.gitignore +0 -0
- {singleserver-0.1.0 → singleserver-0.2.0}/.idea/git_toolbox_prj.xml +0 -0
- {singleserver-0.1.0 → singleserver-0.2.0}/.idea/inspectionProfiles/profiles_settings.xml +0 -0
- {singleserver-0.1.0 → singleserver-0.2.0}/.idea/misc.xml +0 -0
- {singleserver-0.1.0 → singleserver-0.2.0}/.idea/modules.xml +0 -0
- {singleserver-0.1.0 → singleserver-0.2.0}/.idea/singleserver.iml +0 -0
- {singleserver-0.1.0 → singleserver-0.2.0}/.idea/vcs.xml +0 -0
- {singleserver-0.1.0 → singleserver-0.2.0}/BOSTAD_INTEGRATION.md +0 -0
- {singleserver-0.1.0 → singleserver-0.2.0}/LICENSE +0 -0
- {singleserver-0.1.0 → singleserver-0.2.0}/README.md +0 -0
- {singleserver-0.1.0 → singleserver-0.2.0}/SINGLESERVER_LIBRARY_PLAN.md +0 -0
- {singleserver-0.1.0 → singleserver-0.2.0}/src/singleserver/client.py +0 -0
- {singleserver-0.1.0 → singleserver-0.2.0}/src/singleserver/lock.py +0 -0
- {singleserver-0.1.0 → singleserver-0.2.0}/src/singleserver/process.py +0 -0
- {singleserver-0.1.0 → singleserver-0.2.0}/tests/conftest.py +0 -0
- {singleserver-0.1.0 → singleserver-0.2.0}/tests/helpers.py +0 -0
- {singleserver-0.1.0 → singleserver-0.2.0}/tests/test_client.py +0 -0
- {singleserver-0.1.0 → singleserver-0.2.0}/tests/test_lock.py +0 -0
- {singleserver-0.1.0 → singleserver-0.2.0}/tests/test_process.py +0 -0
- {singleserver-0.1.0 → singleserver-0.2.0}/uv.lock +0 -0
|
@@ -4,14 +4,11 @@
|
|
|
4
4
|
<option name="autoReloadType" value="SELECTIVE" />
|
|
5
5
|
</component>
|
|
6
6
|
<component name="ChangeListManager">
|
|
7
|
-
<list default="true" id="d2f5ee2f-f4f5-4e60-a900-7af76aae6765" name="Changes" comment="
|
|
7
|
+
<list default="true" id="d2f5ee2f-f4f5-4e60-a900-7af76aae6765" name="Changes" comment="fix: add missing __init__.py">
|
|
8
8
|
<change beforePath="$PROJECT_DIR$/pyproject.toml" beforeDir="false" afterPath="$PROJECT_DIR$/pyproject.toml" afterDir="false" />
|
|
9
|
-
<change beforePath="$PROJECT_DIR$/src/singleserver/
|
|
10
|
-
<change beforePath="$PROJECT_DIR$/src/singleserver/lock.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/singleserver/lock.py" afterDir="false" />
|
|
11
|
-
<change beforePath="$PROJECT_DIR$/src/singleserver/process.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/singleserver/process.py" afterDir="false" />
|
|
9
|
+
<change beforePath="$PROJECT_DIR$/src/singleserver/__init__.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/singleserver/__init__.py" afterDir="false" />
|
|
12
10
|
<change beforePath="$PROJECT_DIR$/src/singleserver/server.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/singleserver/server.py" afterDir="false" />
|
|
13
11
|
<change beforePath="$PROJECT_DIR$/tests/test_server.py" beforeDir="false" afterPath="$PROJECT_DIR$/tests/test_server.py" afterDir="false" />
|
|
14
|
-
<change beforePath="$PROJECT_DIR$/uv.lock" beforeDir="false" afterPath="$PROJECT_DIR$/uv.lock" afterDir="false" />
|
|
15
12
|
</list>
|
|
16
13
|
<option name="SHOW_DIALOG" value="false" />
|
|
17
14
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
|
@@ -21,41 +18,41 @@
|
|
|
21
18
|
<component name="Git.Settings">
|
|
22
19
|
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
|
23
20
|
</component>
|
|
24
|
-
<component name="GitHubPullRequestSearchHistory"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
<component name="GitHubPullRequestSearchHistory">{
|
|
22
|
+
"lastFilter": {
|
|
23
|
+
"state": "OPEN",
|
|
24
|
+
"assignee": "CaptainDuck"
|
|
28
25
|
}
|
|
29
|
-
}
|
|
30
|
-
<component name="GithubPullRequestsUISettings"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
}</component>
|
|
27
|
+
<component name="GithubPullRequestsUISettings">{
|
|
28
|
+
"selectedUrlAndAccountId": {
|
|
29
|
+
"url": "https://github.com/Technology-Company/singleserver",
|
|
30
|
+
"accountId": "6034dab7-a252-4088-9244-0346eb837ae6"
|
|
34
31
|
}
|
|
35
|
-
}
|
|
36
|
-
<component name="ProjectColorInfo"
|
|
37
|
-
|
|
38
|
-
}
|
|
32
|
+
}</component>
|
|
33
|
+
<component name="ProjectColorInfo">{
|
|
34
|
+
"associatedIndex": 2
|
|
35
|
+
}</component>
|
|
39
36
|
<component name="ProjectId" id="3999htGGq4BCphGNFp4WkmpfpRs" />
|
|
40
37
|
<component name="ProjectViewState">
|
|
41
38
|
<option name="hideEmptyMiddlePackages" value="true" />
|
|
42
39
|
<option name="showLibraryContents" value="true" />
|
|
43
40
|
</component>
|
|
44
|
-
<component name="PropertiesComponent"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
41
|
+
<component name="PropertiesComponent">{
|
|
42
|
+
"keyToString": {
|
|
43
|
+
"RunOnceActivity.ShowReadmeOnStart": "true",
|
|
44
|
+
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
|
|
45
|
+
"RunOnceActivity.git.unshallow": "true",
|
|
46
|
+
"git-widget-placeholder": "main",
|
|
47
|
+
"last_opened_file_path": "/Users/johanna/Documents/GitHub/singleserver",
|
|
48
|
+
"node.js.detected.package.eslint": "true",
|
|
49
|
+
"node.js.detected.package.tslint": "true",
|
|
50
|
+
"node.js.selected.package.eslint": "(autodetect)",
|
|
51
|
+
"node.js.selected.package.tslint": "(autodetect)",
|
|
52
|
+
"nodejs_package_manager_path": "npm",
|
|
53
|
+
"vue.rearranger.settings.migration": "true"
|
|
57
54
|
}
|
|
58
|
-
}
|
|
55
|
+
}</component>
|
|
59
56
|
<component name="RecentsManager">
|
|
60
57
|
<key name="CopyFile.RECENT_KEYS">
|
|
61
58
|
<recent name="$PROJECT_DIR$" />
|
|
@@ -76,7 +73,8 @@
|
|
|
76
73
|
<option name="number" value="Default" />
|
|
77
74
|
<option name="presentableId" value="Default" />
|
|
78
75
|
<updated>1770097541742</updated>
|
|
79
|
-
<workItem from="1770097542784" duration="
|
|
76
|
+
<workItem from="1770097542784" duration="7499000" />
|
|
77
|
+
<workItem from="1770121674396" duration="2602000" />
|
|
80
78
|
</task>
|
|
81
79
|
<task id="LOCAL-00001" summary="feat: initial implementation of singleserver lib">
|
|
82
80
|
<option name="closed" value="true" />
|
|
@@ -86,7 +84,23 @@
|
|
|
86
84
|
<option name="project" value="LOCAL" />
|
|
87
85
|
<updated>1770101696339</updated>
|
|
88
86
|
</task>
|
|
89
|
-
<
|
|
87
|
+
<task id="LOCAL-00002" summary="fix: mypy type errors and add types-requests dependency">
|
|
88
|
+
<option name="closed" value="true" />
|
|
89
|
+
<created>1770103097453</created>
|
|
90
|
+
<option name="number" value="00002" />
|
|
91
|
+
<option name="presentableId" value="LOCAL-00002" />
|
|
92
|
+
<option name="project" value="LOCAL" />
|
|
93
|
+
<updated>1770103097453</updated>
|
|
94
|
+
</task>
|
|
95
|
+
<task id="LOCAL-00003" summary="fix: add missing __init__.py">
|
|
96
|
+
<option name="closed" value="true" />
|
|
97
|
+
<created>1770103235277</created>
|
|
98
|
+
<option name="number" value="00003" />
|
|
99
|
+
<option name="presentableId" value="LOCAL-00003" />
|
|
100
|
+
<option name="project" value="LOCAL" />
|
|
101
|
+
<updated>1770103235277</updated>
|
|
102
|
+
</task>
|
|
103
|
+
<option name="localTasksCounter" value="4" />
|
|
90
104
|
<servers />
|
|
91
105
|
</component>
|
|
92
106
|
<component name="TypeScriptGeneratedFilesManager">
|
|
@@ -94,7 +108,9 @@
|
|
|
94
108
|
</component>
|
|
95
109
|
<component name="VcsManagerConfiguration">
|
|
96
110
|
<MESSAGE value="feat: initial implementation of singleserver lib" />
|
|
97
|
-
<
|
|
111
|
+
<MESSAGE value="fix: mypy type errors and add types-requests dependency" />
|
|
112
|
+
<MESSAGE value="fix: add missing __init__.py" />
|
|
113
|
+
<option name="LAST_COMMIT_MESSAGE" value="fix: add missing __init__.py" />
|
|
98
114
|
</component>
|
|
99
115
|
<component name="XDebuggerManager">
|
|
100
116
|
<breakpoint-manager>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: singleserver
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Manage singleton server processes across multiple workers using atomic socket binding
|
|
5
5
|
Project-URL: Homepage, https://github.com/Technology-Company/singleserver
|
|
6
6
|
Project-URL: Documentation, https://github.com/Technology-Company/singleserver#readme
|
|
@@ -45,6 +45,7 @@ class SingleServer:
|
|
|
45
45
|
name: str,
|
|
46
46
|
command: list[str],
|
|
47
47
|
port: int | None = None,
|
|
48
|
+
host: str = "127.0.0.1",
|
|
48
49
|
socket: str | Path | None = None,
|
|
49
50
|
lock_port: int | None = None,
|
|
50
51
|
lock_socket: str | Path | None = None,
|
|
@@ -66,8 +67,9 @@ class SingleServer:
|
|
|
66
67
|
|
|
67
68
|
Args:
|
|
68
69
|
name: Identifier for logging/debugging.
|
|
69
|
-
command: Command to run. Can include {port}
|
|
70
|
+
command: Command to run. Can include {port}, {host}, {socket} placeholders.
|
|
70
71
|
port: TCP port the server will listen on.
|
|
72
|
+
host: IP address to bind to (default: 127.0.0.1 for localhost only).
|
|
71
73
|
socket: Unix socket path the server will listen on.
|
|
72
74
|
lock_port: Separate port for the coordination lock (defaults to port - 1).
|
|
73
75
|
lock_socket: Separate socket path for coordination lock.
|
|
@@ -90,6 +92,7 @@ class SingleServer:
|
|
|
90
92
|
self.name = name
|
|
91
93
|
self._command_template = command
|
|
92
94
|
self.port = port
|
|
95
|
+
self.host = host
|
|
93
96
|
self.socket_path = Path(socket) if socket else None
|
|
94
97
|
|
|
95
98
|
# Determine lock address (separate from server address)
|
|
@@ -132,6 +135,8 @@ class SingleServer:
|
|
|
132
135
|
for part in self._command_template:
|
|
133
136
|
if "{port}" in part:
|
|
134
137
|
part = part.replace("{port}", str(self.port))
|
|
138
|
+
if "{host}" in part:
|
|
139
|
+
part = part.replace("{host}", self.host)
|
|
135
140
|
if "{socket}" in part:
|
|
136
141
|
part = part.replace("{socket}", str(self.socket_path))
|
|
137
142
|
result.append(part)
|
|
@@ -153,7 +158,7 @@ class SingleServer:
|
|
|
153
158
|
def check() -> bool:
|
|
154
159
|
try:
|
|
155
160
|
client = ServerClient(
|
|
156
|
-
host=
|
|
161
|
+
host=self.host,
|
|
157
162
|
port=self.port,
|
|
158
163
|
socket_path=self.socket_path,
|
|
159
164
|
health_check_url=self.health_check_url,
|
|
@@ -271,7 +276,7 @@ class SingleServer:
|
|
|
271
276
|
|
|
272
277
|
# Create client
|
|
273
278
|
self._client = ServerClient(
|
|
274
|
-
host=
|
|
279
|
+
host=self.host,
|
|
275
280
|
port=self.port,
|
|
276
281
|
socket_path=self.socket_path,
|
|
277
282
|
health_check_url=self.health_check_url,
|
|
@@ -332,6 +337,7 @@ class ManagedServer:
|
|
|
332
337
|
name: str,
|
|
333
338
|
command: list[str],
|
|
334
339
|
port: int | None = None,
|
|
340
|
+
host: str = "127.0.0.1",
|
|
335
341
|
socket: str | Path | None = None,
|
|
336
342
|
**kwargs: Any,
|
|
337
343
|
):
|
|
@@ -343,6 +349,7 @@ class ManagedServer:
|
|
|
343
349
|
self.name = name
|
|
344
350
|
self._command_template = command
|
|
345
351
|
self.port = port
|
|
352
|
+
self.host = host
|
|
346
353
|
self.socket_path = Path(socket) if socket else None
|
|
347
354
|
self._kwargs = kwargs
|
|
348
355
|
self._owner: ProcessOwner | None = None
|
|
@@ -355,6 +362,8 @@ class ManagedServer:
|
|
|
355
362
|
for part in self._command_template:
|
|
356
363
|
if "{port}" in part:
|
|
357
364
|
part = part.replace("{port}", str(self.port))
|
|
365
|
+
if "{host}" in part:
|
|
366
|
+
part = part.replace("{host}", self.host)
|
|
358
367
|
if "{socket}" in part:
|
|
359
368
|
part = part.replace("{socket}", str(self.socket_path))
|
|
360
369
|
result.append(part)
|
|
@@ -371,7 +380,7 @@ class ManagedServer:
|
|
|
371
380
|
def check() -> bool:
|
|
372
381
|
try:
|
|
373
382
|
client = ServerClient(
|
|
374
|
-
host=
|
|
383
|
+
host=self.host,
|
|
375
384
|
port=self.port,
|
|
376
385
|
socket_path=self.socket_path,
|
|
377
386
|
health_check_url=health_check_url,
|
|
@@ -401,7 +410,7 @@ class ManagedServer:
|
|
|
401
410
|
self._owner.start()
|
|
402
411
|
|
|
403
412
|
self._client = ServerClient(
|
|
404
|
-
host=
|
|
413
|
+
host=self.host,
|
|
405
414
|
port=self.port,
|
|
406
415
|
socket_path=self.socket_path,
|
|
407
416
|
health_check_url=health_check_url,
|
|
@@ -75,6 +75,35 @@ class TestSingleServerBasic:
|
|
|
75
75
|
)
|
|
76
76
|
assert server.command == ["server", "-p", str(free_port)]
|
|
77
77
|
|
|
78
|
+
def test_default_host_is_localhost(self, free_port: int):
|
|
79
|
+
"""Test that default host is 127.0.0.1."""
|
|
80
|
+
server = SingleServer(
|
|
81
|
+
name="test",
|
|
82
|
+
command=["echo", "hello"],
|
|
83
|
+
port=free_port,
|
|
84
|
+
)
|
|
85
|
+
assert server.host == "127.0.0.1"
|
|
86
|
+
|
|
87
|
+
def test_custom_host(self, free_port: int):
|
|
88
|
+
"""Test that custom host can be set."""
|
|
89
|
+
server = SingleServer(
|
|
90
|
+
name="test",
|
|
91
|
+
command=["echo", "hello"],
|
|
92
|
+
port=free_port,
|
|
93
|
+
host="0.0.0.0",
|
|
94
|
+
)
|
|
95
|
+
assert server.host == "0.0.0.0"
|
|
96
|
+
|
|
97
|
+
def test_host_placeholder_replacement(self, free_port: int):
|
|
98
|
+
"""Test that {host} placeholder is replaced."""
|
|
99
|
+
server = SingleServer(
|
|
100
|
+
name="test",
|
|
101
|
+
command=["server", "-h", "{host}", "-p", "{port}"],
|
|
102
|
+
port=free_port,
|
|
103
|
+
host="0.0.0.0",
|
|
104
|
+
)
|
|
105
|
+
assert server.command == ["server", "-h", "0.0.0.0", "-p", str(free_port)]
|
|
106
|
+
|
|
78
107
|
|
|
79
108
|
class TestSingleServerConnect:
|
|
80
109
|
"""Tests for SingleServer.connect()."""
|
|
@@ -316,6 +345,35 @@ class TestManagedServer:
|
|
|
316
345
|
)
|
|
317
346
|
assert server.command == ["server", "--port", str(free_port)]
|
|
318
347
|
|
|
348
|
+
def test_default_host(self, free_port: int):
|
|
349
|
+
"""Test that default host is 127.0.0.1."""
|
|
350
|
+
server = ManagedServer(
|
|
351
|
+
name="test",
|
|
352
|
+
command=["echo"],
|
|
353
|
+
port=free_port,
|
|
354
|
+
)
|
|
355
|
+
assert server.host == "127.0.0.1"
|
|
356
|
+
|
|
357
|
+
def test_custom_host(self, free_port: int):
|
|
358
|
+
"""Test that custom host can be set."""
|
|
359
|
+
server = ManagedServer(
|
|
360
|
+
name="test",
|
|
361
|
+
command=["echo"],
|
|
362
|
+
port=free_port,
|
|
363
|
+
host="0.0.0.0",
|
|
364
|
+
)
|
|
365
|
+
assert server.host == "0.0.0.0"
|
|
366
|
+
|
|
367
|
+
def test_host_placeholder(self, free_port: int):
|
|
368
|
+
"""Test that {host} placeholder is replaced."""
|
|
369
|
+
server = ManagedServer(
|
|
370
|
+
name="test",
|
|
371
|
+
command=["server", "-h", "{host}", "-p", "{port}"],
|
|
372
|
+
port=free_port,
|
|
373
|
+
host="192.168.1.1",
|
|
374
|
+
)
|
|
375
|
+
assert server.command == ["server", "-h", "192.168.1.1", "-p", str(free_port)]
|
|
376
|
+
|
|
319
377
|
|
|
320
378
|
class TestSingleServerOutputRedirect:
|
|
321
379
|
"""Tests for output redirection."""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|