Flask reqparse.RequestParser ValueError with parse_args() - python

Good day everyone,
I am having a really strange issue with Flask's RequestParser.parse_args(). Below is a snippet of my __init__ method of the relevant class:
def __init__(self, arg1, arg2):
self.arg1 = arg1
self.arg2 = arg2
self.arg_parser = reqparse.RequestParser()
self.arg_parser.add_argument('optional_arg_from_url', type=bool, default=False)
Notice I only add 1 argument to self.arg_parser. Then, in the post method, I want to retrieve optional_arg_from_url with arg_parser.parse_args() as follows:
def post(self):
# Authorization in headers
token = request.headers.get('Authorization').split()[1]
# Parse args
args = self.arg_parser.parse_args()
optional_arg_from_url = args['optional_arg_from_url']
Now, an example of the request url containing the optional arg would look as follows:
http://localhost:8080/path/to/endpoint/?optional_arg_from_url=True
The error that I am getting is ValueError: not enough values to unpack: expected 2, which is raised when args = self.arg_parser.parse_args() is called. I don't understand why 2 values are expected, since I only add one argument to the parser. Also, why would a value error be raised when I try to parse the args? Shouldn't it just parse all the args regardless?
Another interesting thing, is that the corresponding unit tests are working, regardless of whether the optional arg is included in the url or not. The code ALSO works if I do not include the optional arg in the url (which means the arg defaults to False) and gets parsed correspondingly. It is just when I try to overwrite the arg's value to True within the request url when a value error is raised.
I also made sure that optional_arg_from_url is spelled exactly the same everywhere, so that is not the issue.
Any assistance is appreciated.

Related

mock object library ANY not working as expected

I'm currently trying to mock a patch request to a server and I'm trying to make use of the ANY attribute in the mock object library. I have the following code:
#patch('path_to_patch.patch')
def test_job_restarted_succesfully(mock_patch):
make_patch_call()
mock_patch.assert_called_with(url=ANY, payload=ANY, callback=ANY, async_run=ANY, kwargs=ANY)
I'm getting the following error:
AssertionError: Expected call: patch(async_run=<ANY>, callback=<ANY>, kwargs=<ANY>, payload=<ANY>, url=<ANY>)
E Actual call: patch(async_run=True, callback=<function JobSvc.send_job_patch_request.<locals>.retry_on_404 at 0x000002752B873168>, payload={'analyzer': {'state': 'started'}, 'meta': {}}, svc_auth=UUID('40ed1a00-a51f-11eb-b1ed-b46bfc345269'), url='http://127.0.0.1:8080/rarecyte/1.0/jobs/slide1#20210422_203831_955885')
I found ANY in the docs given below and can't figure out why assert_called_once_with() is expecting the actual parameter that's called.
Here is the relevant section in the docs: https://docs.python.org/3/library/unittest.mock.html#any
EDIT:
The make_patch_call() ultimately calls this patch function after computing all the parameters needed for the patch function.
def patch(self, url, payload, callback=None, async_run=False, **kwargs):
payload = self._serialize_payload(payload)
func = self._do_async_request if async_run else self._do_request
return func('patch', (url, payload), callback, kwargs)
For assert_called_with, the arguments and the used keywords have to exactly match. Substituting an argument for ANY will always match the argument value, but the keyword must still match the used keyword. The generic keywords args and kwargs are no exception: if you expect them, they have to be used in the call to match.
In this case, the kwargs keyword in the expected call:
mock_patch.assert_called_with(url=ANY, payload=ANY, callback=ANY, async_run=ANY, kwargs=ANY)
has to be changed to the really used keyword svc_auth:
mock_patch.assert_called_with(url=ANY, payload=ANY, callback=ANY, async_run=ANY, svc_auth=ANY)
Note that the same applies for keyword versus positional arguments, which is a common pitfall. If you have a function foo(bar), then you have to expect the call exactly as it is made, e.g:
#mock.patch("my_module.foo")
def test_foo(patched):
foo(42)
patched.assert_called_with(ANY) # passes
patched.assert_called_with(foo=ANY) # fails
foo(bar=42)
patched.assert_called_with(ANY) # fails
patched.assert_called_with(foo=ANY) # passes

Can I send **kwargs in place of set parameter in requests.post?

