web.py sessions: AttributeError: 'ThreadedDict' object has no attribute 'count' - python

I just ran this simple code snippet provided by the wiki, because I couldn't get sessions working:
import web
web.config.debug = False
urls = (
"/count", "count",
"/reset", "reset"
)
app = web.application(urls, locals())
session = web.session.Session(app, web.session.DiskStore('sessions'), initializer={'count': 0})
class count:
def GET(self):
session.count += 1
return str(session.count)
class reset:
def GET(self):
session.kill()
return ""
if __name__ == "__main__":
app.run()
But it results in this error:
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/web/application.py", line 237, in process
return self.handle()
File "/usr/local/lib/python2.7/dist-packages/web/application.py", line 228, in handle
return self._delegate(fn, self.fvars, args)
File "/usr/local/lib/python2.7/dist-packages/web/application.py", line 411, in _delegate
return handle_class(cls)
File "/usr/local/lib/python2.7/dist-packages/web/application.py", line 387, in handle_class
return tocall(*args)
File "temp.py", line 12, in GET
session.count += 1
File "/usr/local/lib/python2.7/dist-packages/web/session.py", line 71, in __getattr__
return getattr(self._data, name)
AttributeError: 'ThreadedDict' object has no attribute 'count'
Is webpy not compatible with 2.7.3? I'm running this on the internal webserver of webpy. I'm using Ubuntu 12.04.

session.count += 1 is equal to session.count = session.count + 1 so session.count must exist for this to work.
Add the following check to make it work:
if 'count' not in session:
session.count = 0
session.count += 1
There is also another way which is even shown in the very simple session simple example of the docs:
try:
s.click += 1
except AttributeError:
s.click = 1

Ok for the trick of try...except also i'm not convinced it's the best way to do so (not clean at all).
Like previously said, Session constructor offer a way to initialize the session's variable.
I'm not really sure we can rely on the "very simple session simple example".
First, we hardly have any explication on the purpose of various variablse. For instance, what is the purpose the *db_parameter* dict ?
Last but not least, it needs a serious update. The provided code simply didn't work with the actual framework.
There is simply no web.ctx.session.
By the way, I implemented a simple counter like in the example.
The displayed error you had is due to a drastic change in the Session's API.
You cannot just call "counter" from your session.
That would be more smthg like that: session.store.store_instance.get('counter') .Where store_instance is either a shelf or a db.
Like i said, the official documentation needs a serious update.
That said, I noticed that this is not the same for the docstring. To progress i start Ipython and i see every posibilities I have.
I know it's pure guessing but naming is good so we can figure out what to do.
I will submit my example to the team of web.py so they can update the official doc.

Until my pull request get accepted on GitHub, i post the snippet of code illustrating the use of a simple incrementor:
import web
import shelve
urls = (
'/add', 'counter',
'/reset', 'reset'
)
shelf = shelve.open('session')
shelfStore = web.session.ShelfStore(shelf)
app = web.application(urls, globals())
s = web.session.Session(app, shelfStore)
class counter:
def GET(self):
numberToAdd = web.input().get('number')
if not numberToAdd:
numberToAdd = 1
try:
print numberToAdd
s.store.shelf["count"] += int(numberToAdd)
except Exception:
s.store.shelf["count"] = 1
return s.store.shelf.get("count")
class reset:
def GET(self):
s.store.shelf.clear()
if __name__ == "__main__":
app.run()

The problem is the python version. I had the same problem and I solved it when I executed 2.7 version of python. Just doing > python2.7 code.py and the sessions works perfectly.
It's a pity that the doc to web.py is very poor.

Related

Flask cache set method throwing KeyError?

