Files
entropy/client/solo/main.py
T
Sławomir Nizio 7cf7e1f6b7 convert solo imports, with needed changes to entropy_path_loader
The module entropy_path_loader (used only for running from within the
checkout; otherwise not even installed) is made to provide the _entropy
namespace.

(Other ideas instead of this entropy_path_loader change would be to
reorganise files layout; drop support for running from the checkout as
is - and perhaps require virtualenvs; require sourcing a script that
sets PYTHONPATH. However, as implemented, it is not intrusive, and the
good part is that it is quite isolated, not used in normal usage after
installation. Basically, it only does sys.path + provides _entropy
namespace.)
2018-11-26 20:15:36 +01:00

349 lines
10 KiB
Python

# -*- coding: utf-8 -*-
"""
@author: Fabio Erculiani <lxnay@sabayon.org>
@contact: lxnay@sabayon.org
@copyright: Fabio Erculiani
@license: GPL-2
B{Entropy Command Line Client}.
"""
import os
import sys
import errno
import pdb
from entropy.i18n import _
from entropy.output import print_error, print_warning, bold, purple, \
teal, blue, darkred, darkgreen, readtext, print_generic, TextInterface, \
is_stdout_a_tty, nocolor
from entropy.const import etpConst, const_convert_to_rawstring, \
const_convert_to_unicode, const_debug_enabled, const_mkstemp
from entropy.exceptions import SystemDatabaseError, OnlineMirrorError, \
RepositoryError, PermissionDenied, FileNotFound, SPMError
import entropy.tools
from _entropy.solo.commands.descriptor import SoloCommandDescriptor
from _entropy.solo.utils import read_client_release
def handle_exception(exc_class, exc_instance, exc_tb):
# restore original exception handler, to avoid loops
uninstall_exception_handler()
_text = TextInterface()
if exc_class is SystemDatabaseError:
_text.output(
darkred(_("Installed packages repository corrupted. "
"Please re-generate it")),
importance=1,
level="error")
os._exit(101)
generic_exc_classes = (OnlineMirrorError, RepositoryError,
PermissionDenied, FileNotFound, SPMError, SystemError)
if exc_class in generic_exc_classes:
_text.output(
"%s: %s" % (exc_instance, darkred(_("Cannot continue")),),
importance=1,
level="error")
os._exit(1)
if exc_class is SystemExit:
return
if issubclass(exc_class, IOError): # in Python 3.3+ it's BrokenPipeError
if exc_instance.errno == errno.EPIPE:
return
if exc_class is KeyboardInterrupt:
os._exit(1)
t_back = entropy.tools.get_traceback(tb_obj = exc_tb)
if const_debug_enabled():
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
sys.stdin = sys.__stdin__
entropy.tools.print_exception(tb_data = exc_tb)
pdb.set_trace()
if exc_class in (IOError, OSError):
if exc_instance.errno == errno.ENOSPC:
print_generic(t_back)
_text.output(
"%s: %s" % (
exc_instance,
darkred(_("Your hard drive is full! Your fault!")),),
importance=1,
level="error")
os._exit(5)
elif exc_instance.errno == errno.ENOMEM:
print_generic(t_back)
_text.output(
"%s: %s" % (
exc_instance,
darkred(_("No more memory dude! Your fault!")),),
importance=1,
level="error")
os._exit(5)
_text.output(
darkred(_("Hi. My name is Bug Reporter. "
"I am sorry to inform you that the program crashed. "
"Well, you know, shit happens.")),
importance=1,
level="error")
_text.output(
darkred(_("But there's something you could "
"do to help me to be a better application.")),
importance=1,
level="error")
_text.output(
darkred(
_("-- BUT, DO NOT SUBMIT THE SAME REPORT MORE THAN ONCE --")),
importance=1,
level="error")
_text.output(
darkred(
_("Now I am showing you what happened. "
"Don't panic, I'm here to help you.")),
importance=1,
level="error")
entropy.tools.print_exception(tb_data = exc_tb)
exception_data = entropy.tools.print_exception(silent = True,
tb_data = exc_tb, all_frame_data = True)
exception_tback_raw = const_convert_to_rawstring(t_back)
error_fd, error_file = None, None
try:
error_fd, error_file = const_mkstemp(
prefix="entropy.error.report.",
suffix=".txt")
with os.fdopen(error_fd, "wb") as ferror:
ferror.write(
const_convert_to_rawstring(
"\nRevision: %s\n\n" % (
etpConst['entropyversion'],))
)
ferror.write(
exception_tback_raw)
ferror.write(
const_convert_to_rawstring("\n\n"))
ferror.write(
const_convert_to_rawstring(''.join(exception_data)))
ferror.write(
const_convert_to_rawstring("\n"))
except (OSError, IOError) as err:
_text.output(
"%s: %s" % (
err,
darkred(
_("Oh well, I cannot even write to TMPDIR. "
"So, please copy the error and "
"mail lxnay@sabayon.org."))),
importance=1,
level="error")
os._exit(1)
finally:
if error_fd is not None:
try:
os.close(error_fd)
except OSError:
pass
_text.output("", level="error")
ask_msg = _("Erm... Can I send the error, "
"along with some other information\nabout your "
"hardware to my creators so they can fix me? "
"(Your IP will be logged)")
rc = _text.ask_question(ask_msg)
if rc == _("No"):
_text.output(
darkgreen(_("Ok, ok ok ok... Sorry!")),
level="error")
os._exit(2)
_text.output(
darkgreen(
_("If you want to be contacted back "
"(and actively supported), also answer "
"the questions below:")
),
level="error")
try:
name = readtext(_("Your Full name:"))
email = readtext(_("Your E-Mail address:"))
description = readtext(_("What you were doing:"))
except EOFError:
os._exit(2)
try:
from entropy.client.interfaces.qa import UGCErrorReport
from entropy.core.settings.base import SystemSettings
_settings = SystemSettings()
repository_id = _settings['repositories']['default_repository']
error = UGCErrorReport(repository_id)
except (OnlineMirrorError, AttributeError, ImportError,):
error = None
result = None
if error is not None:
error.prepare(exception_tback_raw, name, email,
'\n'.join([x for x in exception_data]), description)
result = error.submit()
if result:
_text.output(
darkgreen(
_("Thank you very much. The error has been "
"reported and hopefully, the problem will "
"be solved as soon as possible.")),
level="error")
else:
_text.output(
darkred(_("Ugh. Cannot send the report. "
"Please mail the file below "
"to lxnay@sabayon.org.")),
level="error")
_text.output("", level="error")
_text.output("==> %s" % (error_file,), level="error")
_text.output("", level="error")
def install_exception_handler():
sys.excepthook = handle_exception
def uninstall_exception_handler():
sys.excepthook = sys.__excepthook__
def warn_version_mismatch():
equo_ver = read_client_release()
entropy_ver = etpConst['entropyversion']
if equo_ver != entropy_ver:
print_warning("")
print_warning("%s: %s" % (
bold(_("Entropy/Equo version mismatch")),
purple(_("it could make your system explode!")),))
print_warning("(%s [equo] & %s [entropy])" % (
blue(equo_ver),
blue(entropy_ver),))
print_warning("")
def warn_live_system():
print_warning("")
print_warning(
purple(_("Entropy is running off a Live System")))
print_warning(
teal(_("Performance and stability could get"
" severely compromised")))
print_warning("")
def main():
is_color = "--color" in sys.argv
if is_color:
sys.argv.remove("--color")
if not is_color and not is_stdout_a_tty():
nocolor()
warn_version_mismatch()
install_exception_handler()
descriptors = SoloCommandDescriptor.obtain()
args_map = {}
catch_all = None
for descriptor in descriptors:
klass = descriptor.get_class()
if klass.CATCH_ALL:
catch_all = klass
args_map[klass.NAME] = klass
for alias in klass.ALIASES:
args_map[alias] = klass
args = sys.argv[1:]
# convert args to unicode, to avoid passing
# raw string stuff down to entropy layers
def _to_unicode(arg):
try:
return const_convert_to_unicode(
arg, enctype=etpConst['conf_encoding'])
except UnicodeDecodeError:
print_error("invalid argument: %s" % (arg,))
raise SystemExit(1)
args = list(map(_to_unicode, args))
is_bashcomp = False
if "--bashcomp" in args:
is_bashcomp = True
args.remove("--bashcomp")
# the first eit, because bash does:
# argv -> equo --bashcomp equo repo
# and we need to drop --bashcomp and
# argv[2]
if args:
args.pop(0)
cmd = None
last_arg = None
if args:
last_arg = args[-1]
cmd = args[0]
args = args[1:]
cmd_class = args_map.get(cmd)
yell_class = args_map.get("yell")
if cmd_class is None:
cmd_class = catch_all
cmd_obj = cmd_class(args)
if is_bashcomp:
try:
cmd_obj.bashcomp(last_arg)
except NotImplementedError:
pass
raise SystemExit(0)
# non-root users not allowed
allowed = True
if os.getuid() != 0 and \
cmd_class is not catch_all and \
not cmd_class.ALLOW_UNPRIVILEGED and \
"--help" not in args:
cmd_class = catch_all
allowed = False
if allowed:
if not cmd_class.ALLOW_UNPRIVILEGED:
if entropy.tools.islive():
warn_live_system()
func, func_args = cmd_obj.parse()
exit_st = func(*func_args)
if exit_st == -10:
# syntax error, yell at user
func, func_args = yell_class(args).parse()
func(*func_args)
raise SystemExit(10)
else:
yell_class.reset()
raise SystemExit(exit_st)
else:
# execute this anyway so that commands are
# incomplete or invalid, the command error
# message will take precedence.
_func, _func_args = cmd_obj.parse()
print_error(_("superuser access required"))
raise SystemExit(1)