Source code for kivy.lang.compiler.runtime

'''
Kv Compiler Runtime Functions
==============================

Exports functions used by the compiled code at runtime.
'''

import sys
import os
import inspect
import functools
import os.path
import importlib.machinery

__all__ = (
    'clear_kvc_cache', 'get_kvc_filename', 'load_kvc_from_file',
    'save_kvc_to_file', 'add_graphics_callback', 'process_graphics_callbacks')

_graphics_callback_linked_list = None

_kvc_cache = {}

_kvc_path = os.environ.get('KIVY_KVC_PATH', None)
if _kvc_path is not None:
    _kvc_path = os.path.abspath(_kvc_path)
_kvc_freeze = bool(int(os.environ.get('KIVY_KVC_FREEZE', 0)))

# check all the attrs of the compiled function that need to be set to match the
# original function's values. If the sets are not equal, it means we may need
# to set more values if python changed
assert set(functools.WRAPPER_ASSIGNMENTS) == {
    '__module__', '__name__', '__qualname__', '__doc__', '__annotations__'}


[docs]def clear_kvc_cache(): '''Clears the cache that stores all the compiled functions that has been imported. ''' _kvc_cache.clear()
[docs]def add_graphics_callback(item, largs): ''' Add the item to be added to the linked list of canvas instructions to be executed with all the canvas instructions by :func:`process_graphics_callbacks`. If it's already scheduled, it is not added again. This is a internal function, with non-public API. :param item: The item scheduled. :param largs: The largs of the first time the callback was scheduled. It gets forwarded to the callback when executed. ''' # it's already on the list if item[1] is not None: return global _graphics_callback_linked_list if _graphics_callback_linked_list is None: _graphics_callback_linked_list = item item[1] = largs item[2] = StopIteration else: item[1] = largs item[2] = _graphics_callback_linked_list _graphics_callback_linked_list = item
[docs]def process_graphics_callbacks(): ''' Process all the canvas callbacks that has been scheduled by :func:`add_graphics_callback`. This is a internal function, with non-public API. ''' global _graphics_callback_linked_list next_args = _graphics_callback_linked_list if next_args is None: return _graphics_callback_linked_list = None while next_args is not StopIteration: # reset things first in case its somehow rescheduled by the callback args = next_args f, largs, next_args = next_args args[1] = args[2] = None f(*largs)
[docs]def get_kvc_filename(func, flags=''): '''Returns the `kvc` file associated with the function. Specifically, it's the filename where the function's compiled kv code is stored. The file may not exist, if it has not been compiled yet.''' func_name = func.__qualname__ flags = '-{}'.format(flags) if flags else '' mod_name = inspect.getmodule(func).__name__ # generated filename is mod_name-func_qualname-flags.kvc kv_fname = '{}-{}{}.kvc'.format(mod_name, func_name, flags) if not _kvc_path: # generated filename is py_filename-func_qualname-flags.kvc func_filname = inspect.getfile(func) kv_dir = os.path.join(os.path.dirname(func_filname), '__kvcache__') kv_path = os.path.join(kv_dir, kv_fname) else: kv_path = os.path.join(_kvc_path, kv_fname) return kv_path
def get_cache_kvc_filenames(): return [mod.__file__ for mod, _ in _kvc_cache.values()]
[docs]def load_kvc_from_file( func, target_func_name=None, flags='', compile_flags=()): ''' Loads the compiled kv function from the kvc file associated with the function. This is a internal function, with non-public API. :param func: The original function, whose compiled code should be loaded. :param target_func_name: The name of the function as stored in the kvc file. Typically, it's the function's name. :param flags: Any arch flags used when saving the kvc file. This is stored in the kvc filename. :param compile_flags: Any compiler flags used when compiling the kvc file. These are stored within the compiled code, not in the filename. :return: The compiled function object. ''' if target_func_name is None: target_func_name = func.__name__ mod_name = inspect.getmodule(func).__name__ func_name = func.__qualname__ compile_flags_s = repr(compile_flags) key = mod_name, func_name, flags, compile_flags_s if key in _kvc_cache: return _kvc_cache[key] # any and all os.xxx are super slow, so fallback on last resort kv_fname = get_kvc_filename(func, flags) if not os.path.exists(kv_fname): if _kvc_freeze: raise ValueError( 'Could not find compiled file, {}, for function {}, yet ' 'KIVY_KVC_FREEZE was set so we cannot generate a compiled ' 'file'.format(kv_fname, func)) return None, None elif not _kvc_freeze and os.stat( inspect.getfile(func)).st_mtime >= os.stat(kv_fname).st_mtime: # if frozen, we don't care about file ages return None, None mod_name = '__kv{}'.format(len(_kvc_cache)) loader = importlib.machinery.SourceFileLoader(mod_name, kv_fname) mod = loader.load_module() if compile_flags_s != mod.__kv_kvc_compile_flags: del sys.modules[mod_name] if _kvc_freeze: raise ValueError( 'Compiled file, {}, for function {}, does not match flags yet ' 'KIVY_KVC_FREEZE was set so we cannot generate a new compiled ' 'file ({} != {})'.format( kv_fname, func, compile_flags_s, mod.__kv_kvc_compile_flags)) return None, None f = getattr(mod, target_func_name) for attr in functools.WRAPPER_ASSIGNMENTS: try: value = getattr(func, attr) except AttributeError: pass else: setattr(f, attr, value) _kvc_cache[key] = mod, f return _kvc_cache[key]
[docs]def save_kvc_to_file(func, src_code, flags='', compile_flags=()): ''' Saves the compiled source code generated from the function to a kvc file. This is a internal function, with non-public API. :param func: The function object from which the compiled code was generated. :param src_code: The compiled source code as string. :param flags: Any arch flags used when saving the kvc file. This is stored in the kvc filename. :param compile_flags: Any compiler flags used when compiling the kvc file. These are stored within the compiled code, not in the filename. ''' kv_fname = get_kvc_filename(func, flags) head = os.path.dirname(kv_fname) if not os.path.exists(head): os.mkdir(head) with open(kv_fname, 'w') as fh: fh.write('__kv_kvc_compile_flags = {}\n\n'.format( repr(repr(compile_flags)))) fh.write(src_code)