In order to cache some data, I am calling cache.set method. However it is throwing KeyError. The error log:
File "D:\Sample_Project\SomeRouter.py", line 176, in run
FetchFeed._prepareCache(f_content, cache, _program, _sprint)
File "D:\Sample_Project\SomeRouter.py", line 197, in _prepareCache
cache.set(cKey, data[last_index:last_index + MAX_DATA_PER_PAGE])
File "c:\python\lib\site-packages\flask_caching\__init__.py", line 254, in set
return self.cache.set(*args, **kwargs)
File "c:\python\lib\site-packages\flask_caching\__init__.py", line 246, in cache
return app.extensions["cache"][self]
KeyError: <flask_caching.Cache object at 0x04F9C8D0>
The server module looks like :
cache_type = 'simple' if 'FLASK_ENV' in os.environ and os.environ['FLASK_ENV'] == 'development' else 'uwsgi'
cache = Cache(config={'CACHE_TYPE': cache_type})
app = Flask("MY_PROJECT")
cache.init_app(app)
# some api.route functions
# goes here ....
if __name__ == "__main__":
with app.app_context():
cache.clear()
app.run(host="0.0.0.0")
And SomeRouter module:
from server import cache
#staticmethod
def _prepareCache(data, cache, program):
total_records = len(data)
if total_records > 0:
cKey = FetchFeed \
._constructCacheKey(program)
cache.set(cKey, data)
else:
print("data size is empty.")
Note: I have removed unnecessary codes.
I also put breakpoints, and called cache.set(some_key, some_value) in server module itself. It is returning True, but same cache object is throwing KeyError when imported and used in SomeRouter module. Can it be that the way I am importing an object is wrong? I also tried importing the cache object right before using it, but it did not work. Any idea what is going on here?
The problem was I was accessing cache object outside request context i.e. in "SomeRouter" module, and because of which it was unaware that under which context it is been used.
In server module where request has been received, the cache knows about the app application, but during cache.set(cKey, data) in SomeRouter module, it throws KeyError. This error is justified as mentioned above.
The resolution is to push the application context as below:
from server import app, cache
# Using context
with app.app_context():
cache.set(cKey, data)
This will push a new application context (using the application of app).
All thanks to Mark Hildreth, for his great answer on context in flask

Can I patch a static method in python?

I've a class in python that contains a static method. I want to mock.patch it in order to see if it was called. When trying to do it I get an error:
AttributeError: path.to.A does not have the attribute 'foo'
My setup can be simplified to:
class A:
#staticMethod
def foo():
bla bla
Now the test code that fails with error:
def test():
with mock.patch.object("A", "foo") as mock_helper:
mock_helper.return_value = ""
A.some_other_static_function_that_could_call_foo()
assert mock_helper.call_count == 1
You can always use patch as a decorator, my preferred way of patching things:
from mock import patch
#patch('absolute.path.to.class.A.foo')
def test(mock_foo):
mock_foo.return_value = ''
# ... continue with test here
EDIT: Your error seems to hint that you have a problem elsewhere in your code. Possibly some signal or trigger that requires this method that is failing?
I was getting that same error message when trying to patch a method using the #patch decorator.
Here is the full error I got.
Traceback (most recent call last):
File "/usr/local/lib/python3.6/site-packages/tornado/testing.py", line 136, in __call__
result = self.orig_method(*args, **kwargs)
File "/usr/local/lib/python3.6/unittest/mock.py", line 1171, in patched
arg = patching.__enter__()
File "/usr/local/lib/python3.6/unittest/mock.py", line 1243, in __enter__
original, local = self.get_original()
File "/usr/local/lib/python3.6/unittest/mock.py", line 1217, in get_original
"%s does not have the attribute %r" % (target, name)
AttributeError: <module 'py-repo.models.Device' from
'/usr/share/projects/py-repo/models/Device.py'> does not have the attribute 'get_device_from_db'
What I ended up doing to fix this was changing the patch decorator I used
from
#patch('py-repo.models.Device.get_device_from_db')
to #patch.object(DeviceModel, 'get_device_from_db')
I really wish I could explain further why that was the issue but I'm still pretty new to Python myself. The patch documentation was especially helpful in figuring out what was available to work with. Important: I should note that get_device_from_db uses the #staticmethod decorator which may be changing things. Hope it helps though.
What worked for me:
#patch.object(RedisXComBackend, '_handle_conn')
def test_xcoms(self, mock_method: MagicMock):
mock_method.return_value = fakeredis.FakeStrictRedis()
'_handle_conn' (static function) looks like this:
#staticmethod
def _handle_conn():
redis_hook = RedisHook()
conn: Redis = redis_hook.get_conn()

flask View function mapping is overwriting an existing endpoint function: type

