Django

Code

root/django/trunk/django/core/files/base.py

Revision 8244, 4.5 kB (checked in by jacob, 4 months ago)

File storage refactoring, adding far more flexibility to Django's file handling. The new files.txt document has details of the new features.

This is a backwards-incompatible change; consult BackwardsIncompatibleChanges for details.

Fixes #3567, #3621, #4345, #5361, #5655, #7415.

Many thanks to Marty Alchin who did the vast majority of this work.

Line 
1 import os
2
3 from django.utils.encoding import smart_str, smart_unicode
4
5 try:
6     from cStringIO import StringIO
7 except ImportError:
8     from StringIO import StringIO
9
10 class File(object):
11     DEFAULT_CHUNK_SIZE = 64 * 2**10
12
13     def __init__(self, file):
14         self.file = file
15         self._name = file.name
16         self._mode = file.mode
17         self._closed = False
18
19     def __str__(self):
20         return smart_str(self.name or '')
21
22     def __unicode__(self):
23         return smart_unicode(self.name or u'')
24
25     def __repr__(self):
26         return "<%s: %s>" % (self.__class__.__name__, self or "None")
27
28     def __nonzero__(self):
29         return not not self.name
30
31     def __len__(self):
32         return self.size
33
34     def _get_name(self):
35         return self._name
36     name = property(_get_name)
37
38     def _get_mode(self):
39         return self._mode
40     mode = property(_get_mode)
41
42     def _get_closed(self):
43         return self._closed
44     closed = property(_get_closed)
45
46     def _get_size(self):
47         if not hasattr(self, '_size'):
48             if hasattr(self.file, 'size'):
49                 self._size = self.file.size
50             elif os.path.exists(self.file.name):
51                 self._size = os.path.getsize(self.file.name)
52             else:
53                 raise AttributeError("Unable to determine the file's size.")
54         return self._size
55
56     def _set_size(self, size):
57         self._size = size
58
59     size = property(_get_size, _set_size)
60
61     def chunks(self, chunk_size=None):
62         """
63         Read the file and yield chucks of ``chunk_size`` bytes (defaults to
64         ``UploadedFile.DEFAULT_CHUNK_SIZE``).
65         """
66         if not chunk_size:
67             chunk_size = self.__class__.DEFAULT_CHUNK_SIZE
68
69         if hasattr(self, 'seek'):
70             self.seek(0)
71         # Assume the pointer is at zero...
72         counter = self.size
73
74         while counter > 0:
75             yield self.read(chunk_size)
76             counter -= chunk_size
77
78     def multiple_chunks(self, chunk_size=None):
79         """
80         Returns ``True`` if you can expect multiple chunks.
81
82         NB: If a particular file representation is in memory, subclasses should
83         always return ``False`` -- there's no good reason to read from memory in
84         chunks.
85         """
86         if not chunk_size:
87             chunk_size = self.DEFAULT_CHUNK_SIZE
88         return self.size > chunk_size
89
90     def xreadlines(self):
91         return iter(self)
92
93     def readlines(self):
94         return list(self.xreadlines())
95
96     def __iter__(self):
97         # Iterate over this file-like object by newlines
98         buffer_ = None
99         for chunk in self.chunks():
100             chunk_buffer = StringIO(chunk)
101
102             for line in chunk_buffer:
103                 if buffer_:
104                     line = buffer_ + line
105                     buffer_ = None
106
107                 # If this is the end of a line, yield
108                 # otherwise, wait for the next round
109                 if line[-1] in ('\n', '\r'):
110                     yield line
111                 else:
112                     buffer_ = line
113
114         if buffer_ is not None:
115             yield buffer_
116
117     def open(self, mode=None):
118         if not self.closed:
119             self.seek(0)
120         elif os.path.exists(self.file.name):
121             self.file = open(self.file.name, mode or self.file.mode)
122         else:
123             raise ValueError("The file cannot be reopened.")
124
125     def seek(self, position):
126         self.file.seek(position)
127
128     def tell(self):
129         return self.file.tell()
130
131     def read(self, num_bytes=None):
132         if num_bytes is None:
133             return self.file.read()
134         return self.file.read(num_bytes)
135
136     def write(self, content):
137         if not self.mode.startswith('w'):
138             raise IOError("File was not opened with write access.")
139         self.file.write(content)
140
141     def flush(self):
142         if not self.mode.startswith('w'):
143             raise IOError("File was not opened with write access.")
144         self.file.flush()
145
146     def close(self):
147         self.file.close()
148         self._closed = True
149
150 class ContentFile(File):
151     """
152     A File-like object that takes just raw content, rather than an actual file.
153     """
154     def __init__(self, content):
155         self.file = StringIO(content or '')
156         self.size = len(content or '')
157         self.file.seek(0)
158         self._closed = False
159
160     def __str__(self):
161         return 'Raw content'
162
163     def __nonzero__(self):
164         return True
165
166     def open(self, mode=None):
167         if self._closed:
168             self._closed = False
169         self.seek(0)
Note: See TracBrowser for help on using the browser.