We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
st2 python3 interpreter is falling back to importing a python2 lib that is incompatible with python3
##### SUMMARY

 One of my libraries is trying to import `cassandra.cluster`.  That library is trying to import `from concurrent.futures import ThreadPoolExecutor, FIRST_COMPLETED, wait as wait_futures` - which, as I understand, is built into python3 (and a separate library for compatibility with python3)

##### ISSUE TYPE
 - Bug Report

##### STACKSTORM VERSION
```
# st2 --version
st2 3.0.0, on Python 2.7.6
```
##### OS / ENVIRONMENT / INSTALL METHOD
Docker container running Ubuntu 14.04

##### STEPS TO REPRODUCE
Create a python runner in your python3-specific pack.  Inside the runner import cassandra libs and just create an object.
```
from cassandra.cluster import Cluster
        cluster = Cluster()
```

##### EXPECTED RESULTS
I expect the library to import and the object to initialize

##### ACTUAL RESULTS
st2 python3 falls back to python2 to import the lib and it throws an exception similar to
```
  File \"/opt/stackstorm/packs/ostk_common/actions/lib/ostkdbs.py\", line 2, in <module>
    from cassandra.cluster import Cluster
  File \"/opt/stackstorm/virtualenvs/ostk_common/lib/python3.5/site-packages/cassandra/cluster.py\", line 23, in <module>
    from concurrent.futures import ThreadPoolExecutor, FIRST_COMPLETED, wait as wait_futures
  File \"/opt/stackstorm/st2/lib/python2.7/site-packages/concurrent/futures/__init__.py\", line 8, in <module>
    from concurrent.futures._base import (FIRST_COMPLETED,
  File \"/opt/stackstorm/st2/lib/python2.7/site-packages/concurrent/futures/_base.py\", line 414
    raise exception_type, self._exception, self._traceback
                        ^
SyntaxError: invalid syntax
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `st2common/st2common/util/sandboxing.py`
Content:
```
1 # Copyright 2019 Extreme Networks, Inc.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 
15 """
16 Utility functions for our sandboxing model which is implemented on top of separate processes and
17 virtual environments.
18 """
19 
20 from __future__ import absolute_import
21 
22 import os
23 import sys
24 import fnmatch
25 from distutils.sysconfig import get_python_lib
26 
27 from oslo_config import cfg
28 
29 from st2common.constants.pack import SYSTEM_PACK_NAMES
30 from st2common.content.utils import get_pack_base_path
31 
32 __all__ = [
33     'get_sandbox_python_binary_path',
34     'get_sandbox_python_path',
35     'get_sandbox_python_path_for_python_action',
36     'get_sandbox_path',
37     'get_sandbox_virtualenv_path',
38 
39     'is_pack_virtualenv_using_python3'
40 ]
41 
42 
43 def get_sandbox_python_binary_path(pack=None):
44     """
45     Return path to the Python binary for the provided pack.
46 
47     :param pack: Pack name.
48     :type pack: ``str``
49     """
50     system_base_path = cfg.CONF.system.base_path
51     virtualenv_path = os.path.join(system_base_path, 'virtualenvs', pack)
52 
53     if pack in SYSTEM_PACK_NAMES:
54         # Use system python for "packs" and "core" actions
55         python_path = sys.executable
56     else:
57         python_path = os.path.join(virtualenv_path, 'bin/python')
58 
59     return python_path
60 
61 
62 def get_sandbox_path(virtualenv_path):
63     """
64     Return PATH environment variable value for the sandboxed environment.
65 
66     This function makes sure that virtualenv/bin directory is in the path and has precedence over
67     the global PATH values.
68 
69     Note: This function needs to be called from the parent process (one which is spawning a
70     sandboxed process).
71     """
72     sandbox_path = []
73 
74     parent_path = os.environ.get('PATH', '')
75     if not virtualenv_path:
76         return parent_path
77 
78     parent_path = parent_path.split(':')
79     parent_path = [path for path in parent_path if path]
80 
81     # Add virtualenv bin directory
82     virtualenv_bin_path = os.path.join(virtualenv_path, 'bin/')
83     sandbox_path.append(virtualenv_bin_path)
84     sandbox_path.extend(parent_path)
85 
86     sandbox_path = ':'.join(sandbox_path)
87     return sandbox_path
88 
89 
90 def get_sandbox_python_path(inherit_from_parent=True, inherit_parent_virtualenv=True):
91     """
92     Return PYTHONPATH environment variable value for the new sandboxed environment.
93 
94     This function takes into account if the current (parent) process is running under virtualenv
95     and other things like that.
96 
97     Note: This function needs to be called from the parent process (one which is spawning a
98     sandboxed process).
99 
100     :param inherit_from_parent: True to inheir PYTHONPATH from the current process.
101     :type inherit_from_parent: ``str``
102 
103     :param inherit_parent_virtualenv: True to inherit virtualenv path if the current process is
104                                       running inside virtual environment.
105     :type inherit_parent_virtualenv: ``str``
106     """
107     sandbox_python_path = []
108     parent_python_path = os.environ.get('PYTHONPATH', '')
109 
110     parent_python_path = parent_python_path.split(':')
111     parent_python_path = [path for path in parent_python_path if path]
112 
113     if inherit_from_parent:
114         sandbox_python_path.extend(parent_python_path)
115 
116     if inherit_parent_virtualenv and hasattr(sys, 'real_prefix'):
117         # We are running inside virtualenv
118         site_packages_dir = get_python_lib()
119 
120         sys_prefix = os.path.abspath(sys.prefix)
121         assert sys_prefix in site_packages_dir
122 
123         sandbox_python_path.append(site_packages_dir)
124 
125     sandbox_python_path = ':'.join(sandbox_python_path)
126     sandbox_python_path = ':' + sandbox_python_path
127     return sandbox_python_path
128 
129 
130 def get_sandbox_python_path_for_python_action(pack, inherit_from_parent=True,
131                                               inherit_parent_virtualenv=True):
132     """
133     Return sandbox PYTHONPATH for a particular Python runner action.
134 
135     Same as get_sandbox_python_path() function, but it's intended to be used for Python runner
136     actions and also takes into account if a pack virtual environment uses Python 3.
137     """
138     sandbox_python_path = get_sandbox_python_path(
139         inherit_from_parent=inherit_from_parent,
140         inherit_parent_virtualenv=inherit_parent_virtualenv)
141 
142     pack_base_path = get_pack_base_path(pack_name=pack)
143     virtualenv_path = get_sandbox_virtualenv_path(pack=pack)
144 
145     if not virtualenv_path:
146         return sandbox_python_path
147 
148     uses_python3, virtualenv_directories = is_pack_virtualenv_using_python3(pack=pack)
149     if uses_python3:
150         # Add Python 3 lib directory (lib/python3.x) in front of the PYTHONPATH. This way we avoid
151         # issues with scripts trying to use packages / modules from Python 2.7 site-packages
152         # directory instead of the versions from Python 3 stdlib.
153         pack_actions_lib_paths = os.path.join(pack_base_path, 'actions/lib/')
154         pack_virtualenv_lib_path = os.path.join(virtualenv_path, 'lib')
155         python3_lib_directory = os.path.join(pack_virtualenv_lib_path, virtualenv_directories[0])
156 
157         # Add Python 3 site-packages directory (lib/python3.x/site-packages) in front of the Python
158         # 2.7 system site-packages This is important because we want Python 3 compatible libraries
159         # to be used from the pack virtual environment and not system ones.
160         python3_site_packages_directory = os.path.join(pack_virtualenv_lib_path,
161                                                        virtualenv_directories[0],
162                                                        'site-packages')
163         sandbox_python_path = (python3_lib_directory + ':' + python3_site_packages_directory + ':' +
164                                pack_actions_lib_paths + ':' + sandbox_python_path)
165 
166     return sandbox_python_path
167 
168 
169 def is_pack_virtualenv_using_python3(pack):
170     """
171     Return True if a particular pack virtual environment is using Python 3.
172 
173     :return: (uses_python3_bool, virtualenv_lib_directories)
174     :rtype: ``tuple``
175     """
176     # If python3.? directory exists in pack virtualenv lib/ path it means Python 3 is used by
177     # that virtual environment and we take that in to account when constructing PYTHONPATH
178     virtualenv_path = get_sandbox_virtualenv_path(pack=pack)
179 
180     if virtualenv_path and os.path.isdir(virtualenv_path):
181         pack_virtualenv_lib_path = os.path.join(virtualenv_path, 'lib')
182 
183         if not os.path.exists(pack_virtualenv_lib_path):
184             return False, None
185 
186         virtualenv_directories = os.listdir(pack_virtualenv_lib_path)
187         virtualenv_directories = [dir_name for dir_name in virtualenv_directories if
188                                   fnmatch.fnmatch(dir_name, 'python3*')]
189         uses_python3 = bool(virtualenv_directories)
190     else:
191         uses_python3 = False
192         virtualenv_directories = None
193 
194     return uses_python3, virtualenv_directories
195 
196 
197 def get_sandbox_virtualenv_path(pack):
198     """
199     Return a path to the virtual environment for the provided pack.
200     """
201 
202     if pack in SYSTEM_PACK_NAMES:
203         virtualenv_path = None
204     else:
205         system_base_path = cfg.CONF.system.base_path
206         virtualenv_path = os.path.join(system_base_path, 'virtualenvs', pack)
207 
208     return virtualenv_path
209
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/st2common/st2common/util/sandboxing.py b/st2common/st2common/util/sandboxing.py
--- a/st2common/st2common/util/sandboxing.py
+++ b/st2common/st2common/util/sandboxing.py
@@ -160,8 +160,30 @@
         python3_site_packages_directory = os.path.join(pack_virtualenv_lib_path,
                                                        virtualenv_directories[0],
                                                        'site-packages')
