def callView(self, config, path): from pyramid.router import Router from pyramid.testing import DummyRequest router = Router(config.registry) request = DummyRequest(path=path, registry=config.registry) return router.handle_request(request)
def build(self): with main_module(self.site): self.registry.notify(PreBuild(self)) paths = self.get_paths() router = Router(self.registry) extensions = self.registry.queryUtility(IRequestExtensions) for path in paths: request = _make_request(path, registry=self.registry) if extensions is not None: request._set_extensions(extensions) response = router.handle_request(request) self.write(path, response)
def callView(self, config, path, matchdict=None, GET=None, POST=None, iface=None): from pyramid.router import Router from pyramid.testing import DummyRequest router = Router(config.registry) request = DummyRequest(path=path, registry=config.registry) request.matchdict = matchdict or {} if GET: request.GET = GET if POST: request.POST = POST if iface: directlyProvides(request, iface) return router.handle_request(request)
def test_request_validation_error(self) -> None: """Request validation errors are rendered as 400 JSON responses.""" self._add_view() # run request through router router = Router(self.config.registry) environ = { "wsgi.url_scheme": "http", "SERVER_NAME": "localhost", "SERVER_PORT": "8080", "REQUEST_METHOD": "GET", "PATH_INFO": "/foo", "HTTP_ACCEPT": "application/json", } start_response = DummyStartResponse() with self.assertLogs(level="INFO") as cm: response = router(environ, start_response) self.assertEqual(start_response.status, "400 Bad Request") self.assertEqual( json.loads(response[0]), [ { "exception": "MissingRequiredParameter", "message": "Missing required parameter: bar", "field": "bar", } ], ) self.assertEqual( cm.output, ["INFO:pyramid_openapi3:Missing required parameter: bar"] )
def test_response_validation_error(self) -> None: """Test View raises ResponseValidationError. Example view raises an undefined response code. The response validation tween should catch this as response validation error, and return an error 500. """ from pyramid.httpexceptions import HTTPPreconditionFailed self._add_view(lambda *arg: (_ for _ in ()).throw(HTTPPreconditionFailed())) self._add_default_exception_view() # run request through router router = Router(self.config.registry) environ = { "wsgi.url_scheme": "http", "SERVER_NAME": "localhost", "SERVER_PORT": "8080", "REQUEST_METHOD": "GET", "PATH_INFO": "/foo", "HTTP_ACCEPT": "application/json", "QUERY_STRING": "bar=1", } start_response = DummyStartResponse() response = router(environ, start_response) self.assertEqual(start_response.status, "500 Internal Server Error") self.assertIn( "Unknown response http status: 412", json.loads(b"".join(response))["message"], )
def test_nonapi_view_exception(self) -> None: """Test View without openapi validation raises HTTPException. Example view raises a defined response code. """ from pyramid.httpexceptions import HTTPBadRequest def view_func(*args): raise HTTPBadRequest("bad foo request") self._add_view(view_func, openapi=False) self._add_default_exception_view() # run request through router router = Router(self.config.registry) environ = { "wsgi.url_scheme": "http", "SERVER_NAME": "localhost", "SERVER_PORT": "8080", "REQUEST_METHOD": "GET", "PATH_INFO": "/foo", } start_response = DummyStartResponse() response = router(environ, start_response) self.assertEqual(start_response.status, "400 Bad Request") self.assertIn(b"foo", b"".join(response))
def test_no_default_exception_view(self) -> None: """Test Response Validation Error without default exception view. This causes the ResponseValidationError to bubble up to the top, because there is no view to render the HTTPException into a response. """ from pyramid.httpexceptions import HTTPPreconditionFailed from pyramid_openapi3.exceptions import ResponseValidationError # return the exception, so that response validation can kick in self._add_view(lambda *arg: HTTPPreconditionFailed()) # run request through router router = Router(self.config.registry) environ = { "wsgi.url_scheme": "http", "SERVER_NAME": "localhost", "SERVER_PORT": "8080", "REQUEST_METHOD": "GET", "PATH_INFO": "/foo", "QUERY_STRING": "bar=1", } start_response = DummyStartResponse() with self.assertRaises(ResponseValidationError) as cm: router(environ, start_response) self.assertEqual(cm.exception.status_code, 500) self.assertEqual("Unknown response http status: 412", str(cm.exception))
def build(self): with main_module(self.site): __import__("__main__.config") config = self.site.config.config self.registry = config.registry config.add_request_method(static_path) config.add_request_method(relroute_path) config.commit() self.registry['path'] = self.path self.siteconfig = config.registry.queryUtility( IConfigFactory, default=DefaultConfigFactory)(self.registry) self.siteconfig.setdefault('site', {}) self.siteconfig['site'].setdefault('outpath', 'output') self.registry['root'] = config.registry.queryUtility(IRootFactory) self.registry['siteconfig'] = self.siteconfig self.registry.registerUtility(lambda h, r: h, ITweens) written_paths = set() self.registry.notify(PreBuild(self)) paths = self.get_paths() router = Router(self.registry) extensions = self.registry.queryUtility(IRequestExtensions) for path in paths: request = _make_request(path, registry=self.registry) self.threadlocal_manager.push(dict( registry=self.registry, request=request)) try: if extensions is not None: request._set_extensions(extensions) response = router.handle_request(request) finally: self.threadlocal_manager.pop() written_paths.add(self.write(path[1:], response)) all_paths = dirtools.Dir(self.siteconfig['site']['outpath']) for path in set(all_paths.files()).difference(written_paths): fn = os.path.join(self.siteconfig['site']['outpath'], path) log.info("Deleting '%s'." % path) os.unlink(fn) for path in all_paths.subdirs(sort_reverse=True): fn = os.path.join(self.siteconfig['site']['outpath'], path) try: os.rmdir(fn) log.info("Removed '%s'." % path) except OSError as ex: if ex.errno != errno.ENOTEMPTY: raise
def test_request_validation_error_causes_response_validation_error(self) -> None: """Tests fallback for when request validation is disallowed by the spec. When a request fails validation an exception is raised which causes a 400 error to be raised to the end user specifying what the problem was. If the API spec disallows 400 errors, this will be transformed into a 500 error with a response validation message about incorrect types. We should also raise a warning in this case, so developers are alerted to such problems. """ self._add_view() # run request through router router = Router(self.config.registry) environ = { "wsgi.url_scheme": "http", "SERVER_NAME": "localhost", "SERVER_PORT": "8080", "REQUEST_METHOD": "GET", "PATH_INFO": "/foo", "HTTP_ACCEPT": "application/json", "QUERY_STRING": "unknown=1", } start_response = DummyStartResponse() with self.assertLogs(level="ERROR") as cm: with warnings.catch_warnings(record=True) as cw: response = router(environ, start_response) self.assertEqual(len(cw), 1) self.assertEqual( str(cw[0].message), 'Discarding 400 Bad Request validation error with body [{"exception":"MissingRequiredParameter","message":"Missing required parameter: bar","field":"bar"}] as it is not a valid response for GET to /foo (foo)', ) self.assertEqual( cm.output, ["ERROR:pyramid_openapi3:Unknown response http status: 400"] ) self.assertEqual(start_response.status, "500 Internal Server Error") if openapi_core.__version__ == "0.13.8": # pragma: no cover self.assertEqual( json.loads(response[0]), [ { "exception": "ResponseNotFound", "message": "Unknown response http status: 400", } ], ) else: # pragma: no cover self.assertEqual( json.loads(response[0]), [ { "exception": "InvalidResponse", "message": "Unknown response http status: 400", } ], )
def test_add_spec_view_directory() -> None: """Test registration of a view that serves the openapi document.""" with testConfig() as config: config.include("pyramid_openapi3") with tempfile.TemporaryDirectory() as directory: spec_name = os.path.join(directory, "openapi.yaml") spec_paths_name = os.path.join(directory, "paths.yaml") with open(spec_name, "wb") as f: f.write(SPLIT_DOCUMENT) with open(spec_paths_name, "wb") as f: f.write(SPLIT_DOCUMENT_PATHS) config.pyramid_openapi3_spec_directory(spec_name, route="/foo", route_name="foo_api_spec") # assert settings openapi_settings = config.registry.settings["pyramid_openapi3"] assert openapi_settings["filepath"] == spec_name assert openapi_settings["spec_route_name"] == "foo_api_spec" assert openapi_settings["spec"].info.title == "Foo API" assert "get" in openapi_settings["spec"].paths["/foo"].operations assert isinstance(openapi_settings["request_validator"], RequestValidator) assert isinstance(openapi_settings["response_validator"], ResponseValidator) # assert route # routes[0] is the static view, routes[1] is the route mapper = config.registry.getUtility(IRoutesMapper) routes = mapper.get_routes() assert routes[0].name == "__/foo/" assert routes[0].path == "/foo/*subpath" assert routes[1].name == "foo_api_spec" assert routes[1].path == "/foo/openapi.yaml" # assert view route_request = config.registry.queryUtility(IRouteRequest, name="foo_api_spec") static_request = config.registry.queryUtility(IRouteRequest, name="__/foo/") view = config.registry.adapters.registered( (IViewClassifier, static_request, Interface), IView, name="") assert route_request is not None assert static_request is not None assert view is not None # assert router router = Router(config.registry) response = router({"PATH_INFO": "/foo/openapi.yaml"}, DummyStartResponse()) assert next(response) == SPLIT_DOCUMENT response = router({"PATH_INFO": "/foo/paths.yaml"}, DummyStartResponse()) assert next(response) == SPLIT_DOCUMENT_PATHS
def build_traverse_trees(self): """Build all traversed hierarchies.""" mapper = self.get_mapper() router = Router(self.request.registry) routes = mapper.get_routes(include_static=False) for route in routes: if not self.is_traversable_sitemap_route(route): continue root_context = self.get_traverse_endpoint_context(router, route) self.recurse_traversable(router, route, root_context)
def test_response_validation_error(self) -> None: """Test View raises ResponseValidationError. Example view raises an undefined response code. The response validation tween should catch this as response validation error, and return an error 500. """ from pyramid.httpexceptions import HTTPPreconditionFailed self._add_view(lambda *arg: HTTPPreconditionFailed()) # run request through router router = Router(self.config.registry) environ = { "wsgi.url_scheme": "http", "SERVER_NAME": "localhost", "SERVER_PORT": "8080", "REQUEST_METHOD": "GET", "PATH_INFO": "/foo", "HTTP_ACCEPT": "application/json", "QUERY_STRING": "bar=1", } start_response = DummyStartResponse() with self.assertLogs(level="ERROR") as cm: response = router(environ, start_response) self.assertEqual(start_response.status, "500 Internal Server Error") if openapi_core.__version__ == "0.13.8": # pragma: no cover self.assertEqual( json.loads(response[0]), [ { "exception": "ResponseNotFound", "message": "Unknown response http status: 412", } ], ) else: # pragma: no cover self.assertEqual( json.loads(response[0]), [ { "exception": "InvalidResponse", "message": "Unknown response http status: 412", } ], ) self.assertEqual( cm.output, ["ERROR:pyramid_openapi3:Unknown response http status: 412"] )
def test_nonapi_view(self) -> None: """Test View without openapi validation.""" self._add_view(openapi=False) # run request through router router = Router(self.config.registry) environ = { "wsgi.url_scheme": "http", "SERVER_NAME": "localhost", "SERVER_PORT": "8080", "REQUEST_METHOD": "GET", "PATH_INFO": "/foo", } start_response = DummyStartResponse() response = router(environ, start_response) self.assertEqual(start_response.status, "200 OK") self.assertIn(b"foo", b"".join(response))
def test_view_valid_request_response(self) -> None: """Test openapi validated view which has correct request and response.""" self._add_view(lambda *arg: {"test": "correct"}) # run request through router router = Router(self.config.registry) environ = { "wsgi.url_scheme": "http", "SERVER_NAME": "localhost", "SERVER_PORT": "8080", "REQUEST_METHOD": "GET", "PATH_INFO": "/foo", "HTTP_ACCEPT": "application/json", "QUERY_STRING": "bar=1", } start_response = DummyStartResponse() response = router(environ, start_response) self.assertEqual(start_response.status, "200 OK") self.assertEqual(json.loads(response[0]), {"test": "correct"})
def test_tween_factory_can_find_api_factory(self): registry = self.config.registry router = Router(registry) # This palaver is necessary to call the tween directly. def _make_request(**environ): req = make_request(**environ) req.__dict__["request_iface"] = IRequest req.__dict__["registry"] = registry return req # Create the factory with no IAPIFactory registered. # It should grab the api from the request environ. registry.registerUtility(None, IAPIFactory) tween = whoauth_tween_factory(router.handle_request, registry) req = _make_request(PATH_INFO="/ok", HTTP_AUTHORIZATION=GOOD_AUTHZ["test"]) response = tween(req) self.assertHeadersContain(response.headerlist, "X-Dummy-Remember")
def test_default_exception_view(self) -> None: """Test Pyramid default exception response view. Pyramid default exception response view renders a RequestValidationError. """ self._add_view() self._add_default_exception_view() # run request through router router = Router(self.config.registry) environ = { "wsgi.url_scheme": "http", "SERVER_NAME": "localhost", "SERVER_PORT": "8080", "REQUEST_METHOD": "GET", "PATH_INFO": "/foo", "HTTP_ACCEPT": "application/json", } start_response = DummyStartResponse() response = router(environ, start_response) self.assertEqual(start_response.status, "400 Bad Request") self.assertIn("Missing required parameter: bar", json.loads(b"".join(response))["message"])
def test_response_validation_disabled(self) -> None: """Test View with response validation disabled.""" self.config.registry.settings[ "pyramid_openapi3.enable_response_validation" ] = False self._add_view(lambda *arg: "not-valid") # run request through router router = Router(self.config.registry) environ = { "wsgi.url_scheme": "http", "SERVER_NAME": "localhost", "SERVER_PORT": "8080", "REQUEST_METHOD": "GET", "PATH_INFO": "/foo", "HTTP_ACCEPT": "application/json", "QUERY_STRING": "bar=1", } start_response = DummyStartResponse() response = router(environ, start_response) self.assertEqual(start_response.status, "200 OK") self.assertIn(b'"not-valid"', response)
def make_wsgi_app(self): """ Commits any pending configuration statements, sends a :class:`pyramid.events.ApplicationCreated` event to all listeners, adds this configuration's registry to :attr:`pyramid.config.global_registries`, and returns a :app:`Pyramid` WSGI application representing the committed configuration state.""" self.commit() app = Router(self.registry) # Allow tools like "pshell development.ini" to find the 'last' # registry configured. global_registries.add(self.registry) # Push the registry on to the stack in case any code that depends on # the registry threadlocal APIs used in listeners subscribed to the # IApplicationCreated event. self.manager.push({'registry': self.registry, 'request': None}) try: self.registry.notify(ApplicationCreated(app)) finally: self.manager.pop() return app
def _makeRouter(config): from pyramid.router import Router router = Router(config.registry) return router
def make_wsgi_app(self): # pragma: no cover self.commit() global_registries.add(self.registry) return Router(self.registry)
def get_root(app: Router): """Return the root of the application.""" request = Request.blank('/path-is-meaningless-here') request.registry = app.registry root = app.root_factory(request) return root