flask_restplus add_namespace keep getting no attribute 'as_view' - python

I writing a flask, trying to organize it by using Blueprint with Namespace, following this tutorial
I had faced some problem, and had look around internet and had review solution in 1 and 2. The first one is not relevant to what I doing, and the second one the solution just doesn't fix my problem.
Here are my code:
project/project.py
from flask import Flask, jsonify, url_for
from .apis.apis import api
app = Flask(__name__)
app.register_blueprint(api, url_prefix="/api")
project/apis/apis.py
from flask import Blueprint
from .user.authentication import auth
from flask_restplus import Api, apidoc, Resource
blueprint = Blueprint("api", __name__)
api = Api(blueprint, doc='/docs', ui=False)
api.add_namespace(auth, path="/auth") #Keep getting error at this line
project/apis/user/authentication.py
from flask_restplus import Namespace
auth = Namespace('auth', description='Authentication')
#auth.route("/test")
def authentication():
return "test"
Stack Trace
Traceback (most recent call last):
File "/home/gaara/Python/Flask-Api/project/__init__.py", line 1, in <module>
from .project import app
File "/home/gaara/Python/Flask-Api/project/project.py", line 3, in <module>
from .apis.apis import api
File "/home/gaara/Python/Flask-Api/project/apis/apis.py", line 13, in <module>
api.add_namespace(auth, path="/auth")
File "/home/gaara/Python/Flask-Api/venv/lib/python3.6/site-packages/flask_restplus/api.py", line 413, in add_namespace
self.register_resource(ns, resource, *self.ns_urls(ns, urls), **kwargs)
File "/home/gaara/Python/Flask-Api/venv/lib/python3.6/site-packages/flask_restplus/api.py", line 255, in register_resource
self._register_view(self.app, resource, *urls, **kwargs)
File "/home/gaara/Python/Flask-Api/venv/lib/python3.6/site-packages/flask_restplus/api.py", line 276, in _register_view
resource_func = self.output(resource.as_view(endpoint, self, *resource_class_args,
AttributeError: 'function' object has no attribute 'as_view'
I am not sure why I keep getting this error, had try few approach, include put apis.py all in __init__.py and change the import, but always getting the same error.
What I wish is to code api in an organize way, and when go to localhost:5000/api/auth/test it will output me test

You defined a function, but Flask restplus requires a class, as you can also see in your tutorial.
So it should look like this:
from flask_restplus import Resource
#auth.route("/test")
class Authentication(Resource):
def get(self):
return "test"

While the approved answer is fully correct, let me dispose why we always say, that if something is possible, does not mean that you should do!
So the following to be for everyone a lesson (at 1st place for myself) of an ANTI-PATTERN!
I was a bit tinkering here & there, I found out what is needed for a function like you mentioned in order to be registered and usable by a framework, like F-Restplus (I've used in my case F-RestX==0.5.0, but in reality is a fork of that), and here we go:
def avg(dummy_self_var=''):
# the dummy_self_var is needed in order to trick and pretend to be as an instance method
result = 'any-calculation-that-has-to-be-made'
logging.debug(f'AVG result is: {result}')
return Response(str(result))
# imitating that this function is a Resource :D
avg.view_class = Resource
avg.as_view = Resource.as_view # at the end, it could be the following function as well: `lambda x,y: avg`
avg.view_class.methods = {'GET', }
avg.view_class.get = avg
api.add_resource(avg, '/asd')
With the help of this I achieved to have the same functionality, it works &
gets registered automatically by the Swagger-UI docs:
So, while nearly everything is possible, I could not imagine a situation where someone needs this 'workaround', instead, to be forward-compatible, I would definitely refactor instead of producing this mess in the long-run. Of course, the choice is up to you.

Related

Mock flask.request in python nosetests

I'm writing test cases for code that is called via a route under Flask. I don't want to test the code by setting up a test app and calling a URL that hits the route, I want to call the function directly. To make this work I need to mock flask.request and I can't seem to manage it. Google / stackoverflow searches lead to a lot of answers that show how to set up a test application which again is not what I want to do.
The code would look something like this.
somefile.py
-----------
from flask import request
def method_called_from_route():
data = request.values
# do something with data here
test_somefile.py
----------------
import unittest
import somefile
class SomefileTestCase(unittest.TestCase):
#patch('somefile.request')
def test_method_called_from_route(self, mock_request):
# want to mock the request.values here
I'm having two issues.
(1) Patching the request as I've sketched out above does not work. I get an error similar to "AttributeError: 'Blueprint' object has no attribute 'somefile'"
(2) I don't know how to exactly mock the request object if I could patch it. It doesn't really have a return_value since it isn't a function.
Again I can't find any examples on how to do this so I felt a new question was acceptable.
Try this
test_somefile.py
import unittest
import somefile
import mock
class SomefileTestCase(unittest.TestCase):
def test_method_called_from_route(self):
m = mock.MagicMock()
m.values = "MyData"
with mock.patch("somefile.request", m):
somefile.method_called_from_route()
unittest.main()
somefile.py
from flask import request
def method_called_from_route():
data = request.values
assert(data == "MyData")
This is going to mock the entire request object.
If you want to mock only request.values while keeping all others intact, this would not work.
A few years after the question was asked, but this is how I solved this with python 3.9 (other proposed solutions stopped working with python 3.8 see here). I'm using pytest and pytest-mock, but the idea should be the same across testing frameworks, as long as you are using the native unittest.mock.patch in some capacity (pytest-mock essentially just wraps these methods in an easier to use api). Unfortunately, it does require that you set up a test app, however, you do not need to go through the process of using test_client, and can just invoke the function directly.
This can be easily handled by using the Application Factory Design Pattern, and injecting application config. Then, just use the created app's .test_request_context as a context manager to mock out the request object. using .test_request_context as a context manager, gives everything called within the context access to the request object. Here's an example below.
import pytest
from app import create_app
#pytest.fixture
def request_context():
"""create the app and return the request context as a fixture
so that this process does not need to be repeated in each test
"""
app = create_app('module.with.TestingConfig')
return app.test_request_context
def test_something_that_requires_global_request_object(mocker, request_context):
"""do the test thing"""
with request_context():
# mocker.patch is just pytest-mock's way of using unittest.mock.patch
mock_request = mocker.patch('path.to.where.request.is.used')
# make your mocks and stubs
mock_request.headers = {'content-type': 'application/json'}
mock_request.get_json.return_value = {'some': 'json'}
# now you can do whatever you need, using mock_request, and you do not
# need to remain within the request_context context manager
run_the_function()
mock_request.get_json.assert_called_once()
assert 1 == 1
# etc.
pytest is great because it allows you to easily setup fixtures for your tests as described above, but you could do essentially the same thing with UnitTest's setUp instance methods. Happy to provide an example for the Application Factory design pattern, or more context, if necessary!
with help of Gabrielbertouinataa on this article: https://medium.com/#vladbezden/how-to-mock-flask-request-object-in-python-fdbc249de504:
code:
def print_request_data():
print(flask.request.data)
test:
flask_app = flask.Flask('test_flask_app')
with flask_app.test_request_context() as mock_context:
mock_context.request.data = "request_data"
mock_context.request.path = "request_path"
print_request_data()
Here is an example of how I dealt with it:
test_common.py module
import pytest
import flask
def test_user_name(mocker):
# GIVEN: user is provided in the request.headers
given_user_name = "Some_User"
request_mock = mocker.patch.object(flask, "request")
request_mock.headers.get.return_value = given_user_name
# WHEN: request.header.get method is called
result = common.user_name()
# THEN: user name should be returned
request_mock.headers.get.assert_called_once_with("USERNAME", "Invalid User")
assert result == given_user_name
common.py module
import flask
def user_name():
return flask.request.headers.get("USERNAME", "Invalid User")
What you're trying to do is counterproductive. Following the RFC 2616 a request is:
A request message from a client to a server includes, within the first line of that message, the method to be applied to the resource, the identifier of the resource, and the protocol version in use.
Mocking the Flask request you need to rebuild its structure, what certainly, you will not to want to do!
The best approach should be use something like Flask-Testing or use some recipes like this, and then, test your method.

Get list of all routes defined in the Flask app

I have a complex Flask-based web app. There are lots of separate files with view functions. Their URLs are defined with the #app.route('/...') decorator. Is there a way to get a list of all the routes that have been declared throughout my app? Perhaps there is some method I can call on the app object?
All the routes for an application are stored on app.url_map which is an instance of werkzeug.routing.Map. You can iterate over the Rule instances by using the iter_rules method:
from flask import Flask, url_for
app = Flask(__name__)
def has_no_empty_params(rule):
defaults = rule.defaults if rule.defaults is not None else ()
arguments = rule.arguments if rule.arguments is not None else ()
return len(defaults) >= len(arguments)
#app.route("/site-map")
def site_map():
links = []
for rule in app.url_map.iter_rules():
# Filter out rules we can't navigate to in a browser
# and rules that require parameters
if "GET" in rule.methods and has_no_empty_params(rule):
url = url_for(rule.endpoint, **(rule.defaults or {}))
links.append((url, rule.endpoint))
# links is now a list of url, endpoint tuples
See Display links to new webpages created for a bit more information.
I just met the same question. Those solutions above are too complex.
Just open a new shell under your project:
>>> from app import app
>>> app.url_map
The first 'app' is my project script: app.py,
another is my web's name.
(this solution is for the tiny web with a little route)
I make a helper method on my manage.py:
#manager.command
def list_routes():
import urllib
output = []
for rule in app.url_map.iter_rules():
options = {}
for arg in rule.arguments:
options[arg] = "[{0}]".format(arg)
methods = ','.join(rule.methods)
url = url_for(rule.endpoint, **options)
line = urllib.unquote("{:50s} {:20s} {}".format(rule.endpoint, methods, url))
output.append(line)
for line in sorted(output):
print line
It solves the the missing argument by building a dummy set of options. The output looks like:
CampaignView:edit HEAD,OPTIONS,GET /account/[account_id]/campaigns/[campaign_id]/edit
CampaignView:get HEAD,OPTIONS,GET /account/[account_id]/campaign/[campaign_id]
CampaignView:new HEAD,OPTIONS,GET /account/[account_id]/new
Then to run it:
python manage.py list_routes
For more on manage.py checkout: http://flask-script.readthedocs.org/en/latest/
Apparently, since version 0.11, Flask has a built-in CLI. One of the built-in commands lists the routes:
FLASK_APP='my_project.app' flask routes
Similar to Jonathan's answer I opted to do this instead. I don't see the point of using url_for as it will break if your arguments are not string e.g. float
#manager.command
def list_routes():
import urllib
output = []
for rule in app.url_map.iter_rules():
methods = ','.join(rule.methods)
line = urllib.unquote("{:50s} {:20s} {}".format(rule.endpoint, methods, rule))
output.append(line)
for line in sorted(output):
print(line)
Use cli command in Directory where your flask project is.
flask routes
Since you did not specify that it has to be run command-line, the following could easily be returned in json for a dashboard or other non-command-line interface. The result and the output really shouldn't be commingled from a design perspective anyhow. It's bad program design, even if it is a tiny program. The result below could then be used in a web application, command-line, or anything else that ingests json.
You also didn't specify that you needed to know the python function associated with each route, so this more precisely answers your original question.
I use below to add the output to a monitoring dashboard myself. If you want the available route methods (GET, POST, PUT, etc.), you would need to combine it with other answers above.
Rule's repr() takes care of converting the required arguments in the route.
def list_routes():
routes = []
for rule in app.url_map.iter_rules():
routes.append('%s' % rule)
return routes
The same thing using a list comprehension:
def list_routes():
return ['%s' % rule for rule in app.url_map.iter_rules()]
Sample output:
{
"routes": [
"/endpoint1",
"/nested/service/endpoint2",
"/favicon.ico",
"/static/<path:filename>"
]
}
If you need to access the view functions themselves, then instead of app.url_map, use app.view_functions.
Example script:
from flask import Flask
app = Flask(__name__)
#app.route('/foo/bar')
def route1():
pass
#app.route('/qux/baz')
def route2():
pass
for name, func in app.view_functions.items():
print(name)
print(func)
print()
Output from running the script above:
static
<bound method _PackageBoundObject.send_static_file of <Flask '__main__'>>
route1
<function route1 at 0x128f1b9d8>
route2
<function route2 at 0x128f1ba60>
(Note the inclusion of the "static" route, which is created automatically by Flask.)
You can view all the Routes via flask shell by running the following commands after exporting or setting FLASK_APP environment variable.
flask shell
app.url_map
inside your flask app do:
flask shell
>>> app.url_map
Map([<Rule '/' (OPTIONS, HEAD, GET) -> helloworld>,
<Rule '/static/<filename>' (OPTIONS, HEAD, GET) -> static>])
print(app.url_map)
That, is, if your Flask application name is 'app'.
It's an attribute of the instance of the Flask App.
See https://flask.palletsprojects.com/en/2.1.x/api/#flask.Flask.url_map

Google App Engine Python Search API Document Error

I'm using the Python 1.6.6 SDK and having trouble getting the search API to initialise a new Document:
from google.appengine.api import search
def index_document(document_id, some_string):
return search.Document(
doc_id=document_id,
fields=[
search.TextField(name='text_field', value=some_string),
search.DateField(name='date', value=datetime.now().date())
])
In response I get:
AttributeError: type object 'search' has no attribute 'Document'
I can't see any evidence that this is a general problem, and all the docs point to this being the procedure for initialising a Document.
However, if I place the API call within the function it executes as expected:
def index_document(document_id, some_string):
from google.appengine.api import search
return search.Document(...
I can't see any reason why this would work, but a global import wouldn't as I am importing a number of their APIs. Has anyone had a similar problem/success in getting it to work?
Check that you have no other objects with the same name.

pyfacebook + Google App Engine: can't find new functions in facebook.py

I'm trying to use the pyfacebook functions (https://github.com/sciyoshi/pyfacebook/) in a Google app engine project. I've followed the advice on the Facebook developer forum (http://forum.developers.facebook.net/viewtopic.php?pid=164613) and added the additional functions to the __init__.py file, copied that file to the root directory of my project and renamed it facebook.py. Having imported facebook.py I added the following to the get(self) method for the Python class for the page:
facebookapi = facebook.Facebook(API_KEY, SECRET)
if not facebookapi.check_connect_session(self.request):
path = os.path.join(os.path.dirname(__file__), 'templates/login.html')
self.response.out.write(template.render(path, {'apikey': API_KEY}))
return
user = facebookapi.users.getInfo(
[facebookapi.uid],
['uid', 'name', 'birthday', 'relationship_status'])[0]
template_values = {
'name': user['name'],
'birthday': user['birthday'],
'relationship_status': user['relationship_status'],
'uid': user['uid'],
'apikey': API_KEY
}
path = os.path.join(os.path.dirname(__file__), 'templates/index.html')
self.response.out.write(template.render(path, template_values))
When running it I get the following error:
File "\much\baw08u\Private\IDS\helloworld\helloworld.py", line 54, in get
if not facebookapi.check_connect_session(self.request):
AttributeError: 'Facebook' object has no attribute 'check_connect_session'
So it seems to be loading the facebook API fine, but not the new methods I've added. I copied and pasted the code from the developer forum at the bottom of the Facebook class definition, and made sure all the indentation was right but it still doesn't seem to be picking them up. Does anyone know what might be the problem?
Thanks
Ben
You believe the Facebook class has a certain method but Python is sure it hasn't. Why? Maybe you misspelled the method name, maybe you did not get the indentation right - hard to say without seeing the code.
You could try poking around to validate your assumptions:
import facebook
import logging
logging.warn('Facebook class: %r', dir(facebook.Facebook))
logging.warn('facebook module: %r', dir(facebook))
If you are sure you are operating on the correct file, the you should expect to see check_connect_session as a method of Facebook. If you didn't add enough indentation then you expect to see check_connect_method as a function defined in the facebook module. Too much indentation would make check_connect_method a sub function of which ever method precedes it and it won't show up in the above logging. Pay close attention to indentation.
However, a better way to add some custom methods might be:
import facebook
class Facebook(facebook.Facebook):
def check_connect_session(request):
pass
facebookapi = Facebook(API_KEY, SECRET)
if not facebookapi.check_connect_session(...):
...
Now when Facebook update their code you simply copy the new file into place - no need to merge your customisations.

Why do I get TypeError: get() takes exactly 2 arguments (1 given)? Google App Engine

I have been trying and trying for several hours now and there must be an easy way to retreive the url. I thought this was the way:
#from data.models import Program
import basehandler
class ProgramViewHandler(basehandler.BaseHandler):
def get(self,slug):
# query = Program.all()
# query.filter('slug =', fslug)
self.render_template('../presentation/program.html',{})
Whenever this code gets executed I get this error on the stacktrace:
appengine\ext\webapp__init__.py", line 511, in call
handler.get(*groups)
TypeError: get() takes exactly 2 arguments (1 given)
I have done some debugging, but this kind of debugging exceeds my level of debugging. When I remove the slug from def get(self,slug) everything runs fine.
This is the basehandler:
import os
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
class BaseHandler(webapp.RequestHandler):
def __init__(self,**kw):
webapp.RequestHandler.__init__(BaseHandler, **kw)
def render_template(self, template_file, data=None, **kw):
path = os.path.join(os.path.dirname(__file__), template_file)
self.response.out.write(template.render(path, data))
If somebody could point me in the right direction it would be great! Thank you! It's the first time for me to use stackoverflow to post a question, normally I only read it to fix the problems I have.
You are getting this error because ProgramViewHandler.get() is being called without the slug parameter.
Most likely, you need to fix the URL mappings in your main.py file. Your URL mapping should probably look something like this:
application = webapp.WSGIApplication([(r'/(.*)', ProgramViewHandler)])
The parenthesis indicate a regular expression grouping. These matched groups are passed to your handler as arguments. So in the above example, everything in the URL following the initial "/" will be passed to ProgramViewHandler.get()'s slug parameter.
Learn more about URL mappings in webapp here.
If you do this:
obj = MyClass()
obj.foo(3)
The foo method on MyClass is called with two arguments:
def foo(self, number)
The object on which it is called is passed as the first parameter.
Maybe you are calling get() statically (i.e. doing ProgramViewHandler.get() instead of myViewHandlerVariable.get()), or you are missing a parameter.

Categories

Resources