I created a subclass of the Django File object to take care of remote file. I also wanted to make a lazy version by creating a RemoteFileLazy subclassing the Lazyobject class but it does not work as I expected. I get an error.
import urllib2
from django.core.files.base import File
from django.utils.functional import LazyObject
class RemoteFile(File):
def __init__(self, url):
super(RemoteFile, self).__init__(urllib2.urlopen(urllib2.Request(url)))
def __str__(self):
return 'Remote content'
def __nonzero__(self):
return True
def open(self, mode=None):
self.seek(0)
def close(self):
pass
def chunks(self, chunk_size=None):
# CHUNKS taking care of no known size!
if not chunk_size:
chunk_size = self.DEFAULT_CHUNK_SIZE
if hasattr(self, 'seek'):
self.seek(0)
# Assume the pointer is at zero...
counter = self.size
while True:
data = self.read(chunk_size)
if not data:
break
yield data
class RemoteFileLazy(LazyObject):
def __init__(self, url):
# # as said in the django code: For some reason, we have to inline LazyObject.__init__ here to avoid
# recursion
self._wrapped = None
self.url = url
def _setup(self):
self._wrapped = RemoteFile(self.url)
When I run this code:
rfl = filehelper.RemoteFileLazy(url="http://www.google.fr")
I got this error:
RuntimeError: maximum recursion depth exceeded
Any idea ? I did not call LazyObject.init as it was mentioned in the django code though... I think the "self.url = url" in the init method triggers this error right ? So I cannot use a lazy object with attributes ?
Thanks.
Traceback:
c:\Users\Michael\Dropbox\development\tools\Portable Python 2.7.2.1-django1.3.1\App\lib\site-packages\django\utils\functional.pyc in __getattr__(self, name)
274 def __getattr__(self, name):
275 if self._wrapped is None:
--> 276 self._setup()
277 return getattr(self._wrapped, name)
278
C:\Users\Michael\Dropbox\development\projects\django-socialdealing\socialdealing\apps\etl\utils\filehelper.py in _setup(self)
58
59 def _setup(self):
---> 60 self._wrapped = RemoteFile(self.url)
61
62
c:\Users\Michael\Dropbox\development\tools\Portable Python 2.7.2.1-django1.3.1\App\lib\site-packages\django\utils\functional.pyc in __getattr__(self, name)
274 def __getattr__(self, name):
275 if self._wrapped is None:
--> 276 self._setup()
277 return getattr(self._wrapped, name)
278
You can't assign attributes on a LazyObject wrapper in the normal way, it's intended to be treated as it it is the wrapped object, so tries to pass accesses through to the wrapped object, which hasn't been created yet at the point you assign to url
.
To fix it, replace
self.url = url # this tries to retrieve the wrapped object to set its 'url' attribute
with
self.__dict__['url'] = url # this actually stores an attribute on the LazyObject container.
As an aside, when 'borrowing' django internals like this you will want to test really hard every time you upgrade django.