248 lines
9.6 KiB
Python
248 lines
9.6 KiB
Python
'''
|
|
Core Abstraction
|
|
================
|
|
|
|
This module defines the abstraction layers for our core providers and their
|
|
implementations. For further information, please refer to
|
|
:ref:`architecture` and the :ref:`providers` section of the documentation.
|
|
|
|
In most cases, you shouldn't directly use a library that's already covered
|
|
by the core abstraction. Always try to use our providers first.
|
|
In case we are missing a feature or method, please let us know by
|
|
opening a new Bug report instead of relying on your library.
|
|
|
|
.. warning::
|
|
These are **not** widgets! These are just abstractions of the respective
|
|
functionality. For example, you cannot add a core image to your window.
|
|
You have to use the image **widget** class instead. If you're really
|
|
looking for widgets, please refer to :mod:`kivy.uix` instead.
|
|
'''
|
|
|
|
|
|
import os
|
|
import sysconfig
|
|
import sys
|
|
import traceback
|
|
import tempfile
|
|
import subprocess
|
|
import importlib
|
|
import kivy
|
|
from kivy.logger import Logger
|
|
|
|
|
|
class CoreCriticalException(Exception):
|
|
pass
|
|
|
|
|
|
def core_select_lib(category, llist, create_instance=False,
|
|
base='kivy.core', basemodule=None):
|
|
if 'KIVY_DOC' in os.environ:
|
|
return
|
|
category = category.lower()
|
|
basemodule = basemodule or category
|
|
libs_ignored = []
|
|
errs = []
|
|
for option, modulename, classname in llist:
|
|
try:
|
|
# module activated in config ?
|
|
try:
|
|
if option not in kivy.kivy_options[category]:
|
|
libs_ignored.append(modulename)
|
|
Logger.debug(
|
|
'{0}: Provider <{1}> ignored by config'.format(
|
|
category.capitalize(), option))
|
|
continue
|
|
except KeyError:
|
|
pass
|
|
|
|
# import module
|
|
mod = importlib.__import__(name='{2}.{0}.{1}'.format(
|
|
basemodule, modulename, base),
|
|
globals=globals(),
|
|
locals=locals(),
|
|
fromlist=[modulename], level=0)
|
|
cls = mod.__getattribute__(classname)
|
|
|
|
# ok !
|
|
Logger.info('{0}: Provider: {1}{2}'.format(
|
|
category.capitalize(), option,
|
|
'({0} ignored)'.format(libs_ignored) if libs_ignored else ''))
|
|
if create_instance:
|
|
cls = cls()
|
|
return cls
|
|
|
|
except ImportError as e:
|
|
errs.append((option, e, sys.exc_info()[2]))
|
|
libs_ignored.append(modulename)
|
|
Logger.debug('{0}: Ignored <{1}> (import error)'.format(
|
|
category.capitalize(), option))
|
|
Logger.trace('', exc_info=e)
|
|
|
|
except CoreCriticalException as e:
|
|
errs.append((option, e, sys.exc_info()[2]))
|
|
Logger.error('{0}: Unable to use {1}'.format(
|
|
category.capitalize(), option))
|
|
Logger.error(
|
|
'{0}: The module raised an important error: {1!r}'.format(
|
|
category.capitalize(), e.message))
|
|
raise
|
|
|
|
except Exception as e:
|
|
errs.append((option, e, sys.exc_info()[2]))
|
|
libs_ignored.append(modulename)
|
|
Logger.trace('{0}: Unable to use {1}'.format(
|
|
category.capitalize(), option))
|
|
Logger.trace('', exc_info=e)
|
|
|
|
err = '\n'.join(['{} - {}: {}\n{}'.format(opt, e.__class__.__name__, e,
|
|
''.join(traceback.format_tb(tb))) for opt, e, tb in errs])
|
|
Logger.critical(
|
|
'{0}: Unable to find any valuable {0} provider. Please enable '
|
|
'debug logging (e.g. add -d if running from the command line, or '
|
|
'change the log level in the config) and re-run your app to '
|
|
'identify potential causes\n{1}'.format(category.capitalize(), err))
|
|
|
|
|
|
def core_register_libs(category, libs, base='kivy.core'):
|
|
if 'KIVY_DOC' in os.environ:
|
|
return
|
|
category = category.lower()
|
|
kivy_options = kivy.kivy_options[category]
|
|
libs_loadable = {}
|
|
libs_ignored = []
|
|
|
|
for option, lib in libs:
|
|
# module activated in config ?
|
|
if option not in kivy_options:
|
|
Logger.debug('{0}: option <{1}> ignored by config'.format(
|
|
category.capitalize(), option))
|
|
libs_ignored.append(lib)
|
|
continue
|
|
libs_loadable[option] = lib
|
|
|
|
libs_loaded = []
|
|
for item in kivy_options:
|
|
try:
|
|
# import module
|
|
try:
|
|
lib = libs_loadable[item]
|
|
except KeyError:
|
|
continue
|
|
importlib.__import__(name='{2}.{0}.{1}'.format(category, lib, base),
|
|
globals=globals(),
|
|
locals=locals(),
|
|
fromlist=[lib],
|
|
level=0)
|
|
|
|
libs_loaded.append(lib)
|
|
|
|
except Exception as e:
|
|
Logger.trace('{0}: Unable to use <{1}> as loader!'.format(
|
|
category.capitalize(), option))
|
|
Logger.trace('', exc_info=e)
|
|
libs_ignored.append(lib)
|
|
|
|
Logger.info('{0}: Providers: {1} {2}'.format(
|
|
category.capitalize(),
|
|
', '.join(libs_loaded),
|
|
'({0} ignored)'.format(
|
|
', '.join(libs_ignored)) if libs_ignored else ''))
|
|
return libs_loaded
|
|
|
|
|
|
def handle_win_lib_import_error(category, provider, mod_name):
|
|
if sys.platform != 'win32':
|
|
return
|
|
|
|
assert mod_name.startswith('kivy.')
|
|
kivy_root = os.path.dirname(kivy.__file__)
|
|
dirs = mod_name[5:].split('.')
|
|
mod_path = os.path.join(kivy_root, *dirs)
|
|
|
|
# get the full expected path to the compiled pyd file
|
|
# filename is <debug>.cp<major><minor>-<platform>.pyd
|
|
# https://github.com/python/cpython/blob/master/Doc/whatsnew/3.5.rst
|
|
if hasattr(sys, 'gettotalrefcount'): # debug
|
|
mod_path += '._d'
|
|
mod_path += '.cp{}{}-{}.pyd'.format(
|
|
sys.version_info.major, sys.version_info.minor,
|
|
sysconfig.get_platform().replace('-', '_'))
|
|
|
|
# does the compiled pyd exist at all?
|
|
if not os.path.exists(mod_path):
|
|
Logger.debug(
|
|
'{}: Failed trying to import "{}" for provider {}. Compiled file '
|
|
'does not exist. Have you perhaps forgotten to compile Kivy, or '
|
|
'did not install all required dependencies?'.format(
|
|
category, provider, mod_path))
|
|
return
|
|
|
|
# tell user to provide dependency walker
|
|
env_var = 'KIVY_{}_DEPENDENCY_WALKER'.format(provider.upper())
|
|
if env_var not in os.environ:
|
|
Logger.debug(
|
|
'{0}: Failed trying to import the "{1}" provider from "{2}". '
|
|
'This error is often encountered when a dependency is missing,'
|
|
' or if there are multiple copies of the same dependency dll on '
|
|
'the Windows PATH and they are incompatible with each other. '
|
|
'This can occur if you are mixing installations (such as different'
|
|
' python installations, like anaconda python and a system python) '
|
|
'or if another unrelated program added its directory to the PATH. '
|
|
'Please examine your PATH and python installation for potential '
|
|
'issues. To further troubleshoot a "DLL load failed" error, '
|
|
'please download '
|
|
'"Dependency Walker" (64 or 32 bit version - matching your python '
|
|
'bitness) from dependencywalker.com and set the environment '
|
|
'variable {3} to the full path of the downloaded depends.exe file '
|
|
'and rerun your application to generate an error report'.
|
|
format(category, provider, mod_path, env_var))
|
|
return
|
|
|
|
depends_bin = os.environ[env_var]
|
|
if not os.path.exists(depends_bin):
|
|
raise ValueError('"{}" provided in {} does not exist'.format(
|
|
depends_bin, env_var))
|
|
|
|
# make file for the resultant log
|
|
fd, temp_file = tempfile.mkstemp(
|
|
suffix='.dwi', prefix='kivy_depends_{}_log_'.format(provider),
|
|
dir=os.path.expanduser('~/'))
|
|
os.close(fd)
|
|
|
|
Logger.info(
|
|
'{}: Running dependency walker "{}" on "{}" to generate '
|
|
'troubleshooting log. Please wait for it to complete'.format(
|
|
category, depends_bin, mod_path))
|
|
Logger.debug(
|
|
'{}: Dependency walker command is "{}"'.format(
|
|
category,
|
|
[depends_bin, '/c', '/od:{}'.format(temp_file), mod_path]))
|
|
|
|
try:
|
|
subprocess.check_output([
|
|
depends_bin, '/c', '/od:{}'.format(temp_file), mod_path])
|
|
except subprocess.CalledProcessError as exc:
|
|
if exc.returncode >= 0x00010000:
|
|
Logger.error(
|
|
'{}: Dependency walker failed with error code "{}". No '
|
|
'error report was generated'.
|
|
format(category, exc.returncode))
|
|
return
|
|
|
|
Logger.info(
|
|
'{}: dependency walker generated "{}" containing troubleshooting '
|
|
'information about provider {} and its failing file "{} ({})". You '
|
|
'can open the file in dependency walker to view any potential issues '
|
|
'and troubleshoot it yourself. '
|
|
'To share the file with the Kivy developers and request support, '
|
|
'please contact us at our support channels '
|
|
'https://kivy.org/doc/master/contact.html (not on github, unless '
|
|
'it\'s truly a bug). Make sure to provide the generated file as well '
|
|
'as the *complete* Kivy log being printed here. Keep in mind the '
|
|
'generated dependency walker log file contains paths to dlls on your '
|
|
'system used by kivy or its dependencies to help troubleshoot them, '
|
|
'and these paths may include your name in them. Please view the '
|
|
'log file in dependency walker before sharing to ensure you are not '
|
|
'sharing sensitive paths'.format(
|
|
category, temp_file, provider, mod_name, mod_path))
|