def path_join(base, path): """Joins 'base' and 'path' ('path' is interpreted as a relative path). This method is like os.path.join(), but 'path' is interpreted relatively. E.g., os.path.join('/a/b', '/c') yields '/c', but this function yields '/a/b/c'. Args: base: The base path. path: The path to append to base; this is treated as a relative path. Returns: The path obtaining by appending 'path' to 'base'. """ if os.path.isabs(path): # Remove drive letter (if we are on Windows). unused_drive, path_no_drive = os.path.splitdrive(path) # Remove leading path separator. path = path_no_drive[1:] return AbstractFileSystem.normpath(os.path.join(base, path))
def test_url_to_handler_mapping_for_course_type(): """Tests mapping of a URL to a handler for course type.""" # setup rules setup_courses('course:/a/b:/c/d, course:/e/f:/g/h') # setup helper classes class FakeHandler0(object): def __init__(self): self.app_context = None class FakeHandler1(object): def __init__(self): self.app_context = None class FakeHandler2(zipserve.ZipHandler): def __init__(self): self.app_context = None class FakeHandler3(zipserve.ZipHandler): def __init__(self): self.app_context = None class FakeHandler4(zipserve.ZipHandler): def __init__(self): self.app_context = None # Setup handler. handler0 = FakeHandler0 handler1 = FakeHandler1 handler2 = FakeHandler2 urls = [('/', handler0), ('/foo', handler1), ('/bar', handler2)] ApplicationRequestHandler.bind(urls) # Test proper handler mappings. assert_handled('/a/b', FakeHandler0) assert_handled('/a/b/', FakeHandler0) assert_handled('/a/b/foo', FakeHandler1) assert_handled('/a/b/bar', FakeHandler2) # Test partial path match. assert_handled('/a/b/foo/bee', None) assert_handled('/a/b/bar/bee', FakeHandler2) # Test assets mapping. handler = assert_handled('/a/b/assets/img/foo.png', AssetHandler) assert AbstractFileSystem.normpath( handler.app_context.get_template_home()).endswith( AbstractFileSystem.normpath('/c/d/views')) # This is allowed as we don't go out of /assets/... handler = assert_handled( '/a/b/assets/foo/../models/models.py', AssetHandler) assert AbstractFileSystem.normpath(handler.filename).endswith( AbstractFileSystem.normpath('/c/d/assets/models/models.py')) # This is not allowed as we do go out of /assets/... assert_handled('/a/b/assets/foo/../../models/models.py', None) # Test negative cases assert_handled('/foo', None) assert_handled('/baz', None) # Site 'views' and 'data' are not accessible assert_handled('/a/b/view/base.html', None) assert_handled('/a/b/data/units.csv', None) # Default mapping reset_courses() handler3 = FakeHandler3 handler4 = FakeHandler4 urls = [ ('/', handler0), ('/foo', handler1), ('/bar', handler2), ('/zip', handler3), ('/zip/a/b', handler4)] ApplicationRequestHandler.bind(urls) # Positive cases assert_handled('/', FakeHandler0) assert_handled('/foo', FakeHandler1) assert_handled('/bar', FakeHandler2) handler = assert_handled('/assets/js/main.js', AssetHandler) assert AbstractFileSystem.normpath( handler.app_context.get_template_home()).endswith( AbstractFileSystem.normpath('/views')) # Partial URL matching cases test that the most specific match is found. assert_handled('/zip', FakeHandler3) assert_handled('/zip/a', FakeHandler3) assert_handled('/zip/a/b', FakeHandler4) assert_handled('/zip/a/b/c', FakeHandler4) # Negative cases assert_handled('/baz', None) assert_handled('/favicon.ico', None) assert_handled('/e/f/index.html', None) assert_handled('/foo/foo.css', None) # Clean up. ApplicationRequestHandler.bind([])