Skip to content

Commit 24378a6

Browse files
committed
Support using httpbin without flasgger
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.
1 parent 1f6e049 commit 24378a6

File tree

2 files changed

+88
-73
lines changed

2 files changed

+88
-73
lines changed

httpbin/core.py

Lines changed: 85 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@
3333
except ImportError: # werkzeug < 2.1
3434
from werkzeug.wrappers import BaseResponse as Response
3535

36-
from flasgger import Swagger, NO_SANITIZER
36+
try:
37+
from flasgger import Swagger, NO_SANITIZER
38+
except ImportError:
39+
Swagger = None
3740

3841
from . import filters
3942
from .helpers import (
@@ -93,79 +96,82 @@ def jsonify(*args, **kwargs):
9396

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

96-
app.config["SWAGGER"] = {"title": "httpbin.org", "uiversion": 3}
97-
98-
template = {
99-
"swagger": "2.0",
100-
"info": {
101-
"title": "httpbin.org",
102-
"description": (
103-
"A simple HTTP Request & Response Service."
104-
"<br/> A <a href='http://kennethreitz.com/'>Kenneth Reitz</a> project."
105-
"<br/> <br/> <b>Run locally: </b> <br/> "
106-
"<code>$ docker pull ghcr.io/psf/httpbin</code> <br/>"
107-
"<code>$ docker run -p 80:8080 ghcr.io/psf/httpbin</code>"
108-
),
109-
"contact": {
110-
"responsibleOrganization": "Python Software Foundation",
111-
"responsibleDeveloper": "Kenneth Reitz",
112-
"url": "https://github.com/psf/httpbin/",
113-
},
114-
# "termsOfService": "http://me.com/terms",
115-
"version": version,
116-
},
117-
"host": "httpbin.org", # overrides localhost:5000
118-
"basePath": "/", # base bash for blueprint registration
119-
"schemes": ["https"],
120-
"protocol": "https",
121-
"tags": [
122-
{
123-
"name": "HTTP Methods",
124-
"description": "Testing different HTTP verbs",
125-
# 'externalDocs': {'description': 'Learn more', 'url': 'https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html'}
126-
},
127-
{"name": "Auth", "description": "Auth methods"},
128-
{
129-
"name": "Status codes",
130-
"description": "Generates responses with given status code",
131-
},
132-
{"name": "Request inspection", "description": "Inspect the request data"},
133-
{
134-
"name": "Response inspection",
135-
"description": "Inspect the response data like caching and headers",
136-
},
137-
{
138-
"name": "Response formats",
139-
"description": "Returns responses in different data formats",
140-
},
141-
{"name": "Dynamic data", "description": "Generates random and dynamic data"},
142-
{"name": "Cookies", "description": "Creates, reads and deletes Cookies"},
143-
{"name": "Images", "description": "Returns different image formats"},
144-
{"name": "Redirects", "description": "Returns different redirect responses"},
145-
{
146-
"name": "Anything",
147-
"description": "Returns anything that is passed to request",
99+
if Swagger is not None:
100+
app.config["SWAGGER"] = {"title": "httpbin.org", "uiversion": 3}
101+
102+
template = {
103+
"swagger": "2.0",
104+
"info": {
105+
"title": "httpbin.org",
106+
"description": (
107+
"A simple HTTP Request & Response Service."
108+
"<br/> A <a href='http://kennethreitz.com/'>Kenneth Reitz</a> project."
109+
"<br/> <br/> <b>Run locally: </b> <br/> "
110+
"<code>$ docker pull ghcr.io/psf/httpbin</code> <br/>"
111+
"<code>$ docker run -p 80:8080 ghcr.io/psf/httpbin</code>"
112+
),
113+
"contact": {
114+
"responsibleOrganization": "Python Software Foundation",
115+
"responsibleDeveloper": "Kenneth Reitz",
116+
"url": "https://github.com/psf/httpbin/",
117+
},
118+
# "termsOfService": "http://me.com/terms",
119+
"version": version,
148120
},
149-
],
150-
}
151-
152-
swagger_config = {
153-
"headers": [],
154-
"specs": [
155-
{
156-
"endpoint": "spec",
157-
"route": "/spec.json",
158-
"rule_filter": lambda rule: True, # all in
159-
"model_filter": lambda tag: True, # all in
160-
}
161-
],
162-
"static_url_path": "/flasgger_static",
163-
# "static_folder": "static", # must be set by user
164-
"swagger_ui": True,
165-
"specs_route": "/",
166-
}
121+
"host": "httpbin.org", # overrides localhost:5000
122+
"basePath": "/", # base bash for blueprint registration
123+
"schemes": ["https"],
124+
"protocol": "https",
125+
"tags": [
126+
{
127+
"name": "HTTP Methods",
128+
"description": "Testing different HTTP verbs",
129+
# 'externalDocs': {'description': 'Learn more', 'url': 'https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html'}
130+
},
131+
{"name": "Auth", "description": "Auth methods"},
132+
{
133+
"name": "Status codes",
134+
"description": "Generates responses with given status code",
135+
},
136+
{"name": "Request inspection", "description": "Inspect the request data"},
137+
{
138+
"name": "Response inspection",
139+
"description": "Inspect the response data like caching and headers",
140+
},
141+
{
142+
"name": "Response formats",
143+
"description": "Returns responses in different data formats",
144+
},
145+
{"name": "Dynamic data", "description": "Generates random and dynamic data"},
146+
{"name": "Cookies", "description": "Creates, reads and deletes Cookies"},
147+
{"name": "Images", "description": "Returns different image formats"},
148+
{"name": "Redirects", "description": "Returns different redirect responses"},
149+
{
150+
"name": "Anything",
151+
"description": "Returns anything that is passed to request",
152+
},
153+
],
154+
}
167155

168-
swagger = Swagger(app, sanitizer=NO_SANITIZER, template=template, config=swagger_config)
156+
swagger_config = {
157+
"headers": [],
158+
"specs": [
159+
{
160+
"endpoint": "spec",
161+
"route": "/spec.json",
162+
"rule_filter": lambda rule: True, # all in
163+
"model_filter": lambda tag: True, # all in
164+
}
165+
],
166+
"static_url_path": "/flasgger_static",
167+
# "static_folder": "static", # must be set by user
168+
"swagger_ui": True,
169+
"specs_route": "/",
170+
}
171+
172+
swagger = Swagger(app, sanitizer=NO_SANITIZER, template=template, config=swagger_config)
173+
else:
174+
app.logger.warning("Swagger not found, legacy index will be used.")
169175

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

246252

253+
if Swagger is None:
254+
@app.route("/")
255+
def no_flasgger_index():
256+
"""Redirect to legacy index if flasgger is not available."""
257+
return view_landing_page()
258+
259+
247260
@app.route("/legacy")
248261
def view_landing_page():
249262
"""Generates Landing Page in legacy layout."""

pyproject.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ classifiers = [
3333
dependencies = [
3434
"brotlicffi",
3535
"decorator",
36-
"flasgger",
3736
"flask >= 2.2.4",
3837
'greenlet < 3.0; python_version<"3.12"',
3938
'greenlet >= 3.0.0a1; python_version>="3.12.0rc0"',
@@ -44,6 +43,9 @@ dependencies = [
4443

4544
[project.optional-dependencies]
4645
test = ["pytest", "tox"]
46+
flasgger = [
47+
"flasgger",
48+
]
4749
mainapp = [
4850
"gunicorn",
4951
"gevent",

0 commit comments

Comments
 (0)