Skip to content

Commit

Permalink
Support using httpbin without flasgger
Browse files Browse the repository at this point in the history
Make the dependency on flasgger optional.  The dependency has been added
relatively recently (i.e. before the original package was abandoned but
after its last release), and it is only used to provide a more dynamic
landing page.  This is unnecessary for use of httpbin for testing,
and it introduces an indirect dependency on Rust that is problematic.

With this change, flasgger is no longer installed by default.  It can be
enabled via "[flasgger]" extra.  When flasgger is not available, httpbin
redirects to the "legacy" index page.
  • Loading branch information
mgorny committed Feb 15, 2024
1 parent 1f6e049 commit 24378a6
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 73 deletions.
157 changes: 85 additions & 72 deletions httpbin/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@
except ImportError: # werkzeug < 2.1
from werkzeug.wrappers import BaseResponse as Response

from flasgger import Swagger, NO_SANITIZER
try:
from flasgger import Swagger, NO_SANITIZER
except ImportError:
Swagger = None

from . import filters
from .helpers import (
Expand Down Expand Up @@ -93,79 +96,82 @@ def jsonify(*args, **kwargs):

app.add_template_global("HTTPBIN_TRACKING" in os.environ, name="tracking_enabled")

app.config["SWAGGER"] = {"title": "httpbin.org", "uiversion": 3}

template = {
"swagger": "2.0",
"info": {
"title": "httpbin.org",
"description": (
"A simple HTTP Request & Response Service."
"<br/> A <a href='http://kennethreitz.com/'>Kenneth Reitz</a> project."
"<br/> <br/> <b>Run locally: </b> <br/> "
"<code>$ docker pull ghcr.io/psf/httpbin</code> <br/>"
"<code>$ docker run -p 80:8080 ghcr.io/psf/httpbin</code>"
),
"contact": {
"responsibleOrganization": "Python Software Foundation",
"responsibleDeveloper": "Kenneth Reitz",
"url": "https://github.com/psf/httpbin/",
},
# "termsOfService": "http://me.com/terms",
"version": version,
},
"host": "httpbin.org", # overrides localhost:5000
"basePath": "/", # base bash for blueprint registration
"schemes": ["https"],
"protocol": "https",
"tags": [
{
"name": "HTTP Methods",
"description": "Testing different HTTP verbs",
# 'externalDocs': {'description': 'Learn more', 'url': 'https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html'}
},
{"name": "Auth", "description": "Auth methods"},
{
"name": "Status codes",
"description": "Generates responses with given status code",
},
{"name": "Request inspection", "description": "Inspect the request data"},
{
"name": "Response inspection",
"description": "Inspect the response data like caching and headers",
},
{
"name": "Response formats",
"description": "Returns responses in different data formats",
},
{"name": "Dynamic data", "description": "Generates random and dynamic data"},
{"name": "Cookies", "description": "Creates, reads and deletes Cookies"},
{"name": "Images", "description": "Returns different image formats"},
{"name": "Redirects", "description": "Returns different redirect responses"},
{
"name": "Anything",
"description": "Returns anything that is passed to request",
if Swagger is not None:
app.config["SWAGGER"] = {"title": "httpbin.org", "uiversion": 3}

template = {
"swagger": "2.0",
"info": {
"title": "httpbin.org",
"description": (
"A simple HTTP Request & Response Service."
"<br/> A <a href='http://kennethreitz.com/'>Kenneth Reitz</a> project."
"<br/> <br/> <b>Run locally: </b> <br/> "
"<code>$ docker pull ghcr.io/psf/httpbin</code> <br/>"
"<code>$ docker run -p 80:8080 ghcr.io/psf/httpbin</code>"
),
"contact": {
"responsibleOrganization": "Python Software Foundation",
"responsibleDeveloper": "Kenneth Reitz",
"url": "https://github.com/psf/httpbin/",
},
# "termsOfService": "http://me.com/terms",
"version": version,
},
],
}

swagger_config = {
"headers": [],
"specs": [
{
"endpoint": "spec",
"route": "/spec.json",
"rule_filter": lambda rule: True, # all in
"model_filter": lambda tag: True, # all in
}
],
"static_url_path": "/flasgger_static",
# "static_folder": "static", # must be set by user
"swagger_ui": True,
"specs_route": "/",
}
"host": "httpbin.org", # overrides localhost:5000
"basePath": "/", # base bash for blueprint registration
"schemes": ["https"],
"protocol": "https",
"tags": [
{
"name": "HTTP Methods",
"description": "Testing different HTTP verbs",
# 'externalDocs': {'description': 'Learn more', 'url': 'https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html'}
},
{"name": "Auth", "description": "Auth methods"},
{
"name": "Status codes",
"description": "Generates responses with given status code",
},
{"name": "Request inspection", "description": "Inspect the request data"},
{
"name": "Response inspection",
"description": "Inspect the response data like caching and headers",
},
{
"name": "Response formats",
"description": "Returns responses in different data formats",
},
{"name": "Dynamic data", "description": "Generates random and dynamic data"},
{"name": "Cookies", "description": "Creates, reads and deletes Cookies"},
{"name": "Images", "description": "Returns different image formats"},
{"name": "Redirects", "description": "Returns different redirect responses"},
{
"name": "Anything",
"description": "Returns anything that is passed to request",
},
],
}

swagger = Swagger(app, sanitizer=NO_SANITIZER, template=template, config=swagger_config)
swagger_config = {
"headers": [],
"specs": [
{
"endpoint": "spec",
"route": "/spec.json",
"rule_filter": lambda rule: True, # all in
"model_filter": lambda tag: True, # all in
}
],
"static_url_path": "/flasgger_static",
# "static_folder": "static", # must be set by user
"swagger_ui": True,
"specs_route": "/",
}

swagger = Swagger(app, sanitizer=NO_SANITIZER, template=template, config=swagger_config)
else:
app.logger.warning("Swagger not found, legacy index will be used.")

# Set up Bugsnag exception tracking, if desired. To use Bugsnag, install the
# Bugsnag Python client with the command "pip install bugsnag", and set the
Expand Down Expand Up @@ -244,6 +250,13 @@ def set_cors_headers(response):
# ------


if Swagger is None:
@app.route("/")
def no_flasgger_index():
"""Redirect to legacy index if flasgger is not available."""
return view_landing_page()


@app.route("/legacy")
def view_landing_page():
"""Generates Landing Page in legacy layout."""
Expand Down
4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ classifiers = [
dependencies = [
"brotlicffi",
"decorator",
"flasgger",
"flask >= 2.2.4",
'greenlet < 3.0; python_version<"3.12"',
'greenlet >= 3.0.0a1; python_version>="3.12.0rc0"',
Expand All @@ -44,6 +43,9 @@ dependencies = [

[project.optional-dependencies]
test = ["pytest", "tox"]
flasgger = [
"flasgger",
]
mainapp = [
"gunicorn",
"gevent",
Expand Down

0 comments on commit 24378a6

Please sign in to comment.