Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 138 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,144 @@
python-json-socket (jsocket)
============================

The final Python 2 package version is 1.6.1
Python 2 PyPi: https://pypi.org/project/jsocket/1.6.1/
Python 3 PyPi: https://pypi.org/project/jsocket/1.7/
[![CI](https://github.com/chris-piekarski/python-json-socket/actions/workflows/ci.yml/badge.svg)](https://github.com/chris-piekarski/python-json-socket/actions/workflows/ci.yml)
![PyPI](https://img.shields.io/pypi/v/jsocket.svg)
![Python Versions](https://img.shields.io/pypi/pyversions/jsocket.svg)
![License](https://img.shields.io/pypi/l/jsocket.svg)

Simple JSON-over-TCP sockets for Python. This library provides:

pip install jsocket
- JsonClient/JsonServer: length‑prefixed JSON message framing over TCP
- ThreadedServer: a single-connection server running in its own thread
- ServerFactory/ServerFactoryThread: a per‑connection worker model for multiple clients

Example:
It aims to be small, predictable, and easy to integrate in tests or small services.

See the examples_servers.py file for an example of how to use the threaded server factory feature of this module.
Can also upload the python_json_socket_3.ipynb notebook to Google colab

Unit Test:
Lettuce unit test exists under features directory
See that directories README for instructions on installing python3 lettuce fork
Install
-------

```
pip install jsocket
```

Requires Python 3.8+.


Quickstart
----------

Echo server with `ThreadedServer` and a client:

```python
import time
import jsocket

class Echo(jsocket.ThreadedServer):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.timeout = 2.0

# Return a dict to send a response back to the client
def _process_message(self, obj):
if isinstance(obj, dict) and 'echo' in obj:
return obj
return None

# Bind to an ephemeral port (port=0)
server = Echo(address='127.0.0.1', port=0)
_, port = server.socket.getsockname()
server.start()

client = jsocket.JsonClient(address='127.0.0.1', port=port)
assert client.connect() is True

payload = {"echo": "hello"}
client.send_obj(payload)
assert client.read_obj() == payload

client.close()
server.stop()
server.join()
```

Per‑connection workers with `ServerFactory`:

```python
import jsocket

class Worker(jsocket.ServerFactoryThread):
def __init__(self):
super().__init__()
self.timeout = 2.0

def _process_message(self, obj):
if isinstance(obj, dict) and 'message' in obj:
return {"reply": f"got: {obj['message']}"}

server = jsocket.ServerFactory(Worker, address='127.0.0.1', port=5489)
server.start()
# Connect one or more clients; one Worker is spawned per connection
```


API Highlights
--------------

- JsonClient:
- `connect()` returns True on success
- `send_obj(dict)` sends a JSON object
- `read_obj()` blocks until a full message is received; raises `socket.timeout` or `RuntimeError("socket connection broken")`
- `timeout` property controls socket timeouts

- ThreadedServer:
- Subclass and implement `_process_message(self, obj) -> Optional[dict]`
- Return a dict to send a response; return `None` to send nothing
- `start()`, `stop()`, `join()` manage the server thread
- `send_obj(dict)` sends to the currently connected client

- ServerFactory / ServerFactoryThread:
- `ServerFactoryThread` is a worker that handles one client connection
- `ServerFactory` accepts connections and spawns a worker per client


Examples and Tests
------------------

- Examples: see `examples/example_servers.py` and `scripts/smoke_test.py`
- Pytest: end-to-end and listener tests under `tests/`
- Run: `pytest -q`


Behavior-Driven Tests (Behave)
------------------------------

- Steps live under `features/steps/` and environment hooks in `features/environment.py`.
- To run Behave scenarios, add one or more `.feature` files under `features/` and run:
- `pip install -r requirements-dev.txt`
- `PYTHONPATH=. behave -f progress2`
- A minimal example feature:

```gherkin
Feature: Echo round-trip
Scenario: client/server echo
Given I start the server
And I connect the client
When the client sends the object {"echo": "hi"}
Then the client sees a message {"echo": "hi"}
```


Notes
-----

- Message framing uses a 4‑byte big‑endian length header followed by a JSON payload encoded as UTF‑8.
- On disconnect, reads raise `RuntimeError("socket connection broken")` so callers can distinguish cleanly from timeouts.
- Binding with `port=0` lets the OS choose an ephemeral port; find it with `server.socket.getsockname()`.


Links
-----

- PyPI: https://pypi.org/project/jsocket/
- License: see `LICENSE`
2 changes: 2 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[pytest]
pythonpath = .