net-sim: bundle the MCC/MNC list and hide MVNOs from the picker (v2.7.7)

The operator picker depended on a workspace file outside the repo
(<workspace>/samples/mcc-mnc-list.json), so a fresh clone showed
'Operator list not loaded'.

- bundle the list in the package (pysim_simple_server/data/
  mcc-mnc-list.json, byte-identical to pbakondy/mcc-mnc-list master
  commit 97bc1652, MIT) with license + provenance in
  mcc-mnc-list.LICENSE; declared as package data so wheels carry it
- _default_mcc_mnc_list() now points at the bundled file;
  --mcc-mnc-list still overrides it
- hide MVNO entries from the simulator picker (search + random): they
  do not operate their own network.  Detected via the `bands` field
  ('MVNO', 'Satellite MVNO', ...); the full list stays loaded so the
  Network state panel still resolves operator names
- tests/test_mcc_mnc.py: bundled file ships/parses, marker variants,
  search/random filters on fixtures and on the real list
- README/AGENTS updated; version trio 2.7.7; sw cache simple-v210
This commit is contained in:
2026-09-20 21:20:25 +03:00
parent a688c63919
commit 3c6bc7ac02
10 changed files with 37296 additions and 15 deletions
+15 -3
View File
@@ -25,7 +25,7 @@ from osmocom.construct import GsmOrUcs2Adapter
from osmocom.tlv import BER_TLV_IE
VERSION = '2.7.6'
VERSION = '2.7.7'
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE
@@ -461,6 +461,15 @@ def _mcc_mnc_load(path):
return data
def _mcc_mnc_is_mvno(e):
"""True for MVNO entries: `bands` marks them (e.g. 'MVNO', 'Satellite MVNO').
MVNOs do not operate their own radio network, so the network-simulation
picker hides them; the full list is kept for operator-name resolution.
"""
return 'mvno' in str(e.get('bands') or '').lower()
def _mcc_mnc_search(data, query, limit=50):
"""Compact substring search over country/brand/operator/MCC/MNC."""
q = (query or '').strip().lower()
@@ -468,6 +477,8 @@ def _mcc_mnc_search(data, query, limit=50):
return []
out = []
for e in data or []:
if _mcc_mnc_is_mvno(e):
continue
hay = ' '.join(str(e.get(k) or '') for k in
('countryName', 'countryCode', 'mcc', 'mnc', 'brand', 'operator')).lower()
if q not in hay:
@@ -482,9 +493,10 @@ def _mcc_mnc_search(data, query, limit=50):
def _mcc_mnc_random(data, exclude=None):
"""One random operator entry (optionally excluding 'mccmnc' digits)."""
pool = [e for e in (data or [])
if (str(e.get('mcc') or '') + str(e.get('mnc') or '')) != (exclude or '')]
if (str(e.get('mcc') or '') + str(e.get('mnc') or '')) != (exclude or '')
and not _mcc_mnc_is_mvno(e)]
if not pool:
pool = data or []
pool = [e for e in (data or []) if not _mcc_mnc_is_mvno(e)]
if not pool:
return None
e = random.choice(pool)