Tornado-swirl not picking up the params in a URL - python

I have an API which is built in Tornado, and I'm trying to document it using tornado-swirl. For some reason, it's unable to pick the optional query param from the defined URL. How could this be solved? I'm not sure what I'm doing wrong, or what I'm missing here.
I've changed the pattern and even used the exact one used in the
docs and tut.
import tornado.web
import tornado_swirl as swirl
from .base import BaseHandler
#swirl.restapi('/item/(?P<id>[\w-]+)?')
class ItemHandler(BaseHandler):
def post(self, id):
"""Item
Creating a new item
Tags:
Item
"""
# store the item
pass
async def get(self, id):
"""Item
Get items or item
Tags:
Item
"""
# return all items if no id was provided
# or return item by id when provided
pass
I'm getting the following error:
Traceback (most recent call last):
File "/Users/.../venv/lib/python3.7/site-packages/tornado/web.py", line 1697, in _execute
result = method(*self.path_args, **self.path_kwargs)
File "/Users/.../venv/lib/python3.7/site-packages/tornado_swirl/views.py", line 101, in get
for path, spec, operations in apis},
File "/Users/.../venv/lib/python3.7/site-packages/tornado_swirl/views.py", line 100, in <dictcomp>
'paths': {path: self.__get_api_spec(spec, operations)
File "/Users/.../venv/lib/python3.7/site-packages/tornado_swirl/views.py", line 368, in find_api
['{%s}' % arg for arg in [param.name for param in vals]]
TypeError: not enough arguments for format string
Apparently, it's not getting the arguments. I think it has something to do with how I'm defining the URL there.

You just need to inform the path parameter in the docstring, something like that:
"""Item
Creating a new item
Path Params:
id (string) -- Your id
Tags:
Item
"""

Related

BDD behave Python need to create a World map to hold values

I'm not too familiar with Python but I have setup a BDD framework using Python behave, I now want to create a World map class that holds data and is retrievable throughout all scenarios.
For instance I will have a world class where I can use:
World w
w.key.add('key', api.response)
In one scenario and in another I can then use:
World w
key = w.key.get('key').
Edit:
Or if there is a built in way of using context or similar in behave where the attributes are saved and retrievable throughout all scenarios that would be good.
Like lettuce where you can use world http://lettuce.it/tutorial/simple.html
I've tried this between scenarios but it doesn't seem to be picking it up
class World(dict):
def __setitem__(self, key, item):
self.__dict__[key] = item
print(item)
def __getitem__(self, key):
return self.__dict__[key]
Setting the item in one step in scenario A: w.setitem('key', response)
Getting the item in another step in scenario B: w.getitem('key',)
This shows me an error though:
Traceback (most recent call last):
File "C:\Program Files (x86)\Python\lib\site-packages\behave\model.py", line 1456, in run
match.run(runner.context)
File "C:\Program Files (x86)\Python\lib\site-packages\behave\model.py", line 1903, in run
self.func(context, *args, **kwargs)
File "steps\get_account.py", line 14, in step_impl
print(w.__getitem__('appToken'))
File "C:Project\steps\world.py", line 8, in __getitem__
return self.__dict__[key]
KeyError: 'key'
It appears that the World does not hold values here between steps that are run.
Edit:
I'm unsure how to use environment.py but can see it has a way of running code before the steps. How can I allow my call to a soap client within environment.py to be called and then pass this to a particular step?
Edit:
I have made the request in environment.py and hardcoded the values, how can I pass variables to environment.py and back?
It's called "context" in the python-behave jargon. The first argument of your step definition function is an instance of the behave.runner.Context class, in which you can store your world instance. Please see the appropriate part of the tutorial.
Have you tried the
simple approach, using global var, for instance:
def before_all(context):
global response
response = api.response
def before_scenario(context, scenario):
global response
w.key.add('key', response)
Guess feature can be accessed from context, for instance:
def before_feature(context, feature):
feature.response = api.response
def before_scenario(context, scenario):
w.key.add('key', context.feature.response)
You are looking for:
Class variable: A variable that is shared by all instances of a class.
Your code in Q uses Class Instance variable.
Read about: python_classes_objects
For instance:
class World(dict):
__class_var = {}
def __setitem__(self, key, item):
World.__class_var[key] = item
def __getitem__(self, key):
return World.__class_var[key]
# Scenario A
A = World()
A['key'] = 'test'
print('A[\'key\']=%s' % A['key'] )
del A
# Scenario B
B = World()
print('B[\'key\']=%s' % B['key'] )
Output:
A['key']=test
B['key']=test
Tested with Python:3.4.2
Come back and Flag your Question as answered if this is working for you or comment why not.
Defining global var in before_all hook did not work for me.
As mentioned by #stovfl
But defining global var within one of my steps worked out.
Instead, as Szabo Peter mentioned use the context.
context.your_variable_name = api.response
and just use
context.your_variable_name anywhere the value is to be used.
For this I actually used a config file [config.py] I then added the variables in there and retrieved them using getattr. See below:
WSDL_URL = 'wsdl'
USERNAME = 'name'
PASSWORD = 'PWD'
Then retrieved them like:
import config
getattr(config, 'USERNAME ', 'username not found')

Can't find SalesForce Object

I'm having some trouble with SalesForce, I've never used it before so I'm not entirely sure what is going wrong here. I am using the simple_salesforce python module. I have successfully pulled data from SalesForce standard objects, but this custom object is giving me trouble. My query is
result = sf.query("Select Name from Call_Records__c")
which produces this error:
Traceback (most recent call last):
File "simple.py", line 15, in <module>
result = sf.query("Select Name from Call_Records__c")
File "/usr/local/lib/python2.7/dist-packages/simple_salesforce/api.py", line 276, in query
_exception_handler(result)
File "/usr/local/lib/python2.7/dist-packages/simple_salesforce/api.py", line 634, in _exception_handler
raise exc_cls(result.url, result.status_code, name, response_content)
simple_salesforce.api.SalesforceMalformedRequest: Malformed request https://sandbox.company.com/services/data/v29.0/query/?q=Select+Name+from+Call_Records__c. Response content: [{u'errorCode': u'INVALID_TYPE', u'message': u"\nSelect Name from Call_Records__c\n ^\nERROR at Row:1:Column:18\nsObject type 'Call_Records__c' is not supported. If you are attempting to use a custom object, be sure to append the '__c' after the entity name.
Please reference your WSDL or the describe call for the appropriate names."}]
I've tried it with and without the __c for both the table name and the field name, still can't figure this out. Anything blatantly wrong?
Make sure your result is Call_Records__c/CallRecords__c
Call_Records__c result = sf.query("Select Name from Call_Records__c")
Or
CallRecords__c result = sf.query("Select Name from CallRecords__c")
Try using -
result = sf.query("Select Name from CallRecords__c")

Subclass HttpResponse to get JSON output in Django

I have a number of views in a Django app that all return something like this:
return HttpResponse(jsonpickle.encode(data, unpicklable=False), 'application/json')
This works well, but I'd like to create an abstraction over the JSON encoding and creating the response object, so that I can do something like
return JsonResponse(data)
and the class JsonResponse does all the heavy lifting for me.
I tried something like this:
class JsonResponse(HttpResponse):
def __init__(self, obj):
super(HttpResponse, self).__init__(jsonpickle.encode(obj, unpicklable=False), 'application/json')
but when I have that, I get the standard A server error occurred. Please contact the administrator. when I view the web page. I've also tried with self as the first argument to the inner call to __init__ as well as with the arguments named (content and content_type respectively) and with and without a named status=200. Neither of these changes seem to change anything.
The terminal output from the dev server is a little more descriptive, but not by much:
Traceback (most recent call last):
File "/usr/lib/python2.7/wsgiref/handlers.py", line 85, in run
self.result = application(self.environ, self.start_response)
File "/usr/local/lib/python2.7/dist-packages/Django-1.5.1-py2.7.egg/django/core/handlers/wsgi.py", line 267, in __call__
start_response(force_str(status), response_headers)
File "/usr/lib/python2.7/wsgiref/handlers.py", line 175, in start_response
assert int(status[:3]),"Status message must begin w/3-digit code"
ValueError: invalid literal for int() with base 10: 'app'
[02/Jun/2013 00:51:06] "GET / HTTP/1.1" 500 59
I know I could just create a method that returns the HttpResponse instead, like so:
def json(obj):
return HttpResponse(...)
but I'd like to learn a way to do this the way I originally imagined it if it's possible - if nothing else then for my learning (and it also seems to align with the design of Django, which I like).
Is there a way to get a subclass like JsonResponse above to work? If so, what am I doing wrong?
You have to pass JsonResponse as the first argument of super, otherwise you are calling the constructor of HttpResponseBase:
class JsonResponse(HttpResponse):
def __init__(self, obj):
super(JsonResponse, self).__init__(jsonpickle.encode(obj, unpicklable=False), 'application/json')

I get NameError when extending Python's Rdio module

I've been using Python to access the Rdio API a fair bit so decided to add a couple methods to the Rdio module to make life easier. I keep getting stymied.
Here, as background, is some of the Rdio Python module provided by the company:
class Rdio:
def __init__(self, consumer, token=None):
self.__consumer = consumer
self.token = token
def __signed_post(self, url, params):
auth = om(self.__consumer, url, params, self.token)
req = urllib2.Request(url, urllib.urlencode(params), {'Authorization': auth})
res = urllib2.urlopen(req)
return res.read()
def call(self, method, params=dict()):
# make a copy of the dict
params = dict(params)
# put the method in the dict
params['method'] = method
# call to the server and parse the response
return json.loads(self.__signed_post('http://api.rdio.com/1/', params))
Okay, all well and good. Those functions work fine. So I decided to create a method that would copy a playlist with key1 into a playlist with key2. Here's the code:
def copy_playlist(self, key1, key2):
#get track keys from first playlist
playlist = self.call('get', {'keys': key1, 'extras' : 'tracks'})
track_keys = []
for track in tracks:
key = track['key']
track_keys.append(key)
#convert track list into single, comma-separated string (which the API requires)
keys_string = ', '.join(track_keys)
#add the tracks to the second playlist
self.call('addToPlaylist', {'playlist' : key2, 'tracks' : keys_string})
This code works fine if I do it from the terminal or in an external Python file, but for some reason when I include it as part of the Rdio class, then initiate the Rdio object as rdio and call the playlist method, I always get the following error:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "rdio_extended.py", line 83, in copy_playlist
NameError: global name 'rdio' is not defined
I can't seem to get around this. There's probably a simple answer - I'm pretty new to programming - but I'm stumped.
UPDATE: Updated code formatting, and here's the actual code that creates the Rdio object:
rdio = Rdio((RDIO_CONSUMER_KEY, RDIO_CONSUMER_SECRET), (RDIO_TOKEN, RDIO_TOKEN_SECRET))
And then this is the line to call the playlist-copying function:
rdio.copy_playlist(key1, key2)
That results in the NameError described above.

django populating queryset into a dictionary

am not quite advanced python user, I am stuck trying to populate the below but I think am handling list_choices wrong.
class LocationManager(TranslationManager):
def get_location_list(self, lang_code, site=None):
# this function is for building a list to be used in the posting process
# TODO: tune the query to hit database only once
list_choices = {}
for parents in self.language(lang_code).filter(country__site=site, parent=None):
list_child = ((child.id, child.name) for child in self.language(lang_code).filter(parent=parents))
list_choices.setdefault(parents).append(list_child)
return list_choices
Below the error am getting
>>>
>>> Location.objects.get_location_list(lang_code='en', site=current_site)
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/home/mo/Projects/mazban/mazban/apps/listing/geo/models.py", line 108, in get_location_list
list_choices.setdefault(parents).append(list_child)
AttributeError: 'NoneType' object has no attribute 'append'
That is because you use setdefault without a second argument. And it returns None in this case.
Try this fixed code:
# this is much more confinient and clearer
from collections import defaultdict
def get_location_list(self, lang_code, site=None):
# this function is for building a list to be used in the posting process
# TODO: tune the query to hit database only once
list_choices = defaultdict(list)
for parent in self.language(lang_code).filter(country__site=site, parent=None):
list_child = self.language(lang_code).filter(parent=parent).values_list('id', 'name')
list_choices[parent].extend(list_child)
return list_choices

Categories

Resources