VirtualBox

source: vbox/trunk/src/libs/xpcom18a4/python/file.py@ 78197

Last change on this file since 78197 was 59798, checked in by vboxsync, 9 years ago

re-applied the Python 3 changes which were backed out in r105674 sans the changes in .cpp

  • Property svn:eol-style set to native
File size: 11.7 KB
Line 
1# ***** BEGIN LICENSE BLOCK *****
2# Version: MPL 1.1/GPL 2.0/LGPL 2.1
3#
4# The contents of this file are subject to the Mozilla Public License Version
5# 1.1 (the "License"); you may not use this file except in compliance with
6# the License. You may obtain a copy of the License at
7# http://www.mozilla.org/MPL/
8#
9# Software distributed under the License is distributed on an "AS IS" basis,
10# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11# for the specific language governing rights and limitations under the
12# License.
13#
14# The Original Code is the Python XPCOM language bindings.
15#
16# The Initial Developer of the Original Code is
17# ActiveState Tool Corp.
18# Portions created by the Initial Developer are Copyright (C) 2000, 2001
19# the Initial Developer. All Rights Reserved.
20#
21# Contributor(s):
22# Mark Hammond <[email protected]> (original author)
23#
24# Alternatively, the contents of this file may be used under the terms of
25# either the GNU General Public License Version 2 or later (the "GPL"), or
26# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27# in which case the provisions of the GPL or the LGPL are applicable instead
28# of those above. If you wish to allow use of your version of this file only
29# under the terms of either the GPL or the LGPL, and not to allow others to
30# use your version of this file under the terms of the MPL, indicate your
31# decision by deleting the provisions above and replace them with the notice
32# and other provisions required by the GPL or the LGPL. If you do not delete
33# the provisions above, a recipient may use your version of this file under
34# the terms of any one of the MPL, the GPL or the LGPL.
35#
36# ***** END LICENSE BLOCK *****
37
38"""Implementation of Python file objects for Mozilla/xpcom.
39
40Introduction:
41 This module defines various class that are implemented using
42 Mozilla streams. This allows you to open Mozilla URI's, and
43 treat them as Python file object.
44
45Example:
46>>> file = URIFile("chrome://whatever")
47>>> data = file.read(5) # Pass no arg to read everything.
48
49Known Limitations:
50 * Not all URL schemes will work from "python.exe" - most notably
51 "chrome://" and "http://" URLs - this is because a simple initialization of
52 xpcom by Python does not load up the full set of Mozilla URL handlers.
53 If you can work out how to correctly initialize the chrome registry and
54 setup a message queue.
55
56Known Bugs:
57 * Only read ("r") mode is supported. Although write ("w") mode doesnt make
58 sense for HTTP type URLs, it potentially does for file:// etc type ones.
59 * No concept of text mode vs binary mode. It appears Mozilla takes care of
60 this internally (ie, all "text/???" mime types are text, rest are binary)
61
62"""
63
64from xpcom import components, Exception, _xpcom
65import os
66import threading # for locks.
67
68NS_RDONLY = 0x01
69NS_WRONLY = 0x02
70NS_RDWR = 0x04
71NS_CREATE_FILE = 0x08
72NS_APPEND = 0x10
73NS_TRUNCATE = 0x20
74NS_SYNC = 0x40
75NS_EXCL = 0x80
76
77# A helper function that may come in useful
78def LocalFileToURL(localFileName):
79 "Convert a filename to an XPCOM nsIFileURL object."
80 # Create an nsILocalFile
81 localFile = components.classes["@mozilla.org/file/local;1"] \
82 .createInstance(components.interfaces.nsILocalFile)
83 localFile.initWithPath(localFileName)
84
85 # Use the IO Service to create the interface, then QI for a FileURL
86 io_service = components.classes["@mozilla.org/network/io-service;1"] \
87 .getService(components.interfaces.nsIIOService)
88 url = io_service.newFileURI(localFile).queryInterface(components.interfaces.nsIFileURL)
89 # Setting the "file" attribute causes initialization...
90 url.file = localFile
91 return url
92
93# A base class for file objects.
94class _File:
95 def __init__(self, name_thingy = None, mode="r"):
96 self.lockob = threading.Lock()
97 self.inputStream = self.outputStream = None
98 if name_thingy is not None:
99 self.init(name_thingy, mode)
100
101 def __del__(self):
102 self.close()
103
104 # The Moz file streams are not thread safe.
105 def _lock(self):
106 self.lockob.acquire()
107 def _release(self):
108 self.lockob.release()
109 def read(self, n = -1):
110 assert self.inputStream is not None, "Not setup for read!"
111 self._lock()
112 try:
113 return str(self.inputStream.read(n))
114 finally:
115 self._release()
116
117 def readlines(self):
118 # Not part of the xpcom interface, but handy for direct Python users.
119 # Not 100% faithful, but near enough for now!
120 lines = self.read().split("\n")
121 if len(lines) and len(lines[-1]) == 0:
122 lines = lines[:-1]
123 return [s+"\n" for s in lines ]
124
125 def write(self, data):
126 assert self.outputStream is not None, "Not setup for write!"
127 self._lock()
128 try:
129 self.outputStream.write(data, len(data))
130 finally:
131 self._release()
132
133 def close(self):
134 self._lock()
135 try:
136 if self.inputStream is not None:
137 self.inputStream.close()
138 self.inputStream = None
139 if self.outputStream is not None:
140 self.outputStream.close()
141 self.outputStream = None
142 self.channel = None
143 finally:
144 self._release()
145
146 def flush(self):
147 self._lock()
148 try:
149 if self.outputStream is not None: self.outputStream.flush()
150 finally:
151 self._release()
152
153# A synchronous "file object" used to open a URI.
154class URIFile(_File):
155 def init(self, url, mode="r"):
156 self.close()
157 if mode != "r":
158 raise ValueError("only 'r' mode supported")
159 io_service = components.classes["@mozilla.org/network/io-service;1"] \
160 .getService(components.interfaces.nsIIOService)
161 if hasattr(url, "queryInterface"):
162 url_ob = url
163 else:
164 url_ob = io_service.newURI(url, None, None)
165 # Mozilla asserts and starts saying "NULL POINTER" if this is wrong!
166 if not url_ob.scheme:
167 raise ValueError("The URI '%s' is invalid (no scheme)"
168 % (url_ob.spec,))
169 self.channel = io_service.newChannelFromURI(url_ob)
170 self.inputStream = self.channel.open()
171
172# A "file object" implemented using Netscape's native file support.
173# Based on io.js - http://lxr.mozilla.org/seamonkey/source/xpcom/tests/utils/io.js
174# You open this file using a local file name (as a string) so it really is pointless -
175# you may as well be using a standard Python file object!
176class LocalFile(_File):
177 def __init__(self, *args):
178 self.fileIO = None
179 _File.__init__(self, *args)
180
181 def init(self, name, mode = "r"):
182 name = os.path.abspath(name) # Moz libraries under Linux fail with relative paths.
183 self.close()
184 file = components.classes['@mozilla.org/file/local;1'].createInstance("nsILocalFile")
185 file.initWithPath(name)
186 if mode in ["w","a"]:
187 self.fileIO = components.classes["@mozilla.org/network/file-output-stream;1"].createInstance("nsIFileOutputStream")
188 if mode== "w":
189 if file.exists():
190 file.remove(0)
191 moz_mode = NS_CREATE_FILE | NS_WRONLY
192 elif mode=="a":
193 moz_mode = NS_APPEND
194 else:
195 assert 0, "Can't happen!"
196 self.fileIO.init(file, moz_mode, -1,0)
197 self.outputStream = self.fileIO
198 elif mode == "r":
199 self.fileIO = components.classes["@mozilla.org/network/file-input-stream;1"].createInstance("nsIFileInputStream")
200 self.fileIO.init(file, NS_RDONLY, -1,0)
201 self.inputStream = components.classes["@mozilla.org/scriptableinputstream;1"].createInstance("nsIScriptableInputStream")
202 self.inputStream.init(self.fileIO)
203 else:
204 raise ValueError("Unknown mode")
205
206 def close(self):
207 if self.fileIO is not None:
208 self.fileIO.close()
209 self.fileIO = None
210 _File.close(self)
211
212 def read(self, n = -1):
213 return _File.read(self, n)
214
215
216##########################################################
217##
218## Test Code
219##
220##########################################################
221def _DoTestRead(file, expected):
222 # read in a couple of chunks, just to test that our various arg combinations work.
223 got = file.read(3)
224 got = got + file.read(300)
225 got = got + file.read(0)
226 got = got + file.read()
227 if got != expected:
228 raise RuntimeError("Reading '%s' failed - got %d bytes, but expected %d bytes" % (file, len(got), len(expected)))
229
230def _DoTestBufferRead(file, expected):
231 # read in a couple of chunks, just to test that our various arg combinations work.
232 buffer = _xpcom.AllocateBuffer(50)
233 got = ''
234 while 1:
235 # Note - we need to reach into the file object so we
236 # can get at the native buffer supported function.
237 num = file.inputStream.read(buffer)
238 if num == 0:
239 break
240 got = got + str(buffer[:num])
241 if got != expected:
242 raise RuntimeError("Reading '%s' failed - got %d bytes, but expected %d bytes" % (file, len(got), len(expected)))
243
244def _TestLocalFile():
245 import tempfile, os
246 fname = tempfile.mktemp()
247 data = "Hello from Python"
248 test_file = LocalFile(fname, "w")
249 try:
250 test_file.write(data)
251 test_file.close()
252 # Make sure Python can read it OK.
253 f = open(fname, "r")
254 assert f.read() == data, "Eeek - Python could not read the data back correctly!"
255 f.close()
256 # For the sake of the test, try a re-init.
257 test_file.init(fname, "r")
258 got = str(test_file.read())
259 assert got == data, got
260 test_file.close()
261 # Try reading in chunks.
262 test_file = LocalFile(fname, "r")
263 got = test_file.read(10) + test_file.read()
264 assert got == data, got
265 test_file.close()
266 # Open the same file again for writing - this should delete the old one.
267 if not os.path.isfile(fname):
268 raise RuntimeError("The file '%s' does not exist, but we are explicitly testing create semantics when it does" % (fname,))
269 test_file = LocalFile(fname, "w")
270 test_file.write(data)
271 test_file.close()
272 # Make sure Python can read it OK.
273 f = open(fname, "r")
274 assert f.read() == data, "Eeek - Python could not read the data back correctly after recreating an existing file!"
275 f.close()
276
277 # XXX - todo - test "a" mode!
278 finally:
279 os.unlink(fname)
280
281def _TestAll():
282 # A mini test suite.
283 # Get a test file, and convert it to a file:// URI.
284 # check what we read is the same as when
285 # we read this file "normally"
286 fname = components.__file__
287 if fname[-1] in "cCoO": # fix .pyc/.pyo
288 fname = fname[:-1]
289 expected = open(fname, "rb").read()
290 # convert the fname to a URI.
291 url = LocalFileToURL(fname)
292 # First try passing a URL as a string.
293 _DoTestRead( URIFile( url.spec), expected)
294 # Now with a URL object.
295 _DoTestRead( URIFile( url ), expected)
296
297 _DoTestBufferRead( URIFile( url ), expected)
298
299 # For the sake of testing, do our pointless, demo object!
300 _DoTestRead( LocalFile(fname), expected )
301
302 # Now do the full test of our pointless, demo object!
303 _TestLocalFile()
304
305def _TestURI(url):
306 test_file = URIFile(url)
307 print("Opened file is", test_file)
308 got = test_file.read()
309 print("Read %d bytes of data from %r" % (len(got), url))
310 test_file.close()
311
312if __name__=='__main__':
313 import sys
314 if len(sys.argv) < 2:
315 print("No URL specified on command line - performing self-test")
316 _TestAll()
317 else:
318 _TestURI(sys.argv[1])
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette