simple-socket-server 1.8__tar.gz → 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.
Files changed (23) hide show
  1. simple_socket_server-2.0/CHANGELOG.md +115 -0
  2. {simple-socket-server-1.8 → simple_socket_server-2.0}/LICENSE.md +1 -1
  3. simple_socket_server-2.0/MANIFEST.in +1 -0
  4. simple_socket_server-2.0/PKG-INFO +218 -0
  5. simple_socket_server-2.0/README.md +181 -0
  6. simple_socket_server-2.0/pyproject.toml +61 -0
  7. {simple-socket-server-1.8 → simple_socket_server-2.0}/setup.cfg +4 -4
  8. simple_socket_server-2.0/simple_socket_server/__init__.py +262 -0
  9. simple_socket_server-2.0/simple_socket_server/py.typed +0 -0
  10. simple_socket_server-2.0/simple_socket_server.egg-info/PKG-INFO +218 -0
  11. {simple-socket-server-1.8 → simple_socket_server-2.0}/simple_socket_server.egg-info/SOURCES.txt +5 -4
  12. {simple-socket-server-1.8 → simple_socket_server-2.0}/simple_socket_server.egg-info/requires.txt +3 -0
  13. simple_socket_server-2.0/tests/test_simple_socket_server.py +345 -0
  14. simple-socket-server-1.8/MANIFEST.in +0 -3
  15. simple-socket-server-1.8/PKG-INFO +0 -78
  16. simple-socket-server-1.8/README.md +0 -58
  17. simple-socket-server-1.8/requirements.txt +0 -1
  18. simple-socket-server-1.8/setup.py +0 -27
  19. simple-socket-server-1.8/simple_socket_server/__init__.py +0 -163
  20. simple-socket-server-1.8/simple_socket_server.egg-info/PKG-INFO +0 -78
  21. simple-socket-server-1.8/simple_socket_server.egg-info/not-zip-safe +0 -1
  22. {simple-socket-server-1.8 → simple_socket_server-2.0}/simple_socket_server.egg-info/dependency_links.txt +0 -0
  23. {simple-socket-server-1.8 → simple_socket_server-2.0}/simple_socket_server.egg-info/top_level.txt +0 -0
