startup: cardless start is a normal state, not an init failure (v2.2.17)
- __main__ catches NoCardError from the startup init (fast and stock paths, and the stock fallback) and reports it with one line - 'INIT: no card in the reader - server ready; insert a card or press Equip' - without a traceback and without the stock-pysim fallback. Any other init failure still falls back with its traceback. - The cardless PysimApp construction no longer prints pySim's 'Waiting for card...' or pySim-shell's 'pySim-shell not equipped!': a new _LineFilter (server.py) drops whole matching lines while proxying encoding/isatty/ fileno (the cmd2 Rich console probes them on its stdout). - Removed the now-duplicate 'INIT: card not initialized' tail line and the cat_cla assignment when no card was detected (scc exists but is cardless). - Tests: tests/test_startup_cardless.py (line filter semantics incl. partial lines, one-line cardless report, no fallback call, real failures keep the fallback + traceback, muted PysimApp construction). Live-verified against an empty SCR 3310: two init lines, no traceback, API serves 2.2.17. - Docs: READMEs quick start, help EN+RU (Card reader), AGENTS (equip flow). SW cache otaman-v184.
This commit is contained in:
@@ -7,12 +7,13 @@ import traceback
|
||||
from http.server import HTTPServer
|
||||
from pySim.card_handler import CardHandler
|
||||
from pySim.commands import SimCardCommands
|
||||
from pySim.exceptions import NoCardError
|
||||
from pySim.log import PySimLogger
|
||||
from pySim.cards import UiccCardBase
|
||||
|
||||
from .shell import load_pysim_app
|
||||
from . import fastinit
|
||||
from .server import PysimHandler, StderrApduTracer, _LoggingApduTracer, VERSION, _send_terminal_profile, _DefaultProactiveHandler, _handle_proactive_chain, _send_status, _init_proactive_session, _timing_on, _tlog, _set_menu_timeout, start_card_monitor, set_auto_equip, _read_iccid
|
||||
from .server import PysimHandler, StderrApduTracer, _LoggingApduTracer, VERSION, _send_terminal_profile, _DefaultProactiveHandler, _handle_proactive_chain, _send_status, _init_proactive_session, _timing_on, _tlog, _set_menu_timeout, start_card_monitor, set_auto_equip, _read_iccid, _LineFilter
|
||||
|
||||
|
||||
_server_start = 0
|
||||
@@ -105,22 +106,49 @@ def main():
|
||||
if opts.fast_init:
|
||||
try:
|
||||
rs, card = fastinit.init_card_fast(sl, opts.skip_card_init, wait=True)
|
||||
except NoCardError:
|
||||
# Normal cardless start: there was no card in the reader (the
|
||||
# 3s wait timed out). The presence monitor auto-equips once a
|
||||
# card appears; nothing to recover, no traceback.
|
||||
sys.stderr.write('INIT: no card in the reader — server ready; insert a card or press Equip\n')
|
||||
except Exception:
|
||||
print("Warning: fast card initialization failed, falling back to pysim init:", file=sys.stderr)
|
||||
traceback.print_exc()
|
||||
rs, card = mod.init_card(sl, opts.skip_card_init)
|
||||
try:
|
||||
rs, card = mod.init_card(sl, opts.skip_card_init)
|
||||
except NoCardError:
|
||||
# The fallback retried the cardless wait; still a normal
|
||||
# cardless start, not an initialization failure.
|
||||
sys.stderr.write('INIT: no card in the reader — server ready; insert a card or press Equip\n')
|
||||
else:
|
||||
sl.wait_for_card(3)
|
||||
rs, card = mod.init_card(sl, opts.skip_card_init)
|
||||
try:
|
||||
sl.wait_for_card(3)
|
||||
rs, card = mod.init_card(sl, opts.skip_card_init)
|
||||
except NoCardError:
|
||||
sys.stderr.write('INIT: no card in the reader — server ready; insert a card or press Equip\n')
|
||||
_tlog('card_init: %.0fms' % ((time.time() - t_phase) * 1000))
|
||||
scc.cat_cla = '80' if isinstance(card, UiccCardBase) else 'a0'
|
||||
if card is not None:
|
||||
scc.cat_cla = '80' if isinstance(card, UiccCardBase) else 'a0'
|
||||
except Exception:
|
||||
print("Warning: reader/card initialization failed:", file=sys.stderr)
|
||||
traceback.print_exc()
|
||||
ch = CardHandler(sl) if sl else None
|
||||
t_phase = time.time()
|
||||
try:
|
||||
app = mod.PysimApp(verbose=opts.verbose, card=card, rs=rs, sl=sl, ch=ch)
|
||||
if card is not None:
|
||||
app = mod.PysimApp(verbose=opts.verbose, card=card, rs=rs, sl=sl, ch=ch)
|
||||
else:
|
||||
# Cardless start: pySim logs 'Waiting for card...' (its own retry
|
||||
# path) and pySim-shell prints 'pySim-shell not equipped!'; we
|
||||
# report both cases with our own single line above. A PysimApp
|
||||
# without a card would also retry the cardless wait, so install
|
||||
# the filter before constructing it and drop the two internals.
|
||||
saved_stdout = sys.stdout
|
||||
sys.stdout = _LineFilter(saved_stdout, ('Waiting for card...', 'pySim-shell not equipped!'))
|
||||
try:
|
||||
app = mod.PysimApp(verbose=opts.verbose, card=None, rs=None, sl=sl, ch=ch)
|
||||
finally:
|
||||
sys.stdout = saved_stdout
|
||||
except Exception:
|
||||
print("Warning: PysimApp creation failed:", file=sys.stderr)
|
||||
traceback.print_exc()
|
||||
@@ -153,8 +181,6 @@ def main():
|
||||
_tlog('terminal_profile_drain: %.0fms' % ((time.time() - t_phase) * 1000))
|
||||
except Exception:
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
elif scc is not None:
|
||||
sys.stderr.write('INIT: card not initialized — use Equip once the card is readable\n')
|
||||
if app is not None and opts.apdu_trace:
|
||||
# PysimApp.__init__ routes PySimLogger through app.poutput() (app.stdout)
|
||||
# and drops the root level to INFO. Re-route pysim's own APDU trace logging
|
||||
|
||||
@@ -21,7 +21,7 @@ from osmocom.construct import GsmOrUcs2Adapter
|
||||
from osmocom.tlv import BER_TLV_IE
|
||||
|
||||
|
||||
VERSION = '2.2.16'
|
||||
VERSION = '2.2.17'
|
||||
|
||||
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE
|
||||
|
||||
@@ -59,6 +59,51 @@ def _tlog(msg):
|
||||
sys.stderr.write('TIMING [+%7.3fs] %s\n' % (time.time() - _T0, msg))
|
||||
|
||||
|
||||
class _LineFilter:
|
||||
"""Text stream that drops whole lines matching any of the given substrings
|
||||
and forwards everything else to the wrapped stream. Used to mute pySim/
|
||||
pySim-shell internals we report ourselves (e.g. 'Waiting for card...' or
|
||||
'pySim-shell not equipped!'). Lines arrive through write(); the dropped
|
||||
lines are written as one call each by pySim's print/logger and by cmd2's
|
||||
Rich console, so a substring check per line is reliable here."""
|
||||
|
||||
def __init__(self, stream, patterns):
|
||||
self._stream = stream
|
||||
self._patterns = list(patterns)
|
||||
self._pending = ''
|
||||
|
||||
def write(self, text):
|
||||
text = self._pending + text
|
||||
self._pending = ''
|
||||
if not text:
|
||||
return
|
||||
if not text.endswith('\n'):
|
||||
# Keep a trailing partial line so a match is not missed when the
|
||||
# line is completed by the next write().
|
||||
nl = text.rfind('\n')
|
||||
if nl < 0:
|
||||
self._pending = text
|
||||
return
|
||||
self._pending = text[nl + 1:]
|
||||
text = text[:nl + 1]
|
||||
for line in text.splitlines(True):
|
||||
if not any(p in line for p in self._patterns):
|
||||
self._stream.write(line)
|
||||
self._stream.flush()
|
||||
|
||||
def flush(self):
|
||||
if self._pending:
|
||||
line, self._pending = self._pending, ''
|
||||
if not any(p in line for p in self._patterns):
|
||||
self._stream.write(line)
|
||||
self._stream.flush()
|
||||
|
||||
def __getattr__(self, name):
|
||||
# encoding/isatty/fileno: the Rich console probes these on the stream
|
||||
# (cmd2's self.stdout), so they must keep working.
|
||||
return getattr(self._stream, name)
|
||||
|
||||
|
||||
_APDU_TIMES = []
|
||||
_APDU_TIME_COLLECT = False
|
||||
|
||||
|
||||
Reference in New Issue
Block a user