noirbizarre / flask-restplus

Fully featured framework for fast, easy and documented API development with Flask
http://flask-restplus.readthedocs.org
Other
2.74k stars 506 forks source link

Difficulty exporting Swagger specification #357

Open franfabrizio opened 6 years ago

franfabrizio commented 6 years ago

Flask newbie here. I'm trying to follow the documentation to export the Swagger specification for my app. Docs say to do this:

from flask import json
from myapp import api
print(json.dumps(api.__schema__))

My app is structured like this:

bibapi/ --bibapi.py --exportspec.py #script I'm trying to run --apis/ ----__init__.py #defines api

but when I run exportspec.py, which looks like:

from flask import json
from apis import api
print(json.dumps(api.__schema__))

I get:

Traceback (most recent call last):
  File "exportspec.py", line 3, in <module>
    print(json.dumps(api.__schema__))
  File "/Users/fran/anaconda3/envs/bibapi/lib/python3.6/site-packages/werkzeug/utils.py", line 73, in __get__
    value = self.func(obj)
  File "/Users/fran/anaconda3/envs/bibapi/lib/python3.6/site-packages/flask_restplus/api.py", line 472, in __schema__
    self._schema = Swagger(self).as_dict()
  File "/Users/fran/anaconda3/envs/bibapi/lib/python3.6/site-packages/flask_restplus/swagger.py", line 137, in as_dict
    basepath = self.api.base_path
  File "/Users/fran/anaconda3/envs/bibapi/lib/python3.6/site-packages/flask_restplus/api.py", line 462, in base_path
    return url_for(self.endpoint('root'))
  File "/Users/fran/anaconda3/envs/bibapi/lib/python3.6/site-packages/flask/helpers.py", line 270, in url_for
    raise RuntimeError('Attempted to generate a URL without the '
RuntimeError: Attempted to generate a URL without the application context being pushed. This has to be executed when application context is available.

Googling that RuntimeError says that you cannot generate URLs without application context. I came across a solution that showed how you can provide a minimum application context for Flask to work:

from flask import json
from bibapi import app
from apis import api
with app.test_request_context():
    print(json.dumps(api.__schema__))

When I run that I get:

Traceback (most recent call last):
  File "exportspec.py", line 5, in <module>
    print(json.dumps(api.__schema__))
  File "/Users/fran/anaconda3/envs/bibapi/lib/python3.6/site-packages/werkzeug/utils.py", line 73, in __get__
    value = self.func(obj)
  File "/Users/fran/anaconda3/envs/bibapi/lib/python3.6/site-packages/flask_restplus/api.py", line 472, in __schema__
    self._schema = Swagger(self).as_dict()
  File "/Users/fran/anaconda3/envs/bibapi/lib/python3.6/site-packages/flask_restplus/swagger.py", line 137, in as_dict
    basepath = self.api.base_path
  File "/Users/fran/anaconda3/envs/bibapi/lib/python3.6/site-packages/flask_restplus/api.py", line 462, in base_path
    return url_for(self.endpoint('root'))
  File "/Users/fran/anaconda3/envs/bibapi/lib/python3.6/site-packages/flask/helpers.py", line 333, in url_for
    return appctx.app.handle_url_build_error(error, endpoint, values)
  File "/Users/fran/anaconda3/envs/bibapi/lib/python3.6/site-packages/flask/app.py", line 1805, in handle_url_build_error
    reraise(exc_type, exc_value, tb)
  File "/Users/fran/anaconda3/envs/bibapi/lib/python3.6/site-packages/flask/_compat.py", line 33, in reraise
    raise value
  File "/Users/fran/anaconda3/envs/bibapi/lib/python3.6/site-packages/flask/helpers.py", line 323, in url_for
    force_external=external)
  File "/Users/fran/anaconda3/envs/bibapi/lib/python3.6/site-packages/werkzeug/routing.py", line 1768, in build
    raise BuildError(endpoint, values, method, self)
werkzeug.routing.BuildError: Could not build url for endpoint 'root'. Did you mean 'static' instead?

I've been banging my head on this one for a while now. What am I doing wrong?

Thanks, Fran

boompig commented 6 years ago

I am also getting this error. I'm reproducing the example from the docs here.

franfabrizio commented 6 years ago

I never got that example or anything close to it working. What I ended up doing is actually putting an endpoint into my project to expose it. It looks like this:

@api.route('/')
class Spec(Resource):
    def get(self):
      from apis import api
      return api.__schema__

Pretty sure this is a total hack but it's the only way we could think of for it to work.

boompig commented 6 years ago

That's what I resorted to as well

jaxxie commented 6 years ago

The below works for me.

from myapp import api
import json

with app.test_request_context():
    print(json.dumps(api.__schema__, sort_keys=True, indent=4, separators=(',', ': ')))

The code of the module will be


version = "1.0.0"
blueprint = Blueprint('api', __name__, url_prefix='/api/1')
api = Api(blueprint,version=version, title='My API',
    description='My API description')
api.add_namespace(namespace1)
api.add_namespace(namespace2)
api.add_namespace(namespace3)

app.register_blueprint(blueprint)
martijnarts commented 6 years ago

Here's the script I've used to work around it:

from app import api, app
app.config["SERVER_NAME"] = "localhost"
app.app_context().__enter__()
import json
print(json.dumps(api.__schema__, indent=2))