File: C:/Users/fred/anaconda3/Lib/site-packages/spyder/app/utils.py
# -*- coding: utf-8 -*-
#
# Copyright © Spyder Project Contributors
# Licensed under the terms of the MIT License
# (see spyder/__init__.py for details)
"""Utility functions for the Spyder application."""
# Standard library imports
import glob
import logging
import os
import os.path as osp
import re
import sys
# Third-party imports
import psutil
from qtpy.QtCore import QCoreApplication, Qt
from qtpy.QtGui import QColor, QIcon, QPalette, QPixmap, QPainter, QImage
from qtpy.QtWidgets import QApplication, QSplashScreen
from qtpy.QtSvg import QSvgRenderer
# Local imports
from spyder.config.base import (
DEV, get_conf_path, get_debug_level, running_in_mac_app,
running_under_pytest)
from spyder.config.manager import CONF
from spyder.utils.external.dafsa.dafsa import DAFSA
from spyder.utils.image_path_manager import get_image_path
from spyder.utils.installers import running_installer_test
from spyder.utils.palette import QStylePalette
from spyder.utils.qthelpers import file_uri, qapplication
# For spyder-ide/spyder#7447.
try:
from qtpy.QtQuick import QQuickWindow, QSGRendererInterface
except Exception:
QQuickWindow = QSGRendererInterface = None
root_logger = logging.getLogger()
FILTER_NAMES = os.environ.get('SPYDER_FILTER_LOG', "").split(',')
FILTER_NAMES = [f.strip() for f in FILTER_NAMES]
# Keeping a reference to the original sys.exit before patching it
ORIGINAL_SYS_EXIT = sys.exit
class Spy:
"""
This is used to inject a 'spy' object in the internal console
namespace to inspect Spyder internals.
Attributes:
app Reference to main QApplication object
window Reference to spyder.MainWindow widget
"""
def __init__(self, app, window):
self.app = app
self.window = window
def __dir__(self):
return (list(self.__dict__.keys()) +
[x for x in dir(self.__class__) if x[0] != '_'])
def get_python_doc_path():
"""
Return Python documentation path
(Windows: return the PythonXX.chm path if available)
"""
if os.name == 'nt':
doc_path = osp.join(sys.prefix, "Doc")
if not osp.isdir(doc_path):
return
python_chm = [path for path in os.listdir(doc_path)
if re.match(r"(?i)Python[0-9]{3,6}.chm", path)]
if python_chm:
return file_uri(osp.join(doc_path, python_chm[0]))
else:
vinf = sys.version_info
doc_path = '/usr/share/doc/python%d.%d/html' % (vinf[0], vinf[1])
python_doc = osp.join(doc_path, "index.html")
if osp.isfile(python_doc):
return file_uri(python_doc)
def set_opengl_implementation(option):
"""
Set the OpenGL implementation used by Spyder.
See spyder-ide/spyder#7447 for the details.
"""
if option == 'software':
QCoreApplication.setAttribute(Qt.AA_UseSoftwareOpenGL)
if QQuickWindow is not None:
QQuickWindow.setSceneGraphBackend(QSGRendererInterface.Software)
elif option == 'desktop':
QCoreApplication.setAttribute(Qt.AA_UseDesktopOpenGL)
if QQuickWindow is not None:
QQuickWindow.setSceneGraphBackend(QSGRendererInterface.OpenGL)
elif option == 'gles':
QCoreApplication.setAttribute(Qt.AA_UseOpenGLES)
if QQuickWindow is not None:
QQuickWindow.setSceneGraphBackend(QSGRendererInterface.OpenGL)
def setup_logging(cli_options):
"""Setup logging with cli options defined by the user."""
if cli_options.debug_info or get_debug_level() > 0:
levels = {2: logging.INFO, 3: logging.DEBUG}
log_level = levels[get_debug_level()]
log_format = '%(asctime)s [%(levelname)s] [%(name)s] -> %(message)s'
console_filters = cli_options.filter_log.split(',')
console_filters = [x.strip() for x in console_filters]
console_filters = console_filters + FILTER_NAMES
console_filters = [x for x in console_filters if x != '']
handlers = [logging.StreamHandler()]
filepath = os.environ['SPYDER_DEBUG_FILE']
handlers.append(
logging.FileHandler(filename=filepath, mode='w+')
)
match_func = lambda x: True
if console_filters != [''] and len(console_filters) > 0:
dafsa = DAFSA(console_filters)
match_func = lambda x: (dafsa.lookup(x, stop_on_prefix=True)
is not None)
formatter = logging.Formatter(log_format)
class ModuleFilter(logging.Filter):
"""Filter messages based on module name prefix."""
def filter(self, record):
return match_func(record.name)
filter = ModuleFilter()
root_logger.setLevel(log_level)
for handler in handlers:
handler.addFilter(filter)
handler.setFormatter(formatter)
handler.setLevel(log_level)
root_logger.addHandler(handler)
def delete_debug_log_files():
"""Delete previous debug log files."""
regex = re.compile(r'.*_.*_(\d+)[.]log')
files = glob.glob(osp.join(get_conf_path('lsp_logs'), '*.log'))
for f in files:
match = regex.match(f)
if match is not None:
pid = int(match.group(1))
if not psutil.pid_exists(pid):
os.remove(f)
debug_file = os.environ['SPYDER_DEBUG_FILE']
if osp.exists(debug_file):
os.remove(debug_file)
def qt_message_handler(msg_type, msg_log_context, msg_string):
"""
Qt warning messages are intercepted by this handler.
On some operating systems, warning messages might be displayed
even if the actual message does not apply. This filter adds a
blacklist for messages that are being printed for no apparent
reason. Anything else will get printed in the internal console.
In DEV mode, all messages are printed.
"""
BLACKLIST = [
'QMainWidget::resizeDocks: all sizes need to be larger than 0',
]
if DEV or msg_string not in BLACKLIST:
print(msg_string) # spyder: test-skip
def create_splash_screen():
"""Create splash screen."""
if not running_under_pytest():
image = QImage(500, 400, QImage.Format_ARGB32_Premultiplied)
image.fill(0)
painter = QPainter(image)
renderer = QSvgRenderer(get_image_path('splash'))
renderer.render(painter)
painter.end()
pm = QPixmap.fromImage(image)
pm = pm.copy(0, 0, 500, 400)
splash = QSplashScreen(pm)
splash_font = splash.font()
splash_font.setPixelSize(14)
splash.setFont(splash_font)
else:
splash = None
return splash
def set_links_color(app):
"""
Fix color for links.
This was taken from QDarkstyle, which is MIT licensed.
"""
color = QStylePalette.COLOR_ACCENT_4
qcolor = QColor(color)
app_palette = app.palette()
app_palette.setColor(QPalette.Normal, QPalette.Link, qcolor)
app.setPalette(app_palette)
def create_application():
"""Create application and patch sys.exit."""
# Our QApplication
app = qapplication()
# --- Set application icon
app_icon = QIcon(get_image_path("spyder"))
app.setWindowIcon(app_icon)
# Required for correct icon on GNOME/Wayland:
if hasattr(app, 'setDesktopFileName'):
app.setDesktopFileName('spyder')
# ---- Monkey patching QApplication
class FakeQApplication(QApplication):
"""Spyder's fake QApplication"""
def __init__(self, args):
self = app # analysis:ignore
@staticmethod
def exec_():
"""Do nothing because the Qt mainloop is already running"""
pass
from qtpy import QtWidgets
QtWidgets.QApplication = FakeQApplication
# ---- Monkey patching sys.exit
def fake_sys_exit(arg=[]):
pass
sys.exit = fake_sys_exit
# ---- Monkey patching sys.excepthook to avoid crashes in PyQt 5.5+
def spy_excepthook(type_, value, tback):
sys.__excepthook__(type_, value, tback)
if running_installer_test():
# This will exit Spyder with exit code 1 without invoking
# macOS system dialogue window.
raise SystemExit(1)
sys.excepthook = spy_excepthook
# Removing arguments from sys.argv as in standard Python interpreter
sys.argv = ['']
return app
def create_window(WindowClass, app, splash, options, args):
"""
Create and show Spyder's main window and start QApplication event loop.
Parameters
----------
WindowClass: QMainWindow
Subclass to instantiate the Window.
app: QApplication
Instance to start the application.
splash: QSplashScreen
Splash screen instamce.
options: argparse.Namespace
Command line options passed to Spyder
args: list
List of file names passed to the Spyder executable in the
command line.
"""
# Main window
main = WindowClass(splash, options)
try:
main.setup()
except BaseException:
if main.console is not None:
try:
main.console.exit_interpreter()
except BaseException:
pass
raise
main.pre_visible_setup()
main.show()
main.post_visible_setup()
if main.console:
main.console.start_interpreter(namespace={})
main.console.set_namespace_item('spy', Spy(app=app, window=main))
# Propagate current configurations to all configuration observers
CONF.notify_all_observers()
# Don't show icons in menus for Mac
if sys.platform == 'darwin':
QCoreApplication.setAttribute(Qt.AA_DontShowIconsInMenus, True)
# Open external files with our Mac app
if running_in_mac_app():
app.sig_open_external_file.connect(main.open_external_file)
app._has_started = True
if hasattr(app, '_pending_file_open'):
if args:
args = app._pending_file_open + args
else:
args = app._pending_file_open
# Open external files passed as args
if args:
for a in args:
main.open_external_file(a)
# To give focus again to the last focused widget after restoring
# the window
app.focusChanged.connect(main.change_last_focused_widget)
if not running_under_pytest():
app.exec_()
return main