dexter has uploaded this change for review. ( https://gerrit.osmocom.org/c/pysim/+/41229?usp=email )
Change subject: pySim-shell: set callback function to print formatted warnings ......................................................................
pySim-shell: set callback function to print formatted warnings
In many sub modules we still use print() to occassionally print status messages or warnings. This technically does not hurt, but it is an unclean solution which we should replace with something more mature.
The python provided warnings module provides a warn() function that can be used to send warnings to higher layers. The higher layers can receive the warnings via a callback and then decide what to do with it. In our application we will format and print the warnings using the cmd2 provided functions (or print in case the cmd2 object does not exist yet.)
Let's also add a custom warning class "Info", which we can use to print informative messages
To illustrate how the approach can be used in sub-modules, this also replaces the print() calls in runtimpe.py with warn() calls.
Related: OS#6864 Change-Id: I187f117e7e1ccdb2a85dfdfb18e84bd7561704eb --- M pySim-shell.py M pySim/runtime.py A pySim/warnings.py 3 files changed, 56 insertions(+), 6 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/29/41229/1
diff --git a/pySim-shell.py b/pySim-shell.py index 977666c..6b81628 100755 --- a/pySim-shell.py +++ b/pySim-shell.py @@ -24,17 +24,20 @@ import re
import cmd2 +import warnings from packaging import version from cmd2 import style # cmd2 >= 2.3.0 has deprecated the bg/fg in favor of Bg/Fg :( if version.parse(cmd2.__version__) < version.parse("2.3.0"): from cmd2 import fg, bg # pylint: disable=no-name-in-module RED = fg.red + YELLOW = fg.yellow LIGHT_RED = fg.bright_red LIGHT_GREEN = fg.bright_green else: from cmd2 import Fg, Bg # pylint: disable=no-name-in-module RED = Fg.RED + YELLOW = Fg.YELLOW LIGHT_RED = Fg.LIGHT_RED LIGHT_GREEN = Fg.LIGHT_GREEN from cmd2 import CommandSet, with_default_category, with_argparser @@ -67,6 +70,15 @@
from pySim.app import init_card
+def __format_warning(message, category, filename, lineno, debug=False): + if category.__name__ == "Info": + color = None + else: + color = YELLOW + if debug == True: + return style("%s: %s, in file: %s:%s" % (category.__name__, message, filename, lineno), fg=color) + else: + return style("%s: %s" % (category.__name__, message), fg=color)
class Cmd2Compat(cmd2.Cmd): """Backwards-compatibility wrapper around cmd2.Cmd to support older and newer @@ -92,6 +104,9 @@ (C) 2021-2023 by Harald Welte, sysmocom - s.f.m.c. GmbH and contributors Online manual available at https://downloads.osmocom.org/docs/pysim/master/html/shell.html """
+ def __print_warning(self, message, category, filename, lineno, file=None, line=None): + self.poutput(__format_warning(message, category, filename, lineno, self.debug)) + def __init__(self, card, rs, sl, ch, script=None): if version.parse(cmd2.__version__) < version.parse("2.0.0"): kwargs = {'use_ipython': True} @@ -101,6 +116,7 @@ # pylint: disable=unexpected-keyword-arg super().__init__(persistent_history_file='~/.pysim_shell_history', allow_cli_args=False, auto_load_commands=False, startup_script=script, **kwargs) + warnings.showwarning = self.__print_warning self.intro = style(self.BANNER, fg=RED) self.default_category = 'pySim-shell built-in commands' self.card = None @@ -1098,6 +1114,12 @@
if __name__ == '__main__':
+ # Ensure that we are able to print formatted warnings from the beginning. When the PysimApp is created, this + # this callback will be replaced with a different callback (see above) + def __print_warning(message, category, filename, lineno, file=None, line=None): + print(__format_warning(message, category, filename, lineno)) + warnings.showwarning = __print_warning + startup_errors = False opts = option_parser.parse_args()
diff --git a/pySim/runtime.py b/pySim/runtime.py index 5bb730e..f47c053 100644 --- a/pySim/runtime.py +++ b/pySim/runtime.py @@ -17,11 +17,13 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see http://www.gnu.org/licenses/.
+from warnings import warn from typing import Optional, Tuple from osmocom.utils import h2b, i2h, is_hex, Hexstr from osmocom.tlv import bertlv_parse_one
from pySim.exceptions import * +from pySim.warnings import Info from pySim.filesystem import *
def lchan_nr_from_cla(cla: int) -> int: @@ -66,7 +68,7 @@ for addon_cls in self.profile.addons: addon = addon_cls() if addon.probe(self.card): - print("Detected %s Add-on "%s"" % (self.profile, addon)) + warn("Detected %s Add-on "%s"" % (self.profile, addon), Info) for f in addon.files_in_mf: self.mf.add_file(f)
@@ -100,18 +102,18 @@ apps_taken = [] if aids_card: aids_taken = [] - print("AIDs on card:") + warn("AIDs on card:", Info) for a in aids_card: for f in apps_profile: if f.aid in a: - print(" %s: %s (EF.DIR)" % (f.name, a)) + warn(" %s: %s (EF.DIR)" % (f.name, a), Info) aids_taken.append(a) apps_taken.append(f) aids_unknown = set(aids_card) - set(aids_taken) for a in aids_unknown: - print(" unknown: %s (EF.DIR)" % a) + warn(" unknown: %s (EF.DIR)" % a, Info) else: - print("warning: EF.DIR seems to be empty!") + warn("EF.DIR seems to be empty!", Warning)
# Some card applications may not be registered in EF.DIR, we will actively # probe for those applications @@ -126,7 +128,7 @@ _data, sw = self.card.select_adf_by_aid(f.aid) self.selected_adf = f if sw == "9000": - print(" %s: %s" % (f.name, f.aid)) + warn(" %s: %s" % (f.name, f.aid), Info) apps_taken.append(f) except (SwMatchError, ProtocolError): pass diff --git a/pySim/warnings.py b/pySim/warnings.py new file mode 100644 index 0000000..628fb81 --- /dev/null +++ b/pySim/warnings.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +""" pySim: Warnings +""" + +# +# (C) 2024 by Sysmocom s.f.m.c. GmbH +# All Rights Reserved +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. +# + +class Info(Warning): + """Informative message, technically not a warning.""" +