-        sandbox_python_path = (python3_lib_directory + ':' + python3_site_packages_directory + ':' +
-                               pack_actions_lib_paths + ':' + sandbox_python_path)
+
+        # Work around to make sure we also add system lib dir to PYTHONPATH and not just virtualenv
+        # one
+        # NOTE: abc.py is always available in base lib directory which is symlinked to virtualenv
+        # lib directory
+        abc_module_path = os.path.join(python3_lib_directory, 'abc.py')
+        link_path = os.path.realpath(abc_module_path)
+        python3_system_lib_directory = os.path.dirname(link_path)
+
+        if not os.path.exists(python3_system_lib_directory):
+            python3_system_lib_directory = None
+
+        full_sandbox_python_path = []
+
+        # NOTE: Order here is very important for imports to function correctly
+        if python3_lib_directory:
+            full_sandbox_python_path.append(python3_system_lib_directory)
+
+        full_sandbox_python_path.append(python3_lib_directory)
+        full_sandbox_python_path.append(python3_site_packages_directory)
+        full_sandbox_python_path.append(pack_actions_lib_paths)
+        full_sandbox_python_path.append(sandbox_python_path)
+
+        sandbox_python_path = ':'.join(full_sandbox_python_path)
 
     return sandbox_python_path

{"golden_diff": "diff --git a/st2common/st2common/util/sandboxing.py b/st2common/st2common/util/sandboxing.py\n--- a/st2common/st2common/util/sandboxing.py\n+++ b/st2common/st2common/util/sandboxing.py\n@@ -160,8 +160,30 @@\n         python3_site_packages_directory = os.path.join(pack_virtualenv_lib_path,\n                                                        virtualenv_directories[0],\n                                                        'site-packages')\n-        sandbox_python_path = (python3_lib_directory + ':' + python3_site_packages_directory + ':' +\n-                               pack_actions_lib_paths + ':' + sandbox_python_path)\n+\n+        # Work around to make sure we also add system lib dir to PYTHONPATH and not just virtualenv\n+        # one\n+        # NOTE: abc.py is always available in base lib directory which is symlinked to virtualenv\n+        # lib directory\n+        abc_module_path = os.path.join(python3_lib_directory, 'abc.py')\n+        link_path = os.path.realpath(abc_module_path)\n+        python3_system_lib_directory = os.path.dirname(link_path)\n+\n+        if not os.path.exists(python3_system_lib_directory):\n+            python3_system_lib_directory = None\n+\n+        full_sandbox_python_path = []\n+\n+        # NOTE: Order here is very important for imports to function correctly\n+        if python3_lib_directory:\n+            full_sandbox_python_path.append(python3_system_lib_directory)\n+\n+        full_sandbox_python_path.append(python3_lib_directory)\n+        full_sandbox_python_path.append(python3_site_packages_directory)\n+        full_sandbox_python_path.append(pack_actions_lib_paths)\n+        full_sandbox_python_path.append(sandbox_python_path)\n+\n+        sandbox_python_path = ':'.join(full_sandbox_python_path)\n \n     return sandbox_python_path\n", "issue": "st2 python3 interpreter is falling back to importing a python2 lib that is incompatible with python3\n##### SUMMARY\r\n\r\n One of my libraries is trying to import `cassandra.cluster`.  That library is trying to import `from concurrent.futures import ThreadPoolExecutor, FIRST_COMPLETED, wait as wait_futures` - which, as I understand, is built into python3 (and a separate library for compatibility with python3)\r\n\r\n##### ISSUE TYPE\r\n - Bug Report\r\n\r\n##### STACKSTORM VERSION\r\n```\r\n# st2 --version\r\nst2 3.0.0, on Python 2.7.6\r\n```\r\n##### OS / ENVIRONMENT / INSTALL METHOD\r\nDocker container running Ubuntu 14.04\r\n\r\n##### STEPS TO REPRODUCE\r\nCreate a python runner in your python3-specific pack.  Inside the runner import cassandra libs and just create an object.\r\n```\r\nfrom cassandra.cluster import Cluster\r\n        cluster = Cluster()\r\n```\r\n\r\n##### EXPECTED RESULTS\r\nI expect the library to import and the object to initialize\r\n\r\n##### ACTUAL RESULTS\r\nst2 python3 falls back to python2 to import the lib and it throws an exception similar to\r\n```\r\n  File \\\"/opt/stackstorm/packs/ostk_common/actions/lib/ostkdbs.py\\\", line 2, in <module>\r\n    from cassandra.cluster import Cluster\r\n  File \\\"/opt/stackstorm/virtualenvs/ostk_common/lib/python3.5/site-packages/cassandra/cluster.py\\\", line 23, in <module>\r\n    from concurrent.futures import ThreadPoolExecutor, FIRST_COMPLETED, wait as wait_futures\r\n  File \\\"/opt/stackstorm/st2/lib/python2.7/site-packages/concurrent/futures/__init__.py\\\", line 8, in <module>\r\n    from concurrent.futures._base import (FIRST_COMPLETED,\r\n  File \\\"/opt/stackstorm/st2/lib/python2.7/site-packages/concurrent/futures/_base.py\\\", line 414\r\n    raise exception_type, self._exception, self._traceback\r\n                        ^\r\nSyntaxError: invalid syntax\r\n```\n", "before_files": [{"content": "# Copyright 2019 Extreme Networks, Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nUtility functions for our sandboxing model which is implemented on top of separate processes and\nvirtual environments.\n\"\"\"\n\nfrom __future__ import absolute_import\n\nimport os\nimport sys\nimport fnmatch\nfrom distutils.sysconfig import get_python_lib\n\nfrom oslo_config import cfg\n\nfrom st2common.constants.pack import SYSTEM_PACK_NAMES\nfrom st2common.content.utils import get_pack_base_path\n\n__all__ = [\n    'get_sandbox_python_binary_path',\n    'get_sandbox_python_path',\n    'get_sandbox_python_path_for_python_action',\n    'get_sandbox_path',\n    'get_sandbox_virtualenv_path',\n\n    'is_pack_virtualenv_using_python3'\n]\n\n\ndef get_sandbox_python_binary_path(pack=None):\n    \"\"\"\n    Return path to the Python binary for the provided pack.\n\n    :param pack: Pack name.\n    :type pack: ``str``\n    \"\"\"\n    system_base_path = cfg.CONF.system.base_path\n    virtualenv_path = os.path.join(system_base_path, 'virtualenvs', pack)\n\n    if pack in SYSTEM_PACK_NAMES:\n        # Use system python for \"packs\" and \"core\" actions\n        python_path = sys.executable\n    else:\n        python_path = os.path.join(virtualenv_path, 'bin/python')\n\n    return python_path\n\n\ndef get_sandbox_path(virtualenv_path):\n    \"\"\"\n    Return PATH environment variable value for the sandboxed environment.\n\n    This function makes sure that virtualenv/bin directory is in the path and has precedence over\n    the global PATH values.\n\n    Note: This function needs to be called from the parent process (one which is spawning a\n    sandboxed process).\n    \"\"\"\n    sandbox_path = []\n\n    parent_path = os.environ.get('PATH', '')\n    if not virtualenv_path:\n        return parent_path\n\n    parent_path = parent_path.split(':')\n    parent_path = [path for path in parent_path if path]\n\n    # Add virtualenv bin directory\n    virtualenv_bin_path = os.path.join(virtualenv_path, 'bin/')\n    sandbox_path.append(virtualenv_bin_path)\n    sandbox_path.extend(parent_path)\n\n    sandbox_path = ':'.join(sandbox_path)\n    return sandbox_path\n\n\ndef get_sandbox_python_path(inherit_from_parent=True, inherit_parent_virtualenv=True):\n    \"\"\"\n    Return PYTHONPATH environment variable value for the new sandboxed environment.\n\n    This function takes into account if the current (parent) process is running under virtualenv\n    and other things like that.\n\n    Note: This function needs to be called from the parent process (one which is spawning a\n    sandboxed process).\n\n    :param inherit_from_parent: True to inheir PYTHONPATH from the current process.\n    :type inherit_from_parent: ``str``\n\n    :param inherit_parent_virtualenv: True to inherit virtualenv path if the current process is\n                                      running inside virtual environment.\n    :type inherit_parent_virtualenv: ``str``\n    \"\"\"\n    sandbox_python_path = []\n    parent_python_path = os.environ.get('PYTHONPATH', '')\n\n    parent_python_path = parent_python_path.split(':')\n    parent_python_path = [path for path in parent_python_path if path]\n\n    if inherit_from_parent:\n        sandbox_python_path.extend(parent_python_path)\n\n    if inherit_parent_virtualenv and hasattr(sys, 'real_prefix'):\n        # We are running inside virtualenv\n        site_packages_dir = get_python_lib()\n\n        sys_prefix = os.path.abspath(sys.prefix)\n        assert sys_prefix in site_packages_dir\n\n        sandbox_python_path.append(site_packages_dir)\n\n    sandbox_python_path = ':'.join(sandbox_python_path)\n    sandbox_python_path = ':' + sandbox_python_path\n    return sandbox_python_path\n\n\ndef get_sandbox_python_path_for_python_action(pack, inherit_from_parent=True,\n                                              inherit_parent_virtualenv=True):\n    \"\"\"\n    Return sandbox PYTHONPATH for a particular Python runner action.\n\n    Same as get_sandbox_python_path() function, but it's intended to be used for Python runner\n    actions and also takes into account if a pack virtual environment uses Python 3.\n    \"\"\"\n    sandbox_python_path = get_sandbox_python_path(\n        inherit_from_parent=inherit_from_parent,\n        inherit_parent_virtualenv=inherit_parent_virtualenv)\n\n    pack_base_path = get_pack_base_path(pack_name=pack)\n    virtualenv_path = get_sandbox_virtualenv_path(pack=pack)\n\n    if not virtualenv_path:\n        return sandbox_python_path\n\n    uses_python3, virtualenv_directories = is_pack_virtualenv_using_python3(pack=pack)\n    if uses_python3:\n        # Add Python 3 lib directory (lib/python3.x) in front of the PYTHONPATH. This way we avoid\n        # issues with scripts trying to use packages / modules from Python 2.7 site-packages\n        # directory instead of the versions from Python 3 stdlib.\n        pack_actions_lib_paths = os.path.join(pack_base_path, 'actions/lib/')\n        pack_virtualenv_lib_path = os.path.join(virtualenv_path, 'lib')\n        python3_lib_directory = os.path.join(pack_virtualenv_lib_path, virtualenv_directories[0])\n\n        # Add Python 3 site-packages directory (lib/python3.x/site-packages) in front of the Python\n        # 2.7 system site-packages This is important because we want Python 3 compatible libraries\n        # to be used from the pack virtual environment and not system ones.\n        python3_site_packages_directory = os.path.join(pack_virtualenv_lib_path,\n                                                       virtualenv_directories[0],\n                                                       'site-packages')\n        sandbox_python_path = (python3_lib_directory + ':' + python3_site_packages_directory + ':' +\n                               pack_actions_lib_paths + ':' + sandbox_python_path)\n\n    return sandbox_python_path\n\n\ndef is_pack_virtualenv_using_python3(pack):\n    \"\"\"\n    Return True if a particular pack virtual environment is using Python 3.\n\n    :return: (uses_python3_bool, virtualenv_lib_directories)\n    :rtype: ``tuple``\n    \"\"\"\n    # If python3.? directory exists in pack virtualenv lib/ path it means Python 3 is used by\n    # that virtual environment and we take that in to account when constructing PYTHONPATH\n    virtualenv_path = get_sandbox_virtualenv_path(pack=pack)\n\n    if virtualenv_path and os.path.isdir(virtualenv_path):\n        pack_virtualenv_lib_path = os.path.join(virtualenv_path, 'lib')\n\n        if not os.path.exists(pack_virtualenv_lib_path):\n            return False, None\n\n        virtualenv_directories = os.listdir(pack_virtualenv_lib_path)\n        virtualenv_directories = [dir_name for dir_name in virtualenv_directories if\n                                  fnmatch.fnmatch(dir_name, 'python3*')]\n        uses_python3 = bool(virtualenv_directories)\n    else:\n        uses_python3 = False\n        virtualenv_directories = None\n\n    return uses_python3, virtualenv_directories\n\n\ndef get_sandbox_virtualenv_path(pack):\n    \"\"\"\n    Return a path to the virtual environment for the provided pack.\n    \"\"\"\n\n    if pack in SYSTEM_PACK_NAMES:\n        virtualenv_path = None\n    else:\n        system_base_path = cfg.CONF.system.base_path\n        virtualenv_path = os.path.join(system_base_path, 'virtualenvs', pack)\n\n    return virtualenv_path\n", "path": "st2common/st2common/util/sandboxing.py"}], "after_files": [{"content": "# Copyright 2019 Extreme Networks, Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nUtility functions for our sandboxing model which is implemented on top of separate processes and\nvirtual environments.\n\"\"\"\n\nfrom __future__ import absolute_import\n\nimport os\nimport sys\nimport fnmatch\nfrom distutils.sysconfig import get_python_lib\n\nfrom oslo_config import cfg\n\nfrom st2common.constants.pack import SYSTEM_PACK_NAMES\nfrom st2common.content.utils import get_pack_base_path\n\n__all__ = [\n    'get_sandbox_python_binary_path',\n    'get_sandbox_python_path',\n    'get_sandbox_python_path_for_python_action',\n    'get_sandbox_path',\n    'get_sandbox_virtualenv_path',\n\n    'is_pack_virtualenv_using_python3'\n]\n\n\ndef get_sandbox_python_binary_path(pack=None):\n    \"\"\"\n    Return path to the Python binary for the provided pack.\n\n    :param pack: Pack name.\n    :type pack: ``str``\n    \"\"\"\n    system_base_path = cfg.CONF.system.base_path\n    virtualenv_path = os.path.join(system_base_path, 'virtualenvs', pack)\n\n    if pack in SYSTEM_PACK_NAMES:\n        # Use system python for \"packs\" and \"core\" actions\n        python_path = sys.executable\n    else:\n        python_path = os.path.join(virtualenv_path, 'bin/python')\n\n    return python_path\n\n\ndef get_sandbox_path(virtualenv_path):\n    \"\"\"\n    Return PATH environment variable value for the sandboxed environment.\n\n    This function makes sure that virtualenv/bin directory is in the path and has precedence over\n    the global PATH values.\n\n    Note: This function needs to be called from the parent process (one which is spawning a\n    sandboxed process).\n    \"\"\"\n    sandbox_path = []\n\n    parent_path = os.environ.get('PATH', '')\n    if not virtualenv_path:\n        return parent_path\n\n    parent_path = parent_path.split(':')\n    parent_path = [path for path in parent_path if path]\n\n    # Add virtualenv bin directory\n    virtualenv_bin_path = os.path.join(virtualenv_path, 'bin/')\n    sandbox_path.append(virtualenv_bin_path)\n    sandbox_path.extend(parent_path)\n\n    sandbox_path = ':'.join(sandbox_path)\n    return sandbox_path\n\n\ndef get_sandbox_python_path(inherit_from_parent=True, inherit_parent_virtualenv=True):\n    \"\"\"\n    Return PYTHONPATH environment variable value for the new sandboxed environment.\n\n    This function takes into account if the current (parent) process is running under virtualenv\n    and other things like that.\n\n    Note: This function needs to be called from the parent process (one which is spawning a\n    sandboxed process).\n\n    :param inherit_from_parent: True to inheir PYTHONPATH from the current process.\n    :type inherit_from_parent: ``str``\n\n    :param inherit_parent_virtualenv: True to inherit virtualenv path if the current process is\n                                      running inside virtual environment.\n    :type inherit_parent_virtualenv: ``str``\n    \"\"\"\n    sandbox_python_path = []\n    parent_python_path = os.environ.get('PYTHONPATH', '')\n\n    parent_python_path = parent_python_path.split(':')\n    parent_python_path = [path for path in parent_python_path if path]\n\n    if inherit_from_parent:\n        sandbox_python_path.extend(parent_python_path)\n\n    if inherit_parent_virtualenv and hasattr(sys, 'real_prefix'):\n        # We are running inside virtualenv\n        site_packages_dir = get_python_lib()\n\n        sys_prefix = os.path.abspath(sys.prefix)\n        assert sys_prefix in site_packages_dir\n\n        sandbox_python_path.append(site_packages_dir)\n\n    sandbox_python_path = ':'.join(sandbox_python_path)\n    sandbox_python_path = ':' + sandbox_python_path\n    return sandbox_python_path\n\n\ndef get_sandbox_python_path_for_python_action(pack, inherit_from_parent=True,\n                                              inherit_parent_virtualenv=True):\n    \"\"\"\n    Return sandbox PYTHONPATH for a particular Python runner action.\n\n    Same as get_sandbox_python_path() function, but it's intended to be used for Python runner\n    actions and also takes into account if a pack virtual environment uses Python 3.\n    \"\"\"\n    sandbox_python_path = get_sandbox_python_path(\n        inherit_from_parent=inherit_from_parent,\n        inherit_parent_virtualenv=inherit_parent_virtualenv)\n\n    pack_base_path = get_pack_base_path(pack_name=pack)\n    virtualenv_path = get_sandbox_virtualenv_path(pack=pack)\n\n    if not virtualenv_path:\n        return sandbox_python_path\n\n    uses_python3, virtualenv_directories = is_pack_virtualenv_using_python3(pack=pack)\n    if uses_python3:\n        # Add Python 3 lib directory (lib/python3.x) in front of the PYTHONPATH. This way we avoid\n        # issues with scripts trying to use packages / modules from Python 2.7 site-packages\n        # directory instead of the versions from Python 3 stdlib.\n        pack_actions_lib_paths = os.path.join(pack_base_path, 'actions/lib/')\n        pack_virtualenv_lib_path = os.path.join(virtualenv_path, 'lib')\n        python3_lib_directory = os.path.join(pack_virtualenv_lib_path, virtualenv_directories[0])\n\n        # Add Python 3 site-packages directory (lib/python3.x/site-packages) in front of the Python\n        # 2.7 system site-packages This is important because we want Python 3 compatible libraries\n        # to be used from the pack virtual environment and not system ones.\n        python3_site_packages_directory = os.path.join(pack_virtualenv_lib_path,\n                                                       virtualenv_directories[0],\n                                                       'site-packages')\n\n        # Work around to make sure we also add system lib dir to PYTHONPATH and not just virtualenv\n        # one\n        # NOTE: abc.py is always available in base lib directory which is symlinked to virtualenv\n        # lib directory\n        abc_module_path = os.path.join(python3_lib_directory, 'abc.py')\n        link_path = os.path.realpath(abc_module_path)\n        python3_system_lib_directory = os.path.dirname(link_path)\n\n        if not os.path.exists(python3_system_lib_directory):\n            python3_system_lib_directory = None\n\n        full_sandbox_python_path = []\n\n        # NOTE: Order here is very important for imports to function correctly\n        if python3_lib_directory:\n            full_sandbox_python_path.append(python3_system_lib_directory)\n\n        full_sandbox_python_path.append(python3_lib_directory)\n        full_sandbox_python_path.append(python3_site_packages_directory)\n        full_sandbox_python_path.append(pack_actions_lib_paths)\n        full_sandbox_python_path.append(sandbox_python_path)\n\n        sandbox_python_path = ':'.join(full_sandbox_python_path)\n\n    return sandbox_python_path\n\n\ndef is_pack_virtualenv_using_python3(pack):\n    \"\"\"\n    Return True if a particular pack virtual environment is using Python 3.\n\n    :return: (uses_python3_bool, virtualenv_lib_directories)\n    :rtype: ``tuple``\n    \"\"\"\n    # If python3.? directory exists in pack virtualenv lib/ path it means Python 3 is used by\n    # that virtual environment and we take that in to account when constructing PYTHONPATH\n    virtualenv_path = get_sandbox_virtualenv_path(pack=pack)\n\n    if virtualenv_path and os.path.isdir(virtualenv_path):\n        pack_virtualenv_lib_path = os.path.join(virtualenv_path, 'lib')\n\n        if not os.path.exists(pack_virtualenv_lib_path):\n            return False, None\n\n        virtualenv_directories = os.listdir(pack_virtualenv_lib_path)\n        virtualenv_directories = [dir_name for dir_name in virtualenv_directories if\n                                  fnmatch.fnmatch(dir_name, 'python3*')]\n        uses_python3 = bool(virtualenv_directories)\n    else:\n        uses_python3 = False\n        virtualenv_directories = None\n\n    return uses_python3, virtualenv_directories\n\n\ndef get_sandbox_virtualenv_path(pack):\n    \"\"\"\n    Return a path to the virtual environment for the provided pack.\n    \"\"\"\n\n    if pack in SYSTEM_PACK_NAMES:\n        virtualenv_path = None\n    else:\n        system_base_path = cfg.CONF.system.base_path\n        virtualenv_path = os.path.join(system_base_path, 'virtualenvs', pack)\n\n    return virtualenv_path\n", "path": "st2common/st2common/util/sandboxing.py"}]}