blob: 46b50b539cd7517d158f95bea955522d6c773e33 [file] [log] [blame]
# Copyright 2017 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""LocalEnv (local environment data) is key value pairs saved in each thread.
For each thread, ``LocalEnv()`` returns a dictionary bound to current thread.
You can use ``SetLocalEnv()`` to override some entries in the dictionary, for
example::
def PrintFoo():
print LocalEnv().get('foo')
def RunTask():
with SetLocalEnv(foo=1):
PrintFoo() # 1
with SetLocalEnv(foo=2):
PrintFoo() # 2
PrintFoo() # 1
As you can see, the value will be reverted when the program leaves the ``with``
context.
In most cases, you should just add an argument for function calls, instead of
using ``LocalEnv()``. ``LocalEnv()`` is designed for variables that are thread
specific, and works like an option. For example, the utility functions often
needs to know **which device this function should use**. It could be the
station (``CreateStationInterface()``) or DUT (``CreateDUTInterface()``). We
don't want to add arguments for all utility functions, so instead we can use
``LocalEnv()`` as::
def SomeUtilityFunction():
interface = LocalEnv().get('interface', DEFAULT_INTERFACE)
# use ``interface`` to do the task
# ...
def Func():
with SetLocalEnv(interface=CreateDUTInterface()):
SomeUtilityFunction() # will perform on DUT interface
with SetLocalEnv(interface=CreateStationInterface()):
SomeUtilityFunction() # will perform on station interface
"""
import contextlib
import threading
_local_env = threading.local()
class LocalEnvException(Exception):
"""Exception for LocalEnv."""
def _InitLocalEnv():
"""Initialize the stack if it is not initialized yet."""
if not hasattr(_local_env, 'stack'):
_local_env.stack = [{}]
def LocalEnv():
"""Get a dictionary saved in current thread context."""
_InitLocalEnv()
return _local_env.stack[-1]
@contextlib.contextmanager
def SetLocalEnv(**kwargs):
_InitLocalEnv()
stack_size = len(_local_env.stack)
new_env = _local_env.stack[-1].copy()
new_env.update(kwargs)
_local_env.stack.append(new_env)
try:
yield
finally:
if len(_local_env.stack) != stack_size + 1:
raise LocalEnvException(
'mismatched number of append and pop, expected: %d, actual: %d' % (
stack_size + 1, len(_local_env.stack)))
_local_env.stack.pop()