I'm trying to set up a base class that contains common REST methods, to be used by more specific methods in a later testing framework. I decided that instead of creating different request.post methods that correspond to to the user passing in data, json, or files parameters, I would make one method that has the set parameters of url and header and let the user pass in whatever else they want within **kwargs. However, I'm not sure I can even use *kwargs in this context, as it seems the requests module expects a positional argument. This is what I have so far:
class Action:
def __init__(self, url, requestHeaders, **kwargs):
self.url = url
self.requestHeaders = requestHeaders
self.kwargs = kwargs
def postAction(self):
response = requests.post(self.url, headers=self.requestHeaders, self.kwargs)
resultCode = response.status_code
resultMessage = response.text
print(resultCode)
print(resultMessage)
return resultCode,resultMessage
For example, kwargs might contain files={'csv': ('/path/to/csv.csv', open('csv.csv, 'rb'), 'text/csv')} and verify=false. In another request, files might be replaced with data. However, when I try to test, I end up with this:
Traceback (most recent call last):
File "/home/user1/test/action.py", line 24
response = requests.post(self.url, headers=self.requestHeaders, self.kwargs)
SyntaxError: positional argument follows keyword argument
Is what I'm trying to do possible? If not, are there any alternatives?
If you want the kwargs to be treated as keyword arguments given to post, you need to use ** to apply them:
response = requests.post(self.url, headers=self.requestHeaders, **self.kwargs)
This, similar to sequence unpacking (*seq), will cause the data to be "expanded" into the argument list of the call.

Python Tornado get URL arguments

I'm trying to inspect a request's argument before the get() is invoked. I have a route which is described as so:
user_route = r"/users/key=(?P<key>\w+)"
app = web.Application([
web.URLSpec(user_route, user_manager.UserHandler), ..])
Next, (in the handler) prepare() is used to inspect the request before get().
def prepare(self):
# inspect request arguments
print(self.request.arguments) # prints "{}"
The problem I'm having is that I cannot access the arguments from prepare(). The last statement prints an empty dict. My get() successfully uses the arguments as they are passed in the function like this:
def get(self, key):
print(key) #works
How do I access arguments in prepare()? I have also tried self.argument('key') which gives an error "400 GET .... Missing argument key", but requested URL does have a key argument in it.
In your code key is not a GET-argument, it's a part of a path. tornado.we.URLSpec passes any capturing groups in the regex into the handler’s get/post/etc methods as arguments.
tornado.web.RequestHandler has RequestHandler.path_args and RequestHandler.path_kwargs which contain the positional and keyword arguments from URLSpec. Those are available in prepare method:
def prepare(self):
# inspect request arguments
print(self.path_kwargs) # prints {"key": "something"}
As Gennady Kandaurov mentioned, you passed the key as a part of the we.URLSpec path and you can access it using Tornado's self.path_kwargs. If you wanted to pass it as an argument you could used RequestHandler.get_argument to get the argument on your get method and use self.request.arguments on your prepare method to access it as your initial intention.
Your code could be as follow:
class Application(tornado.web.Application):
def __init__(self):
user_route = r"/users"
app = tornado.web.Application([
tornado.web.url(user_route, user_manager.UserHandler), ..])
class UserHandler(tornado.web.RequestHandler):
def get(self):
key = self.get_argument('key')
print(key)
def prepare(self):
# inspect request arguments
print(self.request.arguments)
Please let me know if you have any further question.
It's generally bad to use a character like = in a URL path fragment, since they are generally used for query arguments. Either don't use it:
`r"/users/(?P<key>\w+)"`
or turn it into a proper query argument
`r"/users/\?key=(?P<key>\w+)"`
Otherwise it's confusing for a maintainer to try to figure out which scheme you intended to use (did you really want to route a path fragment called /key%3D\w+? Or did you really mean you wanted a query arg and forgot the ??)
In any case, for URL path fragment matching ("slug-matching"), using argument unpacking can let you access them in the handler too, without having to invoke path_kwargs:
# `r"/users/(?P<key>\w+)"`
class Handler(RequestHandler):
def get(self, **kwargs):
key = kwargs.get('key')
# I prefer dict.get() here, since if you change the `+` to a `*`,
# it's possible that no key was supplied, and kwargs['key']
# will throw a KeyError exception
If you intended to use a query argument for key, then #afxentios's answer is appropriate. (You can also use self.get_query_argument('key') which will explicitly only look for query arguments in the URL (whereas get_argument also checks in the request BODY for a www-url-encoded argument (such as if you POST)).

Handle optional url parameter in Django unit test case

I have created a Dynamic Url with optional parameter
e.g., If my url is as www.example.com/getTest/1/
Now this 1 in url is optional , to handle this in views I have used None like
def function(request, id=None):
pass
Thus if there is no id in URL then still the function works for me.
But I am facing issue while testing this in unit cases.
if I use url as url = reverse('yescourse:academypage_url', args=[None]) it gives me the error
NoReverseMatch: Reverse for 'academypage_url' with arguments '('new', None)' and keyword arguments '{}' not found.
So Please Tell me how I can handle these optional url in Test cases or in Reverse.
Edit :
url(r'^getTest/(?P<action>\w+)/(?P<id>\d*)$', 'app.views.create_edit_academypage', name='academypage_url'),
You made the view function's id parameter optional, but it's not optional in the url pattern. You firsty need to rewrite your pattern as :
r'^getTest/(?P<action>\w+)/(?P<id>\d+)?$'
=> the whole 'id' sub-pattern is optional, but if it's not it must match one or more numerics.
Once done, you can reverse the url by not passing any of the args nor kwargs arguments:
url = reverse('yescourse:academypage_url')
or by passing an empty list
url = reverse('yescourse:academypage_url', args=[])
or by passing None
url = reverse('yescourse:academypage_url', args=None)
but not by passing a list containing None.

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