"""
rave Python execution environments.
"""
import threading
import types

import rave.log


## Internals.

_log = rave.log.get(__name__)
_lock = threading.Lock()
_current_envs = {}

def _identifier():
    """ Get unique identifier for all possible parallel mechanisms. """
    return threading.current_thread().ident


## API.

def current():
    """ Get current execution environment object for whatever parallel mechanism is used, or None if no environment is active. """
    with _lock:
        ident = _identifier()

        envs = _current_envs.get(ident)
        if envs:
            return envs[-1]
        return None

def push(env):
    """ Set current execution environment for whatever parallel mechanism is used. """
    with _lock:
        ident = _identifier()

        _current_envs.setdefault(ident, [])
        if _current_envs[ident]:
            current = _current_envs[ident][-1]
            current.deactivate()

        _current_envs[ident].append(env)
        env.activate()

def pop():
    """ Clear the current execution environment for whatever parallel mechanism is used. """
    with _lock:
        ident = _identifier()

        envs = _current_envs.get(ident)
        if envs:
            env = envs.pop()
            env.deactivate()

            if _current_envs[ident]:
                current = _current_envs[ident][-1]
                current.activate()

            return env

        raise ValueError('No environment to clear.')


## Execution environment.

class ExecutionEnvironment:

    def __init__(self, game=None):
        self.game = game
        self.apis = {}
        self.globals = {}
        self.locals = {}

    def __enter__(self):
        push(self)
        return self

    def __exit__(self, exctype, excval, exctb):
        pop()

    def __repr__(self):
        return '<{} for {!r}>'.format(self.__class__.__qualname__, self.game)

    def activate(self):
        """ Activate environment. """
        pass

    def deactivate(self):
        """ Deactivate environment. """
        pass

    def compile(self, code, filename='<unknown>'):
        """ Compile code for use in this environment. """
        return compile(code, 'exec', filename, dont_inherit=True, optimize=2)

    def run(self, code, filename='<unknown>'):
        """ Run code in this environment. """
        with self:
            if not isinstance(code, types.CodeType):
                code = self.compile(code, filename)

            exec(code, self.globals, self.locals)

    def run_file(self, filename):
        """ Run file system file in this environment. """
        with self.game.fs.open(filename) as f:
            code = f.read()

        return self.run(code, filename)

    def register_api(self, name, api):
        self.apis[name] = api

@@ -90,23 +90,8 @@
 g%0A%0A%0A
-## Internals.%0A%0A
 _log
@@ -141,16 +141,76 @@
 .Lock()%0A
+_ident_funcs = %5B lambda: threading.current_thread().ident %5D%0A
 _current
@@ -221,21 +221,21 @@
 s = %7B%7D%0A%0A
+%0A
 def 
-_
 identifi
@@ -328,49 +328,39 @@
 rn t
-hreading.current_thread().ident%0A%0A%0A## API.
+uple(f() for f in _ident_funcs)
 %0A%0Ade
@@ -527,33 +527,32 @@
         ident = 
-_
 identifier()%0A%0A  
@@ -781,33 +781,32 @@
         ident = 
-_
 identifier()%0A%0A  
@@ -1162,17 +1162,16 @@
 ident = 
-_
 identifi
@@ -1499,35 +1499,8 @@
 )%0A%0A%0A
-## Execution environment.%0A%0A
 clas
@@ -1523,17 +1523,16 @@
 onment:%0A
-%0A
     def 

Make execution identification pluggable.