I recently purchased RealPython to learn about Python and web development. However, I have ran into a road block that I think is a Python configuration issue on my machine. Any help would be much obliged.
So I have a Flask document called app.py similar to RealPython's github app.py
# --- Flask Hello World ---#
# import the Flask class from the flask module
from flask import Flask
# create the application object
app = Flask(__name__)
# use decorators to link the function to a url
#app.route("/")
#app.route("/hello")
# define the view using a function, which returns a string
def hello_world():
return "Hello, World!"
# dynamic route
#app.route("/test/<search_query>")
def search(search_query):
return search_query
# dynamic route with an int type
#app.route("/integer/<int:value>")
def type(value):
print value + 1
return "correct"
# dynamic route with an float type
#app.route("/float/<float:value>")
def type(value):
print value + 1
return "correct"
# dynamic route that accepts slashes
#app.route("/path/<path:value>")
def type(value):
print value
return "correct"
# start the development server using the run() method
if __name__ == "__main__":
app.run()
I unfortunately receive this error when trying to run the app:
machine:flask-hello-world machine$ source env/bin/activate
(env)machine:flask-hello-world machine$ python app.py
Traceback (most recent call last):
File "app.py", line 29, in <module>
#app.route("/float/<float:value>")
File "/Volumes/disk2/Home/Library/RealPython/flask-hello-world/env/lib/python2.7/site-packages/flask/app.py", line 1013, in decorator
self.add_url_rule(rule, endpoint, f, **options)
File "/Volumes/disk2/Home/Library/RealPython/flask-hello-world/env/lib/python2.7/site-packages/flask/app.py", line 62, in wrapper_func
return f(self, *args, **kwargs)
File "/Volumes/disk2/Home/Library/RealPython/flask-hello-world/env/lib/python2.7/site-packages/flask/app.py", line 984, in add_url_rule
'existing endpoint function: %s' % endpoint)
AssertionError: View function mapping is overwriting an existing endpoint function: type
Pip freeze gives these as requirements for virtualenv env. Python 2.7 is what is installed.
Flask==0.10.1
Jinja2==2.7.3
MarkupSafe==0.23
Werkzeug==0.9.6
itsdangerous==0.24
wsgiref==0.1.2
The only way I've been able to get the code to run is by changing def type. One should not have to do this though...
# dynamic route with an int type
#app.route("/integer/<int:value>")
def type(value):
print value + 1
return "correct"
# dynamic route with an float type
# change to type1 so dev server will spool up
#app.route("/float/<float:value>")
def type1(value):
print value + 1
return "correct"
# dynamic route that accepts slashes
# change to type2 so dev server will spool up
#app.route("/path/<path:value>")
def type2(value):
print value
return "correct"
Solution
So, you figured out the solution: It's a namespace issue. You have three functions that are conflicting against one another - def type. When you renamed them using different names, this fixed the issue.
I am the author of Real Python, by the way. Correcting now.
Cheers!
three of your methods have the same name. The wrappers use the name of the method to do their mapping
You can have multiple maps point to the same method:
#app.route("/integer/<int:value>")
#app.route("/float/<float:value>")
def var_type(value):
print value + 1
return "correct"
You should not name your method type as it is the name of the built-in type class:
Help on class type in module __builtin__:
class type(object)
| type(object) -> the object's type
| type(name, bases, dict) -> a new type

Pyramid with memcached: how to make it work? Error - MissingCacheParameter: url is required

I have site on Pyramid framework and want to cache with memcached. For testing reasons I've used memory type caching and everything was OK. I'm using pyramid_beaker package.
Here is my previous code (working version).
In .ini file
cache.regions = day, hour, minute, second
cache.type = memory
cache.second.expire = 1
cache.minute.expire = 60
cache.hour.expire = 3600
cache.day.expire = 86400
In views.py:
from beaker.cache import cache_region
#cache_region('hour')
def get_popular_users():
#some code to work with db
return some_dict
The only .ini settings I've found in docs were about working with memory and file types of caching. But I need to work with memcached.
First of all I've installed package memcached from Ubuntu official repository and also python-memcached to my virtualenv.
In .ini file I've replaced cache.type = memory -> cache.type = memcached. And I've got next error:
beaker.exceptions.MissingCacheParameter
MissingCacheParameter: url is required
What am I doing wrong?
Thanks in advance!
So, using the TurboGears documentation as a guide, what settings do you have for the url?
[app:main]
beaker.cache.type = ext:memcached
beaker.cache.url = 127.0.0.1:11211
# you can also store sessions in memcached, should you wish
# beaker.session.type = ext:memcached
# beaker.session.url = 127.0.0.1:11211
It looks to me as if memcached requires a url to initialize correctly:
def __init__(self, namespace, url=None, data_dir=None, lock_dir=None, **params):
NamespaceManager.__init__(self, namespace)
if not url:
raise MissingCacheParameter("url is required")
I am not really sure why the code allows url to be optional (defaulting to None) and then requires it. I think it would have been simpler just to require the url as an argument.
Later: in response to your next question:
when I used cache.url I've got next error: AttributeError:
'MemcachedNamespaceManager' object has no attribute 'lock_dir'
I'd say that the way I read the code below, you have to provide either lock_dir or data_dir to initialize self.lock_dir:
if lock_dir:
self.lock_dir = lock_dir
elif data_dir:
self.lock_dir = data_dir + "/container_mcd_lock"
if self.lock_dir:
verify_directory(self.lock_dir)
You can replicate that exact error using this test code:
class Foo(object):
def __init__(self, lock_dir=None, data_dir=None):
if lock_dir:
self.lock_dir = lock_dir
elif data_dir:
self.lock_dir = data_dir + "/container_mcd_lock"
if self.lock_dir:
verify_directory(self.lock_dir)
f = Foo()
It turns out like this:
>>> f = Foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in __init__
AttributeError: 'Foo' object has no attribute 'lock_dir'

