#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
**BETTERSIS**: The modern shell for SIS
"""
__author__ = "Zenaro Stefano"
import logging
import logging.handlers
import platform
import re
import os
import argparse
import traceback
import siswrapper
import blifparser.blifparser as blifparser
from prompt_toolkit import PromptSession
from prompt_toolkit import print_formatted_text, HTML
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
from prompt_toolkit.history import FileHistory
try:
from ._version import __version__ # noqa: F401
import bettersis.siscompleter as siscompleter
import bettersis.update_checker as update_checker
import bettersis.texteditor as texteditor
import bettersis.history_utils as history_utils
import bettersis.web_utils as web_utils
except ImportError:
from _version import __version__ # noqa: F401
import siscompleter
import update_checker
import texteditor
import history_utils
import web_utils
boold = False
github_repository_url = "https://github.com/mario33881/betterSIS"
bettersis_cmds = [
"edit",
"cd",
"ls",
"bsis_documentation",
"bsis_tutorials",
"bsis_releases",
"bsis_checkblif",
"help",
]
using_appimage = os.getenv("APPIMAGE") and os.getenv("APPDIR")
logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler())
[docs]def show_ghissues_msg(t_err):
"""
Shows the "please, report this to the developer" message.
:param Exception t_err: error from which to extract the traceback
"""
logger.debug("[SHOW_GHISSUES_MSG] Showing Github Issues message with the error")
print("\nTraceback:\n")
traceback.print_tb(t_err.__traceback__)
print("")
type_of_error = type(t_err)
print_formatted_text(HTML("<red>{}:</red> {}".format(type_of_error.__name__, str(t_err))))
print("\nPlease, (if someone didn't post this error already) create a Github Issue here: '{}'\n"
"and share the '/var/log/pybettersis/pybettersis.log' log file\n"
"to help the developer to fix the problem\n".format(github_repository_url + "/issues"))
print("> If you are NOT using the DEB package version, execute this command:")
print("> 'cat /var/log/syslog | grep \"bettersis\" > pybettersis.log'\n"
"> to create the log file inside the current directory.")
print("\nYou should also make sure that no private information is present in the log file before publishing it")
[docs]class Bettersis:
"""
Shows bettersis' interactive shell.
After the creation of an instance
a SIS process is ready to receive commands.
.. note:: The SIS process is started and controlled by
the `siswrapper <https://github.com/mario33881/siswrapper>`_. library
Shared variables:
- ``res``: dictionary with results of the start operation (success, errors, stdout)
- ``sis``: connection to SIS's process
- ``lastcmd``: contains the last command
- ``lastcmd_success``: boolean that is set to True if the last command execution was successfull
- ``history_file_path``: path to the history file (used when ``BSIS_HISTORY_ENABLED`` environment variable is "true")
"""
def __init__(self):
self.res = {"success": False, "errors": [], "stdout": None}
logger.debug("[__INIT__] Running SIS using siswrapper")
self.sis = siswrapper.Siswrapper()
self.lastcmd = "FIRSTCOMMAND"
self.lastcmd_success = False
self.history_file_path = os.path.join(os.path.expanduser("~"), ".bsis_history")
if self.sis.res["success"]:
self.show_msg_on_startup()
updates_res = update_checker.check_updates("https://api.github.com/repos/mario33881/bettersis/releases",
__version__.split(" ")[1])
if updates_res["success"]:
if updates_res["update_available"]:
print("\nNew Update Available! (latest: {})".format(updates_res["latest_version"]))
print("Type the 'bsis_releases' command to open the download webpage for the latest release!")
print("> If you are using the Snap version type 'snap refresh' "
"on the terminal to update to the latest version")
if using_appimage:
print("You can execute the 'bsis_update' command to download the latest AppImage")
else:
for error in updates_res["errors"]:
print(error)
logger.warning("[ERROR-UPDATE] {}".format(str(error)), exc_info=True)
self.main()
else:
for error in self.sis.res["errors"]:
self.res["errors"].append("[ERROR][INIT] Error during SIS startup: " + error)
[docs] def show_msg_on_startup(self):
"""
Shows the message at the shell startup.
"""
try:
print(" ")
print(" ██████╗ ███████╗████████╗████████╗███████╗██████╗ ███████╗██╗███████╗ ")
print(" ██╔══██╗██╔════╝╚══██╔══╝╚══██╔══╝██╔════╝██╔══██╗██╔════╝██║██╔════╝ ")
print(" ██████╔╝█████╗ ██║ ██║ █████╗ ██████╔╝███████╗██║███████╗ ")
print(" ██╔══██╗██╔══╝ ██║ ██║ ██╔══╝ ██╔══██╗╚════██║██║╚════██║ ")
print(" ██████╔╝███████╗ ██║ ██║ ███████╗██║ ██║███████║██║███████║ ")
print(" ╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚══════╝╚═╝╚══════╝ ")
except UnicodeEncodeError:
logger.debug("user probably didn't set an UTF-8 language in the "
"$LANG environment variable: not showing the ASCII art")
print(" ")
print(" ===================================================================== ")
print(" ")
print(" DISCLAIMER: ")
print(" BetterSIS, this software, controls SIS in the background and tries to ")
print(" provide modern features ")
print(" (such as command history, suggestions and autocompletion) ")
print(" and small improvements to SIS itself. (like the simulate improvement) ")
print(" > SIS is a tool for synthesis and optimization of sequential circuits ")
print(" ")
print(" I'm not affiliated with the SIS developers in any way. ")
print(" You can read more about SIS here: ")
print(" https://jackhack96.github.io/logic-synthesis/sis.html ")
print(" ")
print(" ===================================================================== ")
print(" ")
print(" BetterSIS version: {}".format(__version__))
print(" BetterSIS repository: {}".format(github_repository_url))
print(" Siswrapper version: {}".format(siswrapper.__version__))
print(" Running in the background: ", self.sis.res["stdout"])
logger.info("[STARTUP_MESSAGE] Showing software versions "
"(BetterSIS: %s, Siswrapper: %s, SIS: %s)" % (__version__,
siswrapper.__version__,
self.sis.res["stdout"]))
[docs] def main(self):
"""
Starts the interactive shell.
"""
# create a Session to memorize user commands
session = PromptSession()
# if the BSIS_HISTORY_ENABLED environment variable is set to "true",
# use a history file inside the home directory
if self.manage_history_file():
logger.debug("[MAIN] BSIS_HISTORY_ENABLED is set to true: setting up file history")
history_file = FileHistory(self.history_file_path)
session = PromptSession(history=history_file)
# get the first command to show the custom toolbar
try:
command = session.prompt('bsis> ',
completer=siscompleter.get_siscompleter(), # use completer from siscompleter.py
complete_in_thread=True, # better performance for autocompletion
auto_suggest=AutoSuggestFromHistory(), # suggest rest of command from history
bottom_toolbar="") # empty toolbar
except (KeyboardInterrupt, EOFError):
# user pressed Ctrl+C or Ctrl+D
logger.debug("[MAIN] User wants to exit the software (first prompt)")
else:
# keep getting commands from the user
while True:
try:
logger.debug("[MAIN] User executed command: " + command.strip())
if command.strip() in ["exit", "quit"]:
break
self.manage_multiple_commands(command.strip())
print("")
command = session.prompt('bsis> ',
completer=siscompleter.get_siscompleter(), # use completer from siscompleter.py
complete_in_thread=True, # better performance autocompletion
auto_suggest=AutoSuggestFromHistory(), # suggest rest of command from history
bottom_toolbar=self.bottom_toolbar) # add toolbar at the bottom
except (KeyboardInterrupt, EOFError):
# user pressed Ctrl+C or Ctrl+D
logger.debug("[MAIN] User wants to exit the software (at least one command has been executed before)")
break
# control, if necessary, the size of the history file before closing betterSIS
logger.debug("[MAIN] Checking/keeping under control the size of the history file (if the history file has been used)")
self.manage_history_file()
# stop sis process
logger.debug("[MAIN] Stopping SIS")
self.sis.stop()
[docs] def manage_history_file(self):
"""
Manages the history file size and usage.
Checks if the ``BSIS_HISTORY_ENABLED`` environment variable is set to true:
if is is set to true the function returns True and betterSIS
will use the history file,
else the history will expire at the end of the session.
Then, if a history file already exists, checks the ``BSIS_HISTORY_SIZELIMIT`` environment:
if the value is a number it is used as a size limit,
else the limit is the default one (0.1 MB)
"""
default_size = 0.1 * 1000 * 1000
history_file_enabled = False
if os.getenv("BSIS_HISTORY_ENABLED"):
logger.debug("[MANAGE_HISTORY_FILE] The BSIS_HISTORY_ENABLED "
"env. variable is set to '{}'".format(os.getenv("BSIS_HISTORY_ENABLED")))
if os.getenv("BSIS_HISTORY_ENABLED") == "true":
history_file_enabled = True
# if the history file exists, manage its size
if os.path.isfile(self.history_file_path):
logger.debug("[MANAGE_HISTORY_FILE] The history file exists, controlling its size...")
if os.getenv("BSIS_HISTORY_SIZELIMIT"):
# try to use custom size limit (use default one on error)
try:
size = int(os.getenv("BSIS_HISTORY_SIZELIMIT"))
logger.debug("[MANAGE_HISTORY_FILE] Using custom size limit: '{}' bytes".format(size))
history_utils.limit_history_size(self.history_file_path, size)
except ValueError:
logger.debug("[MANAGE_HISTORY_FILE] Using default size limit: '{}' bytes "
"(BSIS_HISTORY_SIZELIMIT is invalid)".format(default_size))
warning_msg = "<b><yellow>[Warning]</yellow></b> The BSIS_HISTORY_SIZELIMIT env. variable value" \
" ('{}') is invalid: " \
"it needs to be a number".format(os.getenv("BSIS_HISTORY_SIZELIMIT"))
print_formatted_text(warning_msg)
history_utils.limit_history_size(self.history_file_path, default_size)
else:
# use default size limit
logger.debug("[MANAGE_HISTORY_FILE] Using default size limit: '{}' bytes".format(default_size))
history_utils.limit_history_size(self.history_file_path, default_size)
else:
logger.debug("[MANAGE_HISTORY_FILE] The BSIS_HISTORY_ENABLED env. variable is NOT set, using normal session")
return history_file_enabled
[docs] def manage_multiple_commands(self, t_commands):
"""
Executes the command(s) <t_commands>.
:param str t_commands: command(s) to execute (using SIS or special bSIS commands)
"""
# loop for each command separated by ";"
for command in t_commands.split(";"):
if command.strip().split(" ")[0] in bettersis_cmds:
# intercept betterSIS commands
self.manage_bsis_command(command.strip())
else:
# manage SIS commands
self.manage_command(command.strip())
[docs] def manage_bsis_command(self, t_command): # noqa: C901
"""
Executes the ``<t_command>`` command betterSIS.
:param str t_command: custom betterSIS command to execute
"""
# memorize command to show it on the toolbar
command = t_command.split(" ")[0]
self.lastcmd = command
cd_matches = re.match(r"cd[\s]*[(\")*(\')*]*([^\"']*)[(\")*(\')*]*", t_command)
ls_matches = re.match(r"ls[\s]*[(\")*(\')*]*([^\"']*)[(\")*(\')*]*", t_command)
edit_matches = re.match(r"edit[\s]*[(\")*(\')*]*([^\"']*)[(\")*(\')*]*", t_command)
checkblif_matches = re.match(r"bsis_checkblif[\s]*[(\")*(\')*]*([^\"']*)[(\")*(\')*]*", t_command)
help_matches = re.match(r"help ([^\"']*)", t_command)
logger.debug("[%s-MANAGE_BSIS_COMMAND] %s" % (self.lastcmd, t_command))
# cd command
if cd_matches:
if cd_matches.groups()[0].strip() != "":
self.bsiscmd_cd(cd_matches.groups()[0])
logger.debug("[%s-EXECUTED_CD_BSIS_COMMAND] %s" % (self.lastcmd, t_command))
else:
logger.debug("[%s-EXECUTED_INCOMPLETE_BSIS_COMMAND] %s" % (self.lastcmd, t_command))
error_msg = "<b><red>[ERROR]</red></b> Incomplete cd command (need to specify the directory)"
print_formatted_text(HTML(error_msg))
self.lastcmd_success = False
# ls command
elif t_command == "ls":
self.bsiscmd_ls("")
logger.debug("[%s-EXECUTED_LS_BSIS_COMMAND] %s" % (self.lastcmd, t_command))
elif ls_matches:
if ls_matches.groups()[0].strip() != "":
self.bsiscmd_ls(ls_matches.groups()[0])
logger.debug("[%s-EXECUTED_LS_BSIS_COMMAND] %s" % (self.lastcmd, t_command))
else:
error_msg = "<b><red>[ERROR]</red></b> Incomplete ls command"
logger.debug("[%s-EXECUTED_INCOMPLETE_BSIS_COMMAND] %s" % (self.lastcmd, t_command))
print_formatted_text(HTML(error_msg))
self.lastcmd_success = False
# edit command
elif edit_matches:
if edit_matches.groups()[0].strip() != "":
self.bsiscmd_edit(edit_matches.groups()[0])
logger.debug("[%s-EXECUTED_EDIT_BSIS_COMMAND] %s" % (self.lastcmd, t_command))
else:
error_msg = "<b><red>[ERROR]</red></b> Incomplete edit command (need to specify the filename to edit)"
logger.debug("[%s-EXECUTED_INCOMPLETE_BSIS_COMMAND] %s" % (self.lastcmd, t_command))
print_formatted_text(HTML(error_msg))
self.lastcmd_success = False
# bsis_documentation command
elif t_command == "bsis_documentation":
url = "https://bettersis.readthedocs.io/en/latest/readme.html"
self.lastcmd_success = web_utils.open_browser(url, "documentation")
logger.debug("[%s-EXECUTED_BSIS_DOCUMENTATION_BSIS_COMMAND] %s" % (self.lastcmd, t_command))
# bsis_tutorials command
elif t_command == "bsis_tutorials":
url = "https://bettersis.readthedocs.io/en/latest/tutorials/tutorials.html"
self.lastcmd_success = web_utils.open_browser(url, "tutorials")
logger.debug("[%s-EXECUTED_BSIS_TUTORIALS_BSIS_COMMAND] %s" % (self.lastcmd, t_command))
# bsis_releases command
elif t_command == "bsis_releases":
url = "https://github.com/mario33881/betterSIS/releases/latest"
self.lastcmd_success = web_utils.open_browser(url, "Github releases")
logger.debug("[%s-EXECUTED_BSIS_RELEASES_BSIS_COMMAND] %s" % (self.lastcmd, t_command))
# bsis_checkblif command
elif checkblif_matches:
filepath = checkblif_matches.groups()[0]
filepath = filepath.strip()
if filepath != "":
# user specified the input path
if os.path.isfile(filepath):
blif = blifparser.BlifParser(filepath).blif
print("\nISSUES LIST:\n")
for problem in blif.problems:
print(problem)
print("=" * 50)
print("\n{} issues found".format(len(blif.problems)))
self.lastcmd_success = True
logger.debug("[%s-EXECUTED_CHECKBLIF_BSIS_COMMAND] %s : %s"
% (self.lastcmd, "filepath specified and exists", filepath))
else:
print_formatted_text(HTML("<b><red>[ERROR]</red></b> bsis_checkblif input file doesn't exist: "
"please specify a correct path OR "
"call bsis_checkblif (with no parameters) AFTER using the read_blif command"))
logger.debug("[%s-EXECUTED_CHECKBLIF_BSIS_COMMAND] %s : %s"
% (self.lastcmd, "filepath specified but IT DOES NOT exist", filepath))
self.lastcmd_success = False
else:
# user didn't specify input path: using the file read using the read_blif command (if available)
if self.sis.read_path is not None:
blif = blifparser.BlifParser(self.sis.read_path).blif
print("\nISSUES LIST:\n")
for problem in blif.problems:
print(problem)
print("=" * 50)
print("\n{} issues found".format(len(blif.problems)))
logger.debug("[%s-EXECUTED_CHECKBLIF_BSIS_COMMAND] %s : %s" % (
self.lastcmd,
"filepath NOT specified but a read command was used before",
self.sis.read_path
)
)
self.lastcmd_success = True
else:
print_formatted_text(HTML("<b><red>[ERROR]</red></b> "
"no file was specified for the bsis_checkblif command: "
"please specify a correct path as a parameter OR call bsis_checkblif "
"(with no parameters) AFTER using the read_blif command"))
logger.debug("[%s-EXECUTED_CHECKBLIF_BSIS_COMMAND] %s"
% (self.lastcmd, "filepath NOT specified and a read command WAS NOT USED before"))
self.lastcmd_success = False
# bsis_update command
elif t_command == "bsis_update" and using_appimage:
logger.debug("[%s-EXECUTED_BSIS_UPDATE_BSIS_COMMAND] %s" % (self.lastcmd, "trying to find AppImage updates"))
self.lastcmd_success = update_checker.update_appimage()
# help command
elif t_command == "help":
logger.debug("[%s-EXECUTED_HELP_BSIS_COMMAND] %s" % (self.lastcmd, "showing all the commands"))
print("\nSIS COMMANDS:")
self.manage_command(t_command)
print("\nBETTERSIS COMMANDS:\n")
print("* edit : opens a file with a simple CLI text editor")
print("* cd : changes current working directory")
print("* ls : shows the content of the current working directory")
print("* bsis_documentation : opens the website with the betterSIS documentation")
print("* bsis_tutorials : opens the website with SIS/betterSIS tutorials")
print("* bsis_releases : opens the betterSIS's download page")
print("* bsis_checkblif : BLIF parser/validation tool")
print("* bsis_script : executes optimization and mapping commands in one command "
"(needs parameters to specify the type of circuit to optimize/map)")
if using_appimage:
# Running inside AppImage: show that you can update the AppImage
logger.debug("[%s-EXECUTED_HELP_BSIS_COMMAND] %s" % (
self.lastcmd, "also showing the bsis_update command (running in AppImage)"
)
)
print("* bsis_update : download the latest version (as an AppImage)")
print("\n> If you would like to know more about these commands, "
"execute the 'bsis_documentation' command to open the documentation website")
self.lastcmd_success = True
elif help_matches:
self.manage_command(t_command)
# unexpected bsis command
else:
self.lastcmd_success = False
error_msg = "<b><red>[ERROR]</red></b> Unexpected betterSIS command ('" + t_command + "')..."
show_ghissues_msg(Exception(error_msg))
logger.critical("[%s-UNKNOWN_BSIS_COMMAND] %s" % (self.lastcmd, t_command), exc_info=True)
[docs] def bsiscmd_cd(self, t_path):
"""
Changes current directory to the ``<t_path>`` directory.
:param str t_path: new working path
"""
if os.path.isdir(t_path):
os.chdir(t_path)
self.lastcmd_success = True
else:
self.lastcmd_success = False
error_msg = "<b><red>[ERROR]</red></b> '" + t_path + "' folder doesn't exist"
print_formatted_text(HTML(error_msg))
[docs] def bsiscmd_ls(self, t_path):
"""
Lists files and directories inside the ``<t_path>`` folder.
:param str t_path: path in which to execute the "ls" command
"""
if t_path == "":
# show files and directories inside the current folder
print_formatted_text(HTML("<orange>(F)iles</orange> and "
"<DeepSkyBlue>(D)irectories</DeepSkyBlue> "
"inside '" + os.getcwd() + "'"))
print("-" * 50)
self.lastcmd_success = True
for el in os.listdir():
if os.path.isdir(el):
print_formatted_text(HTML("<DeepSkyBlue>(D) " + el + "</DeepSkyBlue>"))
else:
print_formatted_text(HTML("<orange>(F) " + el + "</orange>"))
elif os.path.isdir(t_path):
# show files and directories inside the specified folder
print_formatted_text(HTML("<orange>(F)iles</orange> and "
"<DeepSkyBlue>(D)irectories</DeepSkyBlue> "
"inside '" + t_path + "'"))
print("-" * 50)
self.lastcmd_success = True
for el in os.listdir(t_path):
if os.path.isdir(el):
print_formatted_text(HTML("<DeepSkyBlue>(D) " + el + "</DeepSkyBlue>"))
else:
print_formatted_text(HTML("<orange>(F) " + el + "</orange>"))
else:
self.lastcmd_success = False
error_msg = "<b><red>[ERROR]</red></b> '" + t_path + "' folder doesn't exist"
print_formatted_text(HTML(error_msg))
[docs] def bsiscmd_edit(self, t_path):
"""
Opens the ``<t_path>`` file with the text editor.
:param str t_path: path of the file to open.
"""
directory = os.path.dirname(os.path.abspath(t_path))
if os.path.isdir(directory):
self.lastcmd_success = True
# the directory exists, make sure that the file exists
with open(t_path, "a") as _:
pass
# open it with the text editor
texteditor.SimpleTextEditor(t_path)
else:
error_msg = "<b><red>[ERROR]</red></b> '" + directory + "' folder doesn't exist (can't open/create file)"
print_formatted_text(HTML(error_msg))
self.lastcmd_success = False
[docs] def manage_command(self, t_command):
"""
Executes the ``<t_command>`` command using SIS.
:param str t_command: command to execute using SIS
"""
# memorize command to show it on the toolbar
command = t_command.split(" ")[0]
self.lastcmd = command
# try to execute the best possible method
# to parse the command output
cmd_res = self.sis.parsed_exec(t_command)
logger.debug("[%s-EXECUTED_COMMAND] Executed command: %s" % (self.lastcmd, t_command))
logger.debug("[%s-SISWRAPPER_RETURN] Returned: %s" % (self.lastcmd, cmd_res))
# set success status (used for the bottom toolbar)
self.lastcmd_success = cmd_res["success"]
# if stdout is present, go to newline and show the output of the command
if cmd_res["stdout"] is not None:
print("")
for line in cmd_res["stdout"].split("\r\n"):
print(line)
logger.debug("[%s-STDOUT] %s" % (self.lastcmd, line))
# if the command returns warnings, show the warnings
if "warnings" in cmd_res.keys():
if len(cmd_res["warnings"]) > 0:
print_formatted_text(HTML("\n<b>Command execution returned some "
"warnings (which can probably be ignored):</b>"))
for warning in cmd_res["warnings"]:
formatted_warning = HTML(warning.replace("Warning:", "<b><yellow>[Warning]</yellow></b>"))
print_formatted_text(formatted_warning)
logger.debug("[%s-WARNINGS] %s" % (self.lastcmd, formatted_warning))
# if the command returns errors, show the errors
if len(cmd_res["errors"]) > 0:
print_formatted_text(HTML("\n<b>Command execution returned some ERRORS:</b>"))
for error in cmd_res["errors"]:
error_msg = error.replace("[ERROR]", "<b><red>[ERROR]</red></b>")
if not error_msg.startswith("<b><red>[ERROR]</red></b>"):
error_msg = "<b><red>[ERROR]</red></b> " + error_msg
print_formatted_text(HTML(error_msg))
logger.debug("[%s-ERRORS] %s" % (self.lastcmd, error_msg))
[docs]def main():
"""
Main function: sets up the logger, calls betterSIS and manages exceptions
"""
global boold
parser = argparse.ArgumentParser(prog="bsis/bettersis")
parser.add_argument('--debug', action='store_true', default=False, help='Enables advanced logging for debugging')
parser.add_argument(
'--verbosedebug',
action='store_true',
default=False,
help='If used with the debug flag: shows debug data on the terminal'
)
args = parser.parse_args()
if args.debug:
boold = True
# remove NullHandler
logger.removeHandler(logger.handlers[0])
# create syslog handler
handler = logging.handlers.SysLogHandler(address='/dev/log', facility='local5')
logger.addHandler(handler)
# set handler's output format
formatter = logging.Formatter('bettersis[%(process)d]: %(levelname)s: [%(asctime)s]%(message)s')
handler.setFormatter(formatter)
if boold:
logger.setLevel(logging.DEBUG)
if args.verbosedebug:
console_handler = logging.StreamHandler()
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
else:
logger.setLevel(logging.INFO)
logger.addHandler(handler)
logger.info("[PLATFORM] Using OS: %s" % platform.platform())
logger.info("[PLATFORM] OS version: %s" % platform.version())
logger.info("[PLATFORM] Python version: %s" % platform.python_version())
if using_appimage:
# Running inside AppImage: allow the user to update the image
logger.debug("[MAIN_FUNC] Running inside an AppImage: adding 'bsis_update' command to bettersis_cmds")
bettersis_cmds.append("bsis_update")
bettersis = None
try:
logger.debug("[MAIN_FUNC] Running BetterSIS")
bettersis = Bettersis()
# show errors (if present)
for error in bettersis.res["errors"]:
print_formatted_text(HTML(error.replace("[ERROR]", "<b><red>[ERROR]</red></b>")))
except Exception as e:
logger.critical("[MAIN_FUNC] This error was unexpected and interrupted the program: ", exc_info=True)
show_ghissues_msg(e)
logger.debug("[MAIN_FUNC] Stop SIS if it is still running")
# stop SIS's process if it is still running
if bettersis is not None:
if bettersis.sis.started:
bettersis.sis.stop()
logger.debug("[MAIN_FUNC] Reached the end of the script")
if __name__ == "__main__":
main()