@@ -0,0 +1,115 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [2.0] - 2026-11-15
9
+
10
+ ### Added
11
+
12
+ - `SimpleSocketServer.stop()`: `run()` returns on request, closes every socket and
13
+ emits `stop(host, port)`. It only raises a flag, so it is safe to call from an
14
+ event handler or from another thread.
15
+ - `is_running`, `host` and `port` properties. A port of `0` is resolved by the
16
+ system, and the `start` event plus both properties report the real address.
17
+ - `simple_socket_server.__version__`, an explicit `__all__` and a public `Address`
18
+ alias for a peer address.
19
+ - `py.typed` marker (PEP 561) and type hints on every public name, so mypy and
20
+ pyright see real signatures instead of `Any`.
21
+ - `tests/`: pytest suite covering the lifecycle, event order, echo round trips,
22
+ graceful and abrupt disconnects, large messages, error handling and port reuse.
23
+ - GitHub Actions workflows: tests on Python 3.9-3.15 and 3.13t, plus a tag-driven
24
+ release pipeline publishing to PyPI and creating a GitHub Release.
25
+ - `CHANGELOG.md` and `.gitattributes` with `* text=auto eol=lf`.
26
+
27
+ ### Fixed
28
+
29
+ - A peer closing the connection cleanly (`recv()` returning `b""`) was ignored, so
30
+ the socket stayed in the `select()` set forever: the loop spun on an idle file
31
+ descriptor and `disconnect` never fired.
32
+ - `send()` raised `KeyError` for a socket that was not a client of the server; it
33
+ raises `ValueError` now, and an empty message is a no-op.
34
+ - `send()` dropped data on a non-blocking socket: a single `send()` call can accept
35
+ only part of the buffer. The unwritten tail is buffered and retried, so a message
36
+ larger than the socket buffer arrives in full.
37
+ - Closed sockets were never removed from the client table, so a long-lived server
38
+ leaked memory with every reconnection.
39
+ - The `disconnect` event could be emitted twice for one socket, and a socket already
40
+ dropped earlier in the same poll was used again.
41
+ - The state reset on the server-socket error path left the previous listening
42
+ socket open, and `run()` reused a stale initialized flag.
43
+ - `SimpleSocketServerException` was raised with two positional arguments on a check
44
+ that could not trigger; bind and listen failures are reported now with the address.
45
+ - A `select()` failure is reported through the `error` event and stops the server
46
+ instead of escaping `run()` as an unhandled exception.
47
+
48
+ ### Changed
49
+
50
+ - Packaging moved from `setup.py` to `pyproject.toml` (PEP 621, PEP 639);
51
+ metadata, build configuration and pytest settings live in one file.
52
+ - Errors that end a connection are reported through the `error` event and followed
53
+ by `disconnect`; `message` is never emitted for a reset connection.
54
+ - `run()` no longer sleeps after `select()`: the poll interval is the `select()`
55
+ timeout alone, so the latency of a response is halved.
56
+ - The listening socket no longer tries to restart itself after an error; a broken
57
+ listening socket stops the server with an `error` event.
58
+ - `stop` is emitted when `run()` returns, after every socket is closed.
59
+ - Documentation: README rewritten with the event table, the API table, the
60
+ framing and threading caveats, compatibility and development instructions.
61
+ - Metadata: the `HTTP Servers` topic is replaced by `System :: Networking` and
62
+ `Software Development :: Libraries`, `Requires-Python` is declared, and project
63
+ URLs point to the changelog and issue tracker.
64
+
65
+ ### Removed
66
+
67
+ - Python 2 leftovers: the `coding=utf8` header and the empty `__main__` block.
68
+ - `setup.py` and `requirements.txt`: dependencies are declared in `pyproject.toml`.
69
+ - The debug `print()` call on the server-socket error path.
70
+
71
+ ### Breaking
72
+
73
+ - The `SimpleSocketServer` class is no longer a singleton: every instance is an
74
+ independent server, so two servers can run in one process.
75
+ - Python 3.9 or newer is required.
76
+ - `send()` raises `ValueError` instead of `KeyError` for an unknown socket.
77
+ - `SimpleSocketServerException` wraps the underlying `OSError` (`raise ... from`),
78
+ so `except OSError` keeps working but the arguments differ.
79
+
80
+ ## [1.8] - 2023-02-10
81
+
82
+ - Fixed getting the peer address after a disconnect.
83
+
84
+ ## [1.7] - 2023-02-06
85
+
86
+ - `EventBus.__init__` is called from the server constructor.
87
+
88
+ ## [1.6] - 2023-02-06
89
+
90
+ - Replaced the event emitter with the `event-bus` library.
91
+
92
+ ## [1.5] - 2023-02-05
93
+
94
+ - Replaced the bundled event emitter.
95
+
96
+ ## [1.4] - 2023-02-04
97
+
98
+ - `BrokenPipeError` on send is reported instead of escaping `run()`.
99
+
100
+ ## [1.3] - 2023-02-03
101
+
102
+ - `ConnectionResetError` ends the connection instead of raising from the handler.
103
+
104
+ ## [1.2] - 2023-02-02
105
+
106
+ - Removed the Flask integration.
107
+
108
+ ## [1.1] - 2023-02-01
109
+
110
+ - `start` event with the listening address.
111
+
112
+ ## [1.0] - 2023-01-24
113
+
114
+ - First published release: `select()`-based TCP server with `connect`, `message`
115
+ and `disconnect` events.
@@ -1,4 +1,4 @@
1
- Copyright (c) 2023, Alexey Kuznetsov
1
+ Copyright (c) 2023–2026, webtoucher
2
2
 
3
3
  All rights reserved.
4
4
 
