Source code for friendly_traceback.editors_helpers

"""
editors_helpers.py
------------------

The functions in this module have been created so that user editors/IDEs
could use Friendly without having to change the content of
their own programs.

None of these are part of the public API.

If you make use of any other function here, please file an issue so
it can be determined if it should be added to the public API.
"""
import sys
from typing import Any, Dict, Tuple, Union

from .config import session
from .ft_gettext import current_lang
from .source_cache import cache


[docs]def check_syntax( *, source=None, filename="Fake_filename", path=None, include=None, lang=None ) -> Union[bool, Tuple[Any, str]]: """This uses Python's ``compile()`` builtin which does some analysis of its code argument and will raise an exception if it identifies some syntax errors, but also some less common "overflow" and "value" errors. Note that there are a few syntax errors that are not caught by this, as they are identified by Python very late in its execution process. See for example `this blog post <https://aroberge.blogspot.com/2019/12/a-tiny-python-exception-oddity.html>`_ This function can either be used on a file, using the ``path`` argument, or on some code passed as a string, using the ``source`` argument. For the latter case, one can also specify a corresponding ``filename``: this could be useful if this function is invoked from a GUI-based editor. Note that the ``path`` argument, if provided, takes precedence over the ``source`` argument. Two additional named arguments, ``include`` and ``lang``, can be provided to temporarily set the values to be used during this function call. The original values are restored at the end. If friendly exception hook has not been set up prior to calling check_syntax, it will only be used for the duration of this function call. Returns a tuple containing a code object and a filename if no exception has been raised, False otherwise. """ _ = current_lang.translate saved_except_hook, saved_include = _save_settings() saved_lang = _temp_set_lang(lang) if path is not None: try: with open(path, encoding="utf8") as f: source = f.read() filename = path except Exception: # noqa # Do not show the Python traceback which would include # the call to open() in the traceback if include is None: session.set_include("no_tb") else: session.set_include(include) session.explain_traceback() _reset(saved_except_hook, saved_lang, saved_include) return False cache.add(filename, source) try: code = compile(source, filename, "exec") except Exception: # noqa if include is None: session.set_include("explain") # our default else: session.set_include(include) session.explain_traceback() _reset(saved_except_hook, saved_lang, saved_include) return "" _reset(saved_except_hook, saved_lang, saved_include) return code
[docs]def exec_code(*, source=None, path=None, include=None, lang=None) -> Dict: """This uses check_syntax to see if the code is valid and, if so, executes it into a globals dict containing only ``{"__name__": "__main__"}``. If no ``SyntaxError`` exception is raised, this dict is returned; otherwise, an empty dict is returned. It can either be used on a file, using the ``path`` argument, or on some code passed as a string, using the ``source`` argument. Note that the ``path`` argument, if provided, takes precedence over the ``source`` argument. Two additional named arguments, ``include`` and ``lang``, can be provided to temporarily set the values to be used during this function call. The original values are restored at the end. If friendly exception hook has not been set up prior to calling check_syntax, it will only be used for the duration of this function call. """ code = check_syntax(source=source, path=path, include=include, lang=lang) if not code: return {} saved_except_hook, saved_include = _save_settings() saved_lang = _temp_set_lang(lang) module_globals = {"__name__": "__main__"} try: exec(code, module_globals) except Exception: # noqa if include is None: session.set_include("explain") # our default else: session.set_include(include) session.explain_traceback() _reset(saved_except_hook, saved_lang, saved_include) return module_globals _reset(saved_except_hook, saved_lang, saved_include) return module_globals
def _temp_set_lang(lang: str) -> str: """If lang is not none, temporarily set session.lang to the provided value. Keep track of the original lang setting and return it. A value of None for saved_lang indicates that no resetting will be required. """ saved_lang = None if lang is not None: saved_lang = session.lang if saved_lang != lang: session.install_gettext(lang) else: saved_lang = None return saved_lang def _save_settings(): current_except_hook = sys.excepthook current_include = session.include return current_except_hook, current_include def _reset(saved_except_hook, saved_lang, saved_include): """Resets both include and lang to their original values""" if saved_lang is not None: session.install_gettext(saved_lang) session.set_include(saved_include) # set_include(0) restores sys.excepthook to sys.__excepthook__ # which might not be what is wanted. So, we reset sys.excepthook last sys.excepthook = saved_except_hook