String error in my python api wrapper class

I'm writing an API wrapper to a couple of different web services.
I have a method that has an article url, and I want to extract text from it using alchemyapi.
def extractText(self):
#All Extract Text Methods ---------------------------------------------------------//
#Extract page text from a web URL (ignoring navigation links, ads, etc.).
if self.alchemyapi == True:
self.full_text = self.alchemyObj.URLGetText(self.article_link)
which goes to the following code in the python wrapper
def URLGetText(self, url, textParams=None):
self.CheckURL(url)
if textParams == None:
textParams = AlchemyAPI_TextParams()
textParams.setUrl(url)
return self.GetRequest("URLGetText", "url", textParams)
def GetRequest(self, apiCall, apiPrefix, paramObject):
endpoint = 'http://' + self._hostPrefix + '.alchemyapi.com/calls/' + apiPrefix + '/' + apiCall
endpoint += '?apikey=' + self._apiKey + paramObject.getParameterString()
handle = urllib.urlopen(endpoint)
result = handle.read()
handle.close()
xpathQuery = '/results/status'
nodes = etree.fromstring(result).xpath(xpathQuery)
if nodes[0].text != "OK":
raise 'Error making API call.'
return result
However I get this error ---
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "text_proc.py", line 97, in __init__
self.alchemyObj.loadAPIKey("api_key.txt");
File "text_proc.py", line 115, in extractText
if self.alchemyapi == True:
File "/Users/Diesel/Desktop/AlchemyAPI.py", line 502, in URLGetText
return self.GetRequest("URLGetText", "url", textParams)
File "/Users/Diesel/Desktop/AlchemyAPI.py", line 618, in GetRequest
raise 'Error making API call.'
I know I'm somehow passing the url string to the api wrapper in a faulty format, but I can't figure out how to fix it.
The information provided is not actually very helpful to diagnose or solve the problem. Have you considered taking a look at the response from the server? You might inspect a complete traffic log using Fiddler.
Additionally, the SDK provided by Alchemy doesn't seem to be of - cough, cough - the greatest quality. Since it really consists only of around 600 lines of source code, I'd consider writing a shorter, more robust / pythonic / whatever SDK.
I might also add that right now, even the on-site demo at the Alchemy web site is failing, so maybe your problem is related to that. I really suggest taking a look at the traffic.
You should raise Exception or a subclass thereof, instead of a string.
You're getting the error because your function GetRequest() raising a string as an exception:
if nodes[0].text != "OK":
raise 'Error making API call.'
If that's not what you want, you have two options:
You can have the function return the string or None, or
You can pass the error message to a real subclass of Exception (as suggested by knutin)
In either case, if you are assigning that return value to a variable, you can handle it accordingly. Here is an example:
Option 1
Let's assume you decide to have GetRequest() return None:
def URLGetText(self, url, textParams=None):
self.CheckURL(url)
if textParams == None:
textParams = AlchemyAPI_TextParams()
textParams.setUrl(url)
# Capture the value of GetRequest() before returning it
retval = self.GetRequest("URLGetText", "url", textParams)
if retval is None:
print 'Error making API call.' # print the error but still return
return retval
def GetRequest(self, apiCall, apiPrefix, paramObject):
# ...
if nodes[0].text != "OK":
return None
return result
This option is a little ambiguous. How do you know that it was really an error, or the return value truly was None?
Option 2
This is probably the better way to do it:
First create an subclass of Exception:
class GetRequestError(Exception):
"""Error returned from GetRequest()"""
pass
Then raise it in GetRequest()`:
def URLGetText(self, url, textParams=None):
self.CheckURL(url)
if textParams == None:
textParams = AlchemyAPI_TextParams()
textParams.setUrl(url)
# Attempt to get a legit return value & handle errors
try:
retval = self.GetRequest(apiCall, apiPrefix, paramObject)
except GetRequestError as err:
print err # prints 'Error making API call.'
# handle the error here
retval = None
return retval
def GetRequest(self, apiCall, apiPrefix, paramObject):
# ...
if nodes[0].text != "OK":
raise GetRequestError('Error making API call.')
return result
This way you're raising a legitimate error when GetRequest() doesn't return the desired result, and then you can handle the error using a try..except block and optionally print the error, stop the program there, or keep going (which is what I think you want to do based on your question).
This is Shaun from AlchemyAPI. We just posted a new version of the python SDK that raises exceptions properly. You can get it here http://www.alchemyapi.com/tools/.
If you have any other feedback about the SDK, please message me. Thanks for using our NLP service.

Categories