Beispiel #1
0
def main(options: Optional[List[str]] = None) -> None:
    """
    Launch Zulip Terminal.
    """

    argv = options if options is not None else sys.argv[1:]
    args = parse_args(argv)

    set_encoding('utf-8')

    if args.debug:
        print("NOTE: Debug mode enabled; API calls being logged to {}.".format(
            in_color("blue", API_CALL_LOG_FILENAME)))
        requests_logfile_handler = logging.FileHandler(API_CALL_LOG_FILENAME)
        requests_logger.addHandler(requests_logfile_handler)
    else:
        requests_logger.addHandler(logging.NullHandler())

    if args.profile:
        import cProfile
        prof = cProfile.Profile()
        prof.enable()

    if args.version:
        print('Zulip Terminal ' + ZT_VERSION)
        sys.exit(0)

    if args.list_themes:
        available_themes = all_themes()
        print('Themes available:')
        for theme in available_themes:
            print('    {}'.format(theme))
        sys.exit(0)

    if args.config_file:
        zuliprc_path = args.config_file
    else:
        zuliprc_path = '~/zuliprc'

    try:
        zterm = parse_zuliprc(zuliprc_path)

        if args.autohide:
            zterm['autohide'] = (args.autohide, 'on command line')

        if args.theme:
            theme_to_use = (args.theme, 'on command line')
        else:
            theme_to_use = zterm['theme']

        available_themes = all_themes()
        theme_aliases = aliased_themes()
        is_valid_theme = (theme_to_use[0] in available_themes
                          or theme_to_use[0] in theme_aliases)
        if not is_valid_theme:
            print("Invalid theme '{}' was specified {}.".format(*theme_to_use))
            print("The following themes are available:")
            for theme in available_themes:
                print("  ", theme)
            print("Specify theme in zuliprc file or override "
                  "using -t/--theme options on command line.")
            sys.exit(1)
        if theme_to_use[0] not in available_themes:
            # theme must be an alias, as it is valid
            real_theme_name = theme_aliases[theme_to_use[0]]
            theme_to_use = (real_theme_name, "{} (by alias '{}')".format(
                theme_to_use[1], theme_to_use[0]))

        if args.color_depth:
            zterm['color-depth'] = (args.color_depth, 'on command line')

        color_depth = int(zterm['color-depth'][0])

        print("Loading with:")
        print("   theme '{}' specified {}.".format(*theme_to_use))
        complete, incomplete = complete_and_incomplete_themes()
        if theme_to_use[0] in incomplete:
            print(
                in_color(
                    'yellow', "   WARNING: Incomplete theme; "
                    "results may vary!\n"
                    "      (you could try: {})".format(", ".join(complete))))
        print("   autohide setting '{}' specified {}.".format(
            *zterm['autohide']))
        print("   footlinks setting '{}' specified {}.".format(
            *zterm['footlinks']))
        print("   color depth setting '{}' specified {}.".format(
            *zterm['color-depth']))
        # For binary settings
        # Specify setting in order True, False
        valid_settings = {
            'autohide': ['autohide', 'no_autohide'],
            'notify': ['enabled', 'disabled'],
            'footlinks': ['enabled', 'disabled'],
            'color-depth': ['1', '16', '256']
        }
        boolean_settings = dict()  # type: Dict[str, bool]
        for setting, valid_values in valid_settings.items():
            if zterm[setting][0] not in valid_values:
                print("Invalid {} setting '{}' was specified {}.".format(
                    setting, *zterm[setting]))
                print("The following options are available:")
                for option in valid_values:
                    print("  ", option)
                print("Specify the {} option in zuliprc file.".format(setting))
                sys.exit(1)
            if setting == 'color-depth':
                break
            boolean_settings[setting] = (zterm[setting][0] == valid_values[0])

        if color_depth == 1:
            theme_data = theme_with_monochrome_added(THEMES[theme_to_use[0]])
        else:
            theme_data = THEMES[theme_to_use[0]]

        Controller(zuliprc_path, theme_data, color_depth, args.explore,
                   **boolean_settings).main()
    except ServerConnectionFailure as e:
        print(
            in_color('red',
                     "\nError connecting to Zulip server: {}.".format(e)))
        # Acts as separator between logs
        zt_logger.info("\n\n" + str(e) + "\n\n")
        zt_logger.exception(e)
        sys.exit(1)
    except (display_common.AttrSpecError, display_common.ScreenError) as e:
        # NOTE: Strictly this is not necessarily just a theme error
        # FIXME: Add test for this - once loading takes place after UI setup
        print(in_color('red', "\nPossible theme error: {}.".format(e)))
        # Acts as separator between logs
        zt_logger.info("\n\n" + str(e) + "\n\n")
        zt_logger.exception(e)
        sys.exit(1)
    except Exception as e:
        zt_logger.info("\n\n" + str(e) + "\n\n")
        zt_logger.exception(e)
        if args.debug:
            sys.stdout.flush()
            traceback.print_exc(file=sys.stderr)
            run_debugger = input("Run Debugger? (y/n): ")
            if run_debugger in ["y", "Y", "yes"]:
                # Open PUDB Debuuger
                import pudb
                pudb.post_mortem()

        if hasattr(e, 'extra_info'):
            print(
                "\n" + in_color("red", e.extra_info),  # type: ignore
                file=sys.stderr)

        print(in_color(
            "red", "\nZulip Terminal has crashed!"
            "\nPlease refer to " + TRACEBACK_LOG_FILENAME +
            " for full log of the error."),
              file=sys.stderr)
        print("You can ask for help at:", file=sys.stderr)
        print("https://chat.zulip.org/#narrow/stream/206-zulip-terminal",
              file=sys.stderr)
        print("\nThanks for using the Zulip-Terminal interface.\n")
        sys.stderr.flush()

    finally:
        if args.profile:
            prof.disable()
            import tempfile
            with tempfile.NamedTemporaryFile(prefix="zulip_term_profile.",
                                             suffix=".dat",
                                             delete=False) as profile_file:
                profile_path = profile_file.name
            # Dump stats only after temporary file is closed (for Win NT+ case)
            prof.dump_stats(profile_path)
            print("Profile data saved to {0}.\n"
                  "You can visualize it using e.g. `snakeviz {0}`".format(
                      profile_path))

        sys.exit(1)