@@ -0,0 +1 @@
1
+ include CHANGELOG.md
@@ -0,0 +1,218 @@
1
+ Metadata-Version: 2.4
2
+ Name: simple-socket-server
3
+ Version: 2.0
4
+ Summary: Simple TCP socket server with select
5
+ Author-email: webtoucher <webtoucher@ya.ru>
6
+ License-Expression: BSD-3-Clause
7
+ Project-URL: Homepage, https://github.com/webtoucher/simple-socket-server
8
+ Project-URL: Repository, https://github.com/webtoucher/simple-socket-server
9
+ Project-URL: Issues, https://github.com/webtoucher/simple-socket-server/issues
10
+ Project-URL: Changelog, https://github.com/webtoucher/simple-socket-server/blob/master/CHANGELOG.md
11
+ Keywords: socket,tcp,server,select,networking,event-driven
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3 :: Only
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Classifier: Programming Language :: Python :: 3.15
23
+ Classifier: Topic :: Software Development :: Libraries
24
+ Classifier: Topic :: System :: Networking
25
+ Classifier: Operating System :: Microsoft :: Windows
26
+ Classifier: Operating System :: POSIX
27
+ Classifier: Operating System :: Unix
28
+ Classifier: Operating System :: MacOS
29
+ Classifier: Typing :: Typed
30
+ Requires-Python: >=3.9
31
+ Description-Content-Type: text/markdown
32
+ License-File: LICENSE.md
33
+ Requires-Dist: event-bus~=1.0.2
34
+ Provides-Extra: dev
35
+ Requires-Dist: pytest>=7; extra == "dev"
36
+ Dynamic: license-file
37
+
38
+ # Simple TCP socket server with select
39
+
40
+ ![License](https://img.shields.io/badge/License-BSD%203--Clause-green)
41
+ [![Downloads](https://img.shields.io/pypi/dm/simple-socket-server.svg?color=orange)](https://pypi.python.org/pypi/simple-socket-server)
42
+ [![Latest Version](https://img.shields.io/pypi/v/simple-socket-server.svg)](https://pypi.python.org/pypi/simple-socket-server)
43
+ [![Supported Python versions](https://img.shields.io/pypi/pyversions/simple-socket-server.svg)](https://pypi.python.org/pypi/simple-socket-server)
44
+ [![tests](https://github.com/webtoucher/simple-socket-server/actions/workflows/tests.yml/badge.svg)](https://github.com/webtoucher/simple-socket-server/actions/workflows/tests.yml)
45
+
46
+ A TCP server on a single `select()` loop with no networking framework underneath:
47
+ you subscribe handlers to events and the server calls them. Type hints are shipped
48
+ with the package, the only runtime dependency is the event bus that dispatches
49
+ those events.
50
+
51
+ ## Installation
52
+
53
+ Install it with pip:
54
+
55
+ ```shell
56
+ $ pip install simple-socket-server
57
+ ```
58
+
59
+ Or you can add it as dependency in requirements.txt file of your python application:
60
+
61
+ ```
62
+ simple-socket-server~=2.0
63
+ ```
64
+
65
+ ## Usage
66
+
67
+ Easy way to understand how it works is testing socket server via telnet terminal:
68
+
69
+ ```python
70
+ from socket import socket
71
+ from simple_socket_server import SimpleSocketServer
72
+
73
+ socket_server = SimpleSocketServer()
74
+
75
+
76
+ @socket_server.on('start')
77
+ def on_start(host, port):
78
+ print('Listening on %s:%s' % (host, port))
79
+
80
+
81
+ @socket_server.on('connect')
82
+ def on_connect(sock: socket, peer):
83
+ print('New connection from %s:%s' % peer)
84
+ socket_server.send(sock, b'What is your name?\r\n')
85
+
86
+
87
+ @socket_server.on('disconnect')
88
+ def on_disconnect(_sock, peer):
89
+ print('Connection from %s:%s is closed' % peer)
90
+
91
+
92
+ @socket_server.on('message')
93
+ def on_message(sock: socket, peer, message: bytes):
94
+ print('Incoming data from %s:%s' % peer, message)
95
+ socket_server.send(sock, b'Hi, ' + message)
96
+
97
+
98
+ socket_server.run(host='0.0.0.0', port=5000)
99
+ ```
100
+
101
+ Then you can connect to server:
102
+
103
+ ```shell
104
+ telnet 127.0.0.1 5000
105
+ ```
106
+
107
+ `run()` blocks, so the server stops when someone asks it to. That may be a signal
108
+ handler, a timer or an event handler:
109
+
110
+ ```python
111
+ @another_server.on('message')
112
+ def on_message(sock, peer, message):
113
+ if message.strip() == b'quit':
114
+ another_server.stop()
115
+ ```
116
+
117
+ A port of `0` lets the operating system pick a free port; the real address comes
118
+ back in the `start` event and in the `host` and `port` properties:
119
+
120
+ ```python
121
+ socket_server.run(host='127.0.0.1', port=0)
122
+ # ... inside the start handler:
123
+ print(socket_server.host, socket_server.port)
124
+ ```
125
+
126
+ ## Events
127
+
128
+ Handlers are called from the thread that runs `run()`, one at a time.
129
+
130
+ | Event | Arguments | When it fires |
131
+ |--------------|-------------------------|------------------------------------------------------------------|
132
+ | `start` | `host, port` | the listening socket is ready |
133
+ | `connect` | `sock, peer` | a client has been accepted |
134
+ | `message` | `sock, peer, data` | a chunk of bytes has been received |
135
+ | `disconnect` | `sock, peer` | the connection is gone; the socket is closed right after |
136
+ | `error` | `sock, peer, error` | a socket error occurred; `disconnect` follows |
137
+ | `stop` | `host, port` | `run()` has returned and every socket is closed |
138
+
139
+ `peer` is an `(host, port)` tuple, `data` is `bytes`, `error` is an `OSError`.
140
+
141
+ ## API
142
+
143
+ | Member | Description |
144
+ |---------------------------------------|----------------------------------------------------------------------|
145
+ | `run(host='0.0.0.0', port=6666, max_conn=5)` | serve connections until `stop()` is called |
146
+ | `stop()` | ask `run()` to return; safe from a handler or another thread |
147
+ | `send(sock, message)` | queue `bytes` for a client; `ValueError` if it is not a client |
148
+ | `is_running` | `True` while `run()` is serving connections |
149
+ | `host`, `port` | the address the listening socket is bound to |
150
+ | `on(event)`, `emit(...)` | inherited from `event_bus.EventBus` |
151
+
152
+ Notes:
153
+
154
+ - The server is single-threaded and handlers are called synchronously, so a slow
155
+ handler delays every other connection. Hand off long work to your own thread.
156
+ - `message` carries one raw `recv()` chunk of up to 1024 bytes. There is no
157
+ framing: the protocol you put on top has to delimit its own messages.
158
+ - `send()` never blocks and never truncates: because client sockets are
159
+ non-blocking, the bytes are buffered and the unwritten tail is retried on the
160
+ next poll, so a message larger than the socket buffer arrives in full.
161
+ - Do not use the socket after `disconnect` or `error`: it is closed.
162
+ - An exception raised by a handler propagates out of `run()`. The socket state is
163
+ still cleaned up before the call returns.
164
+
165
+ ## Compatibility
166
+
167
+ - Python **3.9 and newer**, including free-threaded builds: CI runs every version
168
+ from 3.9 to 3.15 plus the free-threaded `3.13t`.
169
+ - One runtime dependency: [`event-bus`](https://pypi.org/project/event-bus/).
170
+ - Type hints are shipped with the package (`py.typed`, PEP 561), so mypy and
171
+ pyright see real signatures instead of `Any`.
172
+ - Any server instance is independent: several servers may run in one process, each
173
+ in its own thread.
174
+
175
+ ## Development
176
+
177
+ ```shell
178
+ $ python -m venv .venv
179
+ $ .venv\Scripts\activate # Windows; on POSIX: source .venv/bin/activate
180
+ $ pip install -e ".[dev]" # installs the package and pytest
181
+ $ python -m pytest
182
+ ```
183
+
184
+ The test suite covers:
185
+
186
+ - the lifecycle: `start`, `stop`, `is_running`, socket cleanup and port reuse,
187
+ - the message flow: `connect`, echo round trips, ordering and large payloads,
188
+ - disconnects: graceful close by the peer, an abrupt reset and a send to a
189
+ connection that is already gone,
190
+ - error handling: an unknown socket for `send()` and a port that is already taken,
191
+ - independence of two servers running in one process.
192
+
193
+ Project layout:
194
+
195
+ ```
196
+ simple_socket_server/__init__.py the whole library
197
+ tests/ test suite
198
+ pyproject.toml metadata, build config and pytest settings
199
+ .github/workflows/ CI: tests on Python 3.9-3.15 and 3.13t, release pipeline
200
+ ```
201
+
202
+ To build and verify a release:
203
+
204
+ ```shell
205
+ $ pip install build twine
206
+ $ python -m build
207
+ $ python -m twine check dist/*
208
+ ```
209
+
210
+ ## Changelog
211
+
212
+ See [CHANGELOG.md](CHANGELOG.md). GitHub Releases are generated from the matching
213
+ entry of that file, so the history is available both in git and on the releases
214
+ page.
215
+
216
+ ## License
217
+
218
+ Distributed under the BSD 3-Clause license. See [LICENSE.md](LICENSE.md) for details.
@@ -0,0 +1,181 @@
1
+ # Simple TCP socket server with select
2
+
3
+ ![License](https://img.shields.io/badge/License-BSD%203--Clause-green)
4
+ [![Downloads](https://img.shields.io/pypi/dm/simple-socket-server.svg?color=orange)](https://pypi.python.org/pypi/simple-socket-server)
5
+ [![Latest Version](https://img.shields.io/pypi/v/simple-socket-server.svg)](https://pypi.python.org/pypi/simple-socket-server)
6
+ [![Supported Python versions](https://img.shields.io/pypi/pyversions/simple-socket-server.svg)](https://pypi.python.org/pypi/simple-socket-server)
7
+ [![tests](https://github.com/webtoucher/simple-socket-server/actions/workflows/tests.yml/badge.svg)](https://github.com/webtoucher/simple-socket-server/actions/workflows/tests.yml)
8
+
9
+ A TCP server on a single `select()` loop with no networking framework underneath:
10
+ you subscribe handlers to events and the server calls them. Type hints are shipped
11
+ with the package, the only runtime dependency is the event bus that dispatches
12
+ those events.
13
+
14
+ ## Installation
15
+
16
+ Install it with pip:
17
+
18
+ ```shell
19
+ $ pip install simple-socket-server
20
+ ```
21
+
22
+ Or you can add it as dependency in requirements.txt file of your python application:
23
+
24
+ ```
25
+ simple-socket-server~=2.0
26
+ ```
27
+
28
+ ## Usage
29
+
30
+ Easy way to understand how it works is testing socket server via telnet terminal:
31
+
32
+ ```python
33
+ from socket import socket
34
+ from simple_socket_server import SimpleSocketServer
35
+
36
+ socket_server = SimpleSocketServer()
37
+
38
+
39
+ @socket_server.on('start')
40
+ def on_start(host, port):
41
+ print('Listening on %s:%s' % (host, port))
42
+
43
+
44
+ @socket_server.on('connect')
45
+ def on_connect(sock: socket, peer):
46
+ print('New connection from %s:%s' % peer)
47
+ socket_server.send(sock, b'What is your name?\r\n')
48
+
49
+
50
+ @socket_server.on('disconnect')
51
+ def on_disconnect(_sock, peer):
52
+ print('Connection from %s:%s is closed' % peer)
53
+
54
+
55
+ @socket_server.on('message')
56
+ def on_message(sock: socket, peer, message: bytes):
57
+ print('Incoming data from %s:%s' % peer, message)
58
+ socket_server.send(sock, b'Hi, ' + message)
59
+
60
+
61
+ socket_server.run(host='0.0.0.0', port=5000)
62
+ ```
63
+
64
+ Then you can connect to server:
65
+
66
+ ```shell
67
+ telnet 127.0.0.1 5000
68
+ ```
69
+
70
+ `run()` blocks, so the server stops when someone asks it to. That may be a signal
71
+ handler, a timer or an event handler:
72
+
73
+ ```python
74
+ @another_server.on('message')
75
+ def on_message(sock, peer, message):
76
+ if message.strip() == b'quit':
77
+ another_server.stop()
78
+ ```
79
+
80
+ A port of `0` lets the operating system pick a free port; the real address comes
81
+ back in the `start` event and in the `host` and `port` properties:
82
+
83
+ ```python
84
+ socket_server.run(host='127.0.0.1', port=0)
85
+ # ... inside the start handler:
86
+ print(socket_server.host, socket_server.port)
87
+ ```
88
+
89
+ ## Events
90
+
91
+ Handlers are called from the thread that runs `run()`, one at a time.
92
+
93
+ | Event | Arguments | When it fires |
94
+ |--------------|-------------------------|------------------------------------------------------------------|
95
+ | `start` | `host, port` | the listening socket is ready |
96
+ | `connect` | `sock, peer` | a client has been accepted |
97
+ | `message` | `sock, peer, data` | a chunk of bytes has been received |
98
+ | `disconnect` | `sock, peer` | the connection is gone; the socket is closed right after |
99
+ | `error` | `sock, peer, error` | a socket error occurred; `disconnect` follows |
100
+ | `stop` | `host, port` | `run()` has returned and every socket is closed |
101
+
102
+ `peer` is an `(host, port)` tuple, `data` is `bytes`, `error` is an `OSError`.
103
+
104
+ ## API
105
+
106
+ | Member | Description |
107
+ |---------------------------------------|----------------------------------------------------------------------|
108
+ | `run(host='0.0.0.0', port=6666, max_conn=5)` | serve connections until `stop()` is called |
109
+ | `stop()` | ask `run()` to return; safe from a handler or another thread |
110
+ | `send(sock, message)` | queue `bytes` for a client; `ValueError` if it is not a client |
111
+ | `is_running` | `True` while `run()` is serving connections |
112
+ | `host`, `port` | the address the listening socket is bound to |
113
+ | `on(event)`, `emit(...)` | inherited from `event_bus.EventBus` |
114
+
115
+ Notes:
116
+
117
+ - The server is single-threaded and handlers are called synchronously, so a slow
118
+ handler delays every other connection. Hand off long work to your own thread.
119
+ - `message` carries one raw `recv()` chunk of up to 1024 bytes. There is no
120
+ framing: the protocol you put on top has to delimit its own messages.
121
+ - `send()` never blocks and never truncates: because client sockets are
122
+ non-blocking, the bytes are buffered and the unwritten tail is retried on the
123
+ next poll, so a message larger than the socket buffer arrives in full.
124
+ - Do not use the socket after `disconnect` or `error`: it is closed.
125
+ - An exception raised by a handler propagates out of `run()`. The socket state is
126
+ still cleaned up before the call returns.
127
+
128
+ ## Compatibility
129
+
130
+ - Python **3.9 and newer**, including free-threaded builds: CI runs every version
131
+ from 3.9 to 3.15 plus the free-threaded `3.13t`.
132
+ - One runtime dependency: [`event-bus`](https://pypi.org/project/event-bus/).
133
+ - Type hints are shipped with the package (`py.typed`, PEP 561), so mypy and
134
+ pyright see real signatures instead of `Any`.
135
+ - Any server instance is independent: several servers may run in one process, each
136
+ in its own thread.
137
+
138
+ ## Development
139
+
140
+ ```shell
141
+ $ python -m venv .venv
142
+ $ .venv\Scripts\activate # Windows; on POSIX: source .venv/bin/activate
143
+ $ pip install -e ".[dev]" # installs the package and pytest
144
+ $ python -m pytest
145
+ ```
146
+
147
+ The test suite covers:
148
+
149
+ - the lifecycle: `start`, `stop`, `is_running`, socket cleanup and port reuse,
150
+ - the message flow: `connect`, echo round trips, ordering and large payloads,
151
+ - disconnects: graceful close by the peer, an abrupt reset and a send to a
152
+ connection that is already gone,
153
+ - error handling: an unknown socket for `send()` and a port that is already taken,
154
+ - independence of two servers running in one process.
155
+
156
+ Project layout:
157
+
158
+ ```
159
+ simple_socket_server/__init__.py the whole library
160
+ tests/ test suite
161
+ pyproject.toml metadata, build config and pytest settings
162
+ .github/workflows/ CI: tests on Python 3.9-3.15 and 3.13t, release pipeline
163
+ ```
164
+
165
+ To build and verify a release:
166
+
167
+ ```shell
168
+ $ pip install build twine
169
+ $ python -m build
170
+ $ python -m twine check dist/*
171
+ ```
172
+
173
+ ## Changelog
174
+
175
+ See [CHANGELOG.md](CHANGELOG.md). GitHub Releases are generated from the matching
176
+ entry of that file, so the history is available both in git and on the releases
177
+ page.
178
+
179
+ ## License
180
+
181
+ Distributed under the BSD 3-Clause license. See [LICENSE.md](LICENSE.md) for details.
@@ -0,0 +1,61 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "simple-socket-server"
7
+ description = "Simple TCP socket server with select"
8
+ readme = "README.md"
9
+ requires-python = ">=3.9"
10
+ license = "BSD-3-Clause"
11
+ license-files = ["LICENSE.md"]
12
+ authors = [
13
+ { name = "webtoucher", email = "webtoucher@ya.ru" },
14
+ ]
15
+ keywords = ["socket", "tcp", "server", "select", "networking", "event-driven"]
16
+ classifiers = [
17
+ "Development Status :: 5 - Production/Stable",
18
+ "Intended Audience :: Developers",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3 :: Only",
21
+ "Programming Language :: Python :: 3.9",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Programming Language :: Python :: 3.13",
26
+ "Programming Language :: Python :: 3.14",
27
+ "Programming Language :: Python :: 3.15",
28
+ "Topic :: Software Development :: Libraries",
29
+ "Topic :: System :: Networking",
30
+ "Operating System :: Microsoft :: Windows",
31
+ "Operating System :: POSIX",
32
+ "Operating System :: Unix",
33
+ "Operating System :: MacOS",
34
+ "Typing :: Typed",
35
+ ]
36
+ dynamic = ["version"]
37
+ dependencies = [
38
+ "event-bus~=1.0.2",
39
+ ]
40
+
41
+ [project.optional-dependencies]
42
+ dev = ["pytest>=7"]
43
+
44
+ [project.urls]
45
+ Homepage = "https://github.com/webtoucher/simple-socket-server"
46
+ Repository = "https://github.com/webtoucher/simple-socket-server"
47
+ Issues = "https://github.com/webtoucher/simple-socket-server/issues"
48
+ Changelog = "https://github.com/webtoucher/simple-socket-server/blob/master/CHANGELOG.md"
49
+
50
+ [tool.setuptools]
51
+ packages = ["simple_socket_server"]
52
+
53
+ [tool.setuptools.dynamic]
54
+ version = { attr = "simple_socket_server.__version__" }
55
+
56
+ [tool.setuptools.package-data]
57
+ simple_socket_server = ["py.typed"]
58
+
59
+ [tool.pytest.ini_options]
60
+ testpaths = ["tests"]
61
+ addopts = "-ra"
@@ -1,4 +1,4 @@
1
- [egg_info]
2
- tag_build =
3
- tag_date = 0
4
-
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+