Files
viandante/docs/development.md
T

5.0 KiB
Raw Blame History

Sviluppo

Setup locale, pytest, ruff, e come estendere il client.


Setup

git clone https://192.168.0.183:8085/truetype/progetto2.git
cd progetto2

python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"

Lo sviluppo quotidiano è Python puro; nessun build system esterno (setuptools è trasparente via pyproject.toml).


Test

pytest                              # 66+ test (telnet + ansi + deadsouls + mapper + logger)
pytest tests/test_logger.py -v      # solo il logger (più verboso)
pytest -k "test_rolling_parser"     # filtrato per nome
pytest --tb=short                   # traceback corti

I test usano pytest.ini_options = ["-q"] di default in pyproject.toml.

Cosa è coperto

File Copre
test_ansi.py stripper ANSI + visible_len
test_telnet.py IACParser (state machine completa) + TelnetConnection (mock asyncio)
test_deadsouls.py parse_exits + RollingRoomParser scenario multi‑chunk
test_mapper.py Mapper.visit, link, layout BFS, orphan placement, render
test_logger.py EventLogger round‑trip JSONL, MD rendering per ogni kind, redazione password, idempotenza close(), lone‑ESC safety

Cosa non è coperto

  • cli.py end‑to‑end (richiede telnet server; il vero test è una smoke run contro il docker locale di Dead Souls).
  • mapper._render_vertical_row (casi degeneri con celle non continue; non aggiunti perché non osservati in pratica).
  • Re‑entrata del logger in un worker thread (lock difensivo è documentato ma non testato).

Lint

ruff check src tests
ruff format src tests

Config in pyproject.toml:

[tool.ruff]
line-length = 100
target-version = "py310"

(Aggiungi regole strict select = ["E", "F", "I", "B", "UP"] se vuoi enforcement.)


Smoke test end‑to‑end

Dead Souls docker su localhost:6666:

# 2 secondi, log JSONL + MD
LOGJSON=$(mktemp /tmp/mud-json.XXXXXX.jsonl)
LOGMD=$(mktemp /tmp/mud-md.XXXXXX.md)
timeout 2 python -m progetto2 --host localhost --port 6666 \
    --log-json "$LOGJSON" --log-md "$LOGMD" </dev/null >/dev/null 2>&1 || true
wc -l "$LOGJSON" "$LOGMD"
head -3 "$LOGJSON"
head -8 "$LOGMD"

Aspettative: almeno 2‑3 righe JSONL (session_start, text, session_end) e un header MD con host/port/user.


Come aggiungere un modulo

Esempio: nuovo modulo directions.py che assorbe EXIT_ALIASES da deadsouls.py. Procedura:

  1. Crea src/progetto2/directions.py con le costanti e normalize_exits().
  2. Aggiorna deadsouls.py → re‑import da directions per back‑compat.
  3. Aggiorna gmcp.py, mapper.py, cli.py → import da directions.
  4. Scrivi tests/test_directions.py.
  5. Aggiorna questo file (docs/development.md) e docs/modules.md.
  6. pytest + ruff check.

Principi

  • Foglia pura sopra orchestratore. Solo cli.py può dipendere da più moduli. Mai il contrario.
  • No mutazione cross‑module. Ogni modulo possiede il suo stato.
  • Mai dipendere dal payload GMCP da mapper o deadsouls. Sono moduli di testo puro.
  • Mai import cli da moduli bassi. Solo cli sa di prompt_toolkit.

Debugging tipico

Sintomo Tool
IAC parser perso un byte pytest tests/test_telnet.py -v + aggiungere print in _byte()
GMCP module non riconosciuto leggere gmcp.parse_gmcp con data=b"..." in REPL
Stanze non rilevate senza GMCP stampare state.parser._buf dopo feed_text
Mapper collassa in [mappa troppo larga] abbassare max_cols/max_rows in render(...)
Stanza GMCP arriva ma render non aggiorna state.mapper.current_room punta ancora al vecchio?
Password finisce nel log grep --log-md per hunter2/*** ratio
Sessione hung dopo quit conn.close() chiamato? finally cleanup?

Aggiungere un nuovo comando slash

  1. In cli.py:_handle_local_command, aggiungi un ramo elif cmd == "foo":.
  2. Se modifica stato → metti la logica prima del if state.logger is not None: state.logger.local_cmd(...).
  3. Se inoltra al MUD → usa await conn.send_line("foo").
  4. Aggiungi voce nella tabella di docs/usage.md e _print_help().
  5. Test: aggiungi in tests/test_cli.py (futuro; ancora non esiste).

Versioning

__version__ in __init__.py (0.1.0 in v0.2). Bump:

  • patch (0.1.x): bug fix, no nuovi flag.
  • minor (0.x.0): nuovi flag, nuove feature senza break della CLI.
  • major (x.0.0): break di --log-* schema eventi o rename simboli pubblici dei moduli.

CI minima (Gitea Actions, opzionale)

Esempio .gitea/workflows/ci.yml:

name: CI
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.10"
      - run: python -m pip install -e ".[dev]"
      - run: pytest
      - run: ruff check src tests

Adatta ai vincoli del tuo runner Gitea (alcuni runner non hanno actions/setup-python@v5; pinning a versioni disponibili è tipico).