Beispiel #2
0
def main(options: Optional[List[str]] = None) -> None:
    """
    Launch Zulip Terminal.
    """

    argv = options if options is not None else sys.argv[1:]
    args = parse_args(argv)

    set_encoding("utf-8")

    if args.debug:
        print("NOTE: Debug mode enabled; API calls being logged to {}.".format(
            in_color("blue", API_CALL_LOG_FILENAME)))
        requests_logfile_handler = logging.FileHandler(API_CALL_LOG_FILENAME)
        requests_logger.addHandler(requests_logfile_handler)
    else:
        requests_logger.addHandler(logging.NullHandler())

    if args.profile:
        import cProfile

        prof = cProfile.Profile()
        prof.enable()

    if args.version:
        print(f"Zulip Terminal {ZT_VERSION}")
        sys.exit(0)

    if args.list_themes:
        print(list_themes())
        sys.exit(0)

    if args.config_file:
        zuliprc_path = args.config_file
    else:
        zuliprc_path = "~/zuliprc"

    try:
        zterm = parse_zuliprc(zuliprc_path)

        if args.autohide:
            zterm["autohide"] = (args.autohide, "on command line")

        if args.theme:
            theme_to_use = (args.theme, "on command line")
        else:
            theme_to_use = zterm["theme"]

        if (zterm["footlinks"][1] == ZULIPRC_CONFIG
                and zterm["maximum-footlinks"][1] == ZULIPRC_CONFIG):
            exit_with_error(
                "Footlinks property is not allowed alongside maximum-footlinks"
            )

        if (zterm["maximum-footlinks"][1] == ZULIPRC_CONFIG
                and int(zterm["maximum-footlinks"][0]) < 0):
            exit_with_error("Minimum value allowed for maximum-footlinks is 0")

        if zterm["footlinks"][1] == ZULIPRC_CONFIG:
            if zterm["footlinks"][0] == DEFAULT_SETTINGS["footlinks"]:
                maximum_footlinks = 3
            else:
                maximum_footlinks = 0
        else:
            maximum_footlinks = int(zterm["maximum-footlinks"][0])

        available_themes = all_themes()
        theme_aliases = aliased_themes()
        is_valid_theme = (theme_to_use[0] in available_themes
                          or theme_to_use[0] in theme_aliases)
        if not is_valid_theme:
            exit_with_error(
                "Invalid theme '{}' was specified {}.".format(*theme_to_use),
                helper_text=list_themes(),
            )
        if theme_to_use[0] not in available_themes:
            # theme must be an alias, as it is valid
            real_theme_name = theme_aliases[theme_to_use[0]]
            theme_to_use = (
                real_theme_name,
                "{} (by alias '{}')".format(theme_to_use[1], theme_to_use[0]),
            )

        if args.color_depth:
            zterm["color-depth"] = (args.color_depth, "on command line")

        color_depth_str = zterm["color-depth"][0]
        if color_depth_str == "24bit":
            color_depth = 2**24
        else:
            color_depth = int(color_depth_str)

        if args.notify:
            zterm["notify"] = (args.notify, "on command line")

        print("Loading with:")
        print("   theme '{}' specified {}.".format(*theme_to_use))
        complete, incomplete = complete_and_incomplete_themes()
        if theme_to_use[0] in incomplete:
            if complete:
                incomplete_theme_warning = (
                    "   WARNING: Incomplete theme; results may vary!\n"
                    "      (you could try: {})".format(", ".join(complete)))
            else:
                incomplete_theme_warning = (
                    "   WARNING: Incomplete theme; results may vary!\n"
                    "      (all themes are incomplete)")
            print(in_color("yellow", incomplete_theme_warning))
        print("   autohide setting '{}' specified {}.".format(
            *zterm["autohide"]))
        if zterm["footlinks"][1] == ZULIPRC_CONFIG:
            print(
                "   maximum footlinks value '{}' specified {} from footlinks.".
                format(maximum_footlinks, zterm["footlinks"][1]))
        else:
            print("   maximum footlinks value '{}' specified {}.".format(
                *zterm["maximum-footlinks"]))
        print("   color depth setting '{}' specified {}.".format(
            *zterm["color-depth"]))
        print("   notify setting '{}' specified {}.".format(*zterm["notify"]))

        # For binary settings
        # Specify setting in order True, False
        valid_settings = {
            "autohide": ["autohide", "no_autohide"],
            "notify": ["enabled", "disabled"],
            "color-depth": ["1", "16", "256", "24bit"],
        }
        boolean_settings: Dict[str, bool] = dict()
        for setting, valid_values in valid_settings.items():
            if zterm[setting][0] not in valid_values:
                helper_text = (
                    ["Valid values are:"] +
                    [f"  {option}" for option in valid_values] +
                    [f"Specify the {setting} option in zuliprc file."])
                exit_with_error(
                    "Invalid {} setting '{}' was specified {}.".format(
                        setting, *zterm[setting]),
                    helper_text="\n".join(helper_text),
                )
            if setting == "color-depth":
                break
            boolean_settings[setting] = zterm[setting][0] == valid_values[0]

        theme_data = generate_theme(theme_to_use[0], color_depth)

        Controller(
            zuliprc_path,
            maximum_footlinks,
            theme_to_use[0],
            theme_data,
            color_depth,
            args.explore,
            **boolean_settings,
        ).main()
    except ServerConnectionFailure as e:
        # Acts as separator between logs
        zt_logger.info(f"\n\n{e}\n\n")
        zt_logger.exception(e)
        exit_with_error(f"\nError connecting to Zulip server: {e}.")
    except (display_common.AttrSpecError, display_common.ScreenError) as e:
        # NOTE: Strictly this is not necessarily just a theme error
        # FIXME: Add test for this - once loading takes place after UI setup

        # Acts as separator between logs
        zt_logger.info(f"\n\n{e}\n\n")
        zt_logger.exception(e)
        exit_with_error(f"\nPossible theme error: {e}.")
    except Exception as e:
        zt_logger.info("\n\n{e}\n\n")
        zt_logger.exception(e)
        if args.debug:
            sys.stdout.flush()
            traceback.print_exc(file=sys.stderr)
            run_debugger = input("Run Debugger? (y/n): ")
            if run_debugger in ["y", "Y", "yes"]:
                # Open PUDB Debugger
                import pudb

                pudb.post_mortem()

        if hasattr(e, "extra_info"):
            print(in_color("red", f"\n{e.extra_info}"),
                  file=sys.stderr)  # type: ignore

        print(
            in_color(
                "red",
                "\nZulip Terminal has crashed!"
                f"\nPlease refer to {TRACEBACK_LOG_FILENAME}"
                " for full log of the error.",
            ),
            file=sys.stderr,
        )
        print(
            "You can ask for help at:"
            "\nhttps://chat.zulip.org/#narrow/stream/206-zulip-terminal",
            file=sys.stderr,
        )
        print("\nThanks for using the Zulip-Terminal interface.\n")
        sys.stderr.flush()

    finally:
        if args.profile:
            prof.disable()
            import tempfile

            with tempfile.NamedTemporaryFile(prefix="zulip_term_profile.",
                                             suffix=".dat",
                                             delete=False) as profile_file:
                profile_path = profile_file.name
            # Dump stats only after temporary file is closed (for Win NT+ case)
            prof.dump_stats(profile_path)
            print("Profile data saved to {0}.\n"
                  "You can visualize it using e.g. `snakeviz {0}`".format(
                      profile_path))

        sys.exit(1)