Don't use the locals parameter on exec. (#29979)
Using the locals parameter makes it so that the code of a Python script runs as if it were in the body of a ``class``. One effect of this is that functions defined as part of a script cannot call one another directly. Fixes: #24704, #13653
This commit is contained in:
parent
b058742404
commit
33cbb398ad
2 changed files with 28 additions and 3 deletions
|
@ -175,6 +175,7 @@ def execute(hass, filename, source, data=None):
|
|||
builtins["sorted"] = sorted
|
||||
builtins["time"] = TimeWrapper()
|
||||
builtins["dt_util"] = dt_util
|
||||
logger = logging.getLogger(f"{__name__}.{filename}")
|
||||
restricted_globals = {
|
||||
"__builtins__": builtins,
|
||||
"_print_": StubPrinter,
|
||||
|
@ -184,14 +185,15 @@ def execute(hass, filename, source, data=None):
|
|||
"_getitem_": default_guarded_getitem,
|
||||
"_iter_unpack_sequence_": guarded_iter_unpack_sequence,
|
||||
"_unpack_sequence_": guarded_unpack_sequence,
|
||||
"hass": hass,
|
||||
"data": data or {},
|
||||
"logger": logger,
|
||||
}
|
||||
logger = logging.getLogger(f"{__name__}.{filename}")
|
||||
local = {"hass": hass, "data": data or {}, "logger": logger}
|
||||
|
||||
try:
|
||||
_LOGGER.info("Executing %s: %s", filename, data)
|
||||
# pylint: disable=exec-used
|
||||
exec(compiled.code, restricted_globals, local)
|
||||
exec(compiled.code, restricted_globals)
|
||||
except ScriptError as err:
|
||||
logger.error("Error executing script: %s", err)
|
||||
except Exception as err: # pylint: disable=broad-except
|
||||
|
|
|
@ -258,6 +258,29 @@ hass.states.set('module.datetime',
|
|||
assert caplog.text == ""
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def test_execute_functions(hass, caplog):
|
||||
"""Test functions defined in script can call one another."""
|
||||
caplog.set_level(logging.ERROR)
|
||||
source = """
|
||||
def a():
|
||||
hass.states.set('hello.a', 'one')
|
||||
|
||||
def b():
|
||||
a()
|
||||
hass.states.set('hello.b', 'two')
|
||||
|
||||
b()
|
||||
"""
|
||||
hass.async_add_job(execute, hass, "test.py", source, {})
|
||||
yield from hass.async_block_till_done()
|
||||
|
||||
assert hass.states.is_state("hello.a", "one")
|
||||
assert hass.states.is_state("hello.b", "two")
|
||||
# No errors logged = good
|
||||
assert caplog.text == ""
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def test_reload(hass):
|
||||
"""Test we can re-discover scripts."""
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue