Bug 1452896 [wpt PR 10397] - Add python handler directory to Python path in wptserve, a=testonly
authorjgraham <james@hoppipolla.co.uk>
Thu, 19 Apr 2018 15:38:08 +0000
changeset 468421 4223e774159b88711cf147f8178eb9ab226709b4
parent 468420 2458ceba3304b0d3acd6dc221936e5639aafbb77
child 468422 9a95926b64a1f531ed0ed124e366f6115ce5d240
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstestonly
bugs1452896, 10397
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1452896 [wpt PR 10397] - Add python handler directory to Python path in wptserve, a=testonly Automatic update from web-platform-testsAdd python handler directory to Python path in wptserve (#10397) Currently making import of files relative to the handler is tricky because, unlike in normal Python, the script directory isn't on the path. This patch adds it to the path when executing the handler. It also ensures that any modifications made to the path or to sys.modules are rolled back after the handler has run. -- wpt-commits: b3b6ea0b307a2a47cb19d1e843c7344769c410d9 wpt-pr: 10397 wpt-commits: b3b6ea0b307a2a47cb19d1e843c7344769c410d9 wpt-pr: 10397
testing/web-platform/tests/tools/wptserve/tests/functional/docroot/subdir/example_module.py
testing/web-platform/tests/tools/wptserve/tests/functional/docroot/subdir/import_handler.py
testing/web-platform/tests/tools/wptserve/tests/functional/test_handlers.py
testing/web-platform/tests/tools/wptserve/wptserve/handlers.py
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/tools/wptserve/tests/functional/docroot/subdir/example_module.py
@@ -0,0 +1,2 @@
+def module_function():
+    return [("Content-Type", "text/plain")], "PASS"
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/tools/wptserve/tests/functional/docroot/subdir/import_handler.py
@@ -0,0 +1,5 @@
+import example_module
+
+
+def main(request, response):
+    return example_module.module_function()
--- a/testing/web-platform/tests/tools/wptserve/tests/functional/test_handlers.py
+++ b/testing/web-platform/tests/tools/wptserve/tests/functional/test_handlers.py
@@ -1,10 +1,11 @@
 import json
 import os
+import sys
 import unittest
 import uuid
 
 import pytest
 from six.moves.urllib.error import HTTPError
 
 wptserve = pytest.importorskip("wptserve")
 from .base import TestUsingServer, doc_root
@@ -223,16 +224,17 @@ class TestJSONHandler(TestUsingServer):
         route = ("GET", "/test/test_json_tuple_2", handler)
         self.server.router.register(*route)
         resp = self.request(route[1])
         self.assertEqual(202, resp.getcode())
         self.assertEqual("Giraffe", resp.msg)
         self.assertEqual("test-value", resp.info()["test-header"])
         self.assertEqual({"data": "test data"}, json.load(resp))
 
+
 class TestPythonHandler(TestUsingServer):
     def test_string(self):
         resp = self.request("/test_string.py")
         self.assertEqual(200, resp.getcode())
         self.assertEqual("text/plain", resp.info()["Content-Type"])
         self.assertEqual("PASS", resp.read())
 
     def test_tuple_2(self):
@@ -245,16 +247,27 @@ class TestPythonHandler(TestUsingServer)
     def test_tuple_3(self):
         resp = self.request("/test_tuple_3.py")
         self.assertEqual(202, resp.getcode())
         self.assertEqual("Giraffe", resp.msg)
         self.assertEqual("text/html", resp.info()["Content-Type"])
         self.assertEqual("PASS", resp.info()["X-Test"])
         self.assertEqual("PASS", resp.read())
 
+    def test_import(self):
+        dir_name = os.path.join(doc_root, "subdir")
+        assert dir_name not in sys.path
+        assert "test_module" not in sys.modules
+        resp = self.request("/subdir/import_handler.py")
+        assert dir_name not in sys.path
+        assert "test_module" not in sys.modules
+        self.assertEqual(200, resp.getcode())
+        self.assertEqual("text/plain", resp.info()["Content-Type"])
+        self.assertEqual("PASS", resp.read())
+
     def test_no_main(self):
         with pytest.raises(HTTPError) as cm:
             self.request("/no_main.py")
 
         assert cm.value.code == 500
 
     def test_invalid(self):
         with pytest.raises(HTTPError) as cm:
--- a/testing/web-platform/tests/tools/wptserve/wptserve/handlers.py
+++ b/testing/web-platform/tests/tools/wptserve/wptserve/handlers.py
@@ -1,11 +1,12 @@
 import cgi
 import json
 import os
+import sys
 import traceback
 
 from six.moves.urllib.parse import parse_qs, quote, unquote, urljoin
 
 from .constants import content_types
 from .pipes import Pipeline, template
 from .ranges import RangeParser
 from .request import Authentication
@@ -226,27 +227,34 @@ class PythonScriptHandler(object):
         self.url_base = url_base
 
     def __repr__(self):
         return "<%s base_path:%s url_base:%s>" % (self.__class__.__name__, self.base_path, self.url_base)
 
     def __call__(self, request, response):
         path = filesystem_path(self.base_path, request, self.url_base)
 
+        sys_path = sys.path[:]
+        sys_modules = sys.modules.copy()
         try:
             environ = {"__file__": path}
+            sys.path.insert(0, os.path.dirname(path))
             execfile(path, environ, environ)
             if "main" in environ:
                 handler = FunctionHandler(environ["main"])
                 handler(request, response)
                 wrap_pipeline(path, request, response)
             else:
                 raise HTTPException(500, "No main function in script %s" % path)
         except IOError:
             raise HTTPException(404)
+        finally:
+            sys.path = sys_path
+            sys.modules = sys_modules
+
 
 python_script_handler = PythonScriptHandler()
 
 class FunctionHandler(object):
     def __init__(self, func):
         self.func = func
 
     def __call__(self, request, response):