1 | Thu Jul 15 06:31:15 GMT Daylight Time 2010 david-sarah@jacaranda.org |
---|
2 | * Changes to Tahoe needed to work with new zetuptoolz (that does not use .exe wrappers on Windows), and to support Unicode arguments and stdout/stderr -- v4. |
---|
3 | |
---|
4 | New patches: |
---|
5 | |
---|
6 | [Changes to Tahoe needed to work with new zetuptoolz (that does not use .exe wrappers on Windows), and to support Unicode arguments and stdout/stderr -- v4. |
---|
7 | david-sarah@jacaranda.org**20100715053115 |
---|
8 | Ignore-this: 8bcab59c644bda66493c9e51e91ccc29 |
---|
9 | ] { |
---|
10 | hunk ./bin/tahoe-script.template 8 |
---|
11 | where = os.path.realpath(sys.argv[0]) |
---|
12 | base = os.path.dirname(os.path.dirname(where)) |
---|
13 | |
---|
14 | +if sys.platform == "win32": |
---|
15 | + installed_tahoe = os.path.join(os.path.dirname(sys.executable), 'Scripts', 'tahoe.pyscript') |
---|
16 | +else: |
---|
17 | + installed_tahoe = "/usr/bin/tahoe" |
---|
18 | + |
---|
19 | whoami = '''\ |
---|
20 | I am a "bin/tahoe" executable who is only for the convenience of running |
---|
21 | Tahoe from its source distribution -- I work only when invoked as the "tahoe" |
---|
22 | hunk ./bin/tahoe-script.template 27 |
---|
23 | print '''\ |
---|
24 | I just tried to run and found that I am not living in such a directory, so I |
---|
25 | am stopping now. To run Tahoe after it has been is installed, please execute |
---|
26 | -my brother, also named "tahoe", who gets installed into the appropriate place |
---|
27 | -for executables when you run "make install" (perhaps as /usr/bin/tahoe). |
---|
28 | -''' |
---|
29 | +my brother, who gets installed into the appropriate place for executables |
---|
30 | +when you run "make install" (perhaps as %s). |
---|
31 | +''' % (installed_tahoe,) |
---|
32 | sys.exit(1) |
---|
33 | |
---|
34 | # we've found our home. Put the tahoe support/lib etc. in our PYTHONPATH. |
---|
35 | hunk ./bin/tahoe-script.template 49 |
---|
36 | pp = supportdir |
---|
37 | os.environ["PYTHONPATH"] = pp |
---|
38 | |
---|
39 | -# find the location of the tahoe executable. |
---|
40 | -bin_dir = "bin" |
---|
41 | +# find commandline args and the location of the tahoe executable. |
---|
42 | if sys.platform == "win32": |
---|
43 | hunk ./bin/tahoe-script.template 51 |
---|
44 | - bin_dir = "Scripts" |
---|
45 | -executable = os.path.join(base, "support", bin_dir, "tahoe") |
---|
46 | + import re |
---|
47 | + from ctypes import WINFUNCTYPE, POINTER, byref, c_wchar_p, c_int, windll |
---|
48 | + |
---|
49 | + GetCommandLineW = WINFUNCTYPE(c_wchar_p)(("GetCommandLineW", windll.kernel32)) |
---|
50 | + CommandLineToArgvW = WINFUNCTYPE(POINTER(c_wchar_p), c_wchar_p, POINTER(c_int)) \ |
---|
51 | + (("CommandLineToArgvW", windll.shell32)) |
---|
52 | + |
---|
53 | + argc = c_int(0) |
---|
54 | + argv_unicode = CommandLineToArgvW(GetCommandLineW(), byref(argc)) |
---|
55 | + |
---|
56 | + # See src/allmydata/scripts/runner.py for the corresponding unmangler. |
---|
57 | + # Note that this doesn't escape \x7F. If it did, test_unicode_arguments_and_output |
---|
58 | + # in test_runner.py wouldn't work. |
---|
59 | + def mangle(s): |
---|
60 | + return str(re.sub(ur'[^\x20-\x7F]', lambda m: u'\x7F%x;' % (ord(m.group(0)),), s)) |
---|
61 | + |
---|
62 | + argv = [mangle(argv_unicode[i]) for i in xrange(1, argc.value)] |
---|
63 | + local_tahoe = "Scripts\\tahoe.pyscript" |
---|
64 | +else: |
---|
65 | + argv = sys.argv |
---|
66 | + local_tahoe = "bin/tahoe" |
---|
67 | + |
---|
68 | +script = os.path.join(base, "support", local_tahoe) |
---|
69 | |
---|
70 | try: |
---|
71 | hunk ./bin/tahoe-script.template 76 |
---|
72 | - res = subprocess.call([executable] + sys.argv[1:], env=os.environ) |
---|
73 | + res = subprocess.call([sys.executable, script] + argv[1:], env=os.environ) |
---|
74 | except (OSError, IOError), le: |
---|
75 | if le.args[0] == errno.ENOENT: |
---|
76 | print whoami |
---|
77 | hunk ./bin/tahoe-script.template 80 |
---|
78 | + |
---|
79 | print '''\ |
---|
80 | I just tried to run and could not find my brother, named |
---|
81 | hunk ./bin/tahoe-script.template 83 |
---|
82 | -"../support/bin/tahoe". To run Tahoe when it is installed, please execute my |
---|
83 | -brother, also named "tahoe", who gets installed into the appropriate place |
---|
84 | -for executables when you run "make install" (perhaps as /usr/bin/tahoe). |
---|
85 | -''' |
---|
86 | +"../support/%s". To run Tahoe when it is installed, please execute my |
---|
87 | +brother, who gets installed into the appropriate place for executables |
---|
88 | +when you run "make install" (perhaps as %s). |
---|
89 | +''' % (local_tahoe, installed_tahoe) |
---|
90 | raise |
---|
91 | except Exception, le: |
---|
92 | print whoami |
---|
93 | hunk ./bin/tahoe-script.template 91 |
---|
94 | print '''\ |
---|
95 | -I just tried to invoke my brother, named "../support/bin/tahoe" and got an |
---|
96 | +I just tried to invoke my brother, named "../support/%s" and got an |
---|
97 | exception. |
---|
98 | hunk ./bin/tahoe-script.template 93 |
---|
99 | -''' |
---|
100 | +''' % (local_tahoe,) |
---|
101 | raise |
---|
102 | else: |
---|
103 | sys.exit(res) |
---|
104 | hunk ./docs/quickstart.html 34 |
---|
105 | |
---|
106 | <p>Unpack the zip file and cd into the top-level directory.</p> |
---|
107 | |
---|
108 | + <p>If you are on Windows and have not already done so, run <cite>python setup.py winsetup</cite>. This makes the registry and environment changes needed to run the <cite>tahoe</cite> command-line script. You may need to accept an elevation or other confirmation prompt.</p> |
---|
109 | + |
---|
110 | <p>Run <cite>python setup.py build</cite> to generate the <cite>tahoe</cite> executable in a subdirectory of the current directory named <cite>bin</cite>.</p> |
---|
111 | |
---|
112 | <p>Optionally run <cite>python setup.py test</cite> to verify that it passes all of its self-tests.</p> |
---|
113 | hunk ./setup.py 238 |
---|
114 | def run(self): |
---|
115 | bin_tahoe_template = os.path.join("bin", "tahoe-script.template") |
---|
116 | |
---|
117 | - # Create the 'tahoe-script.py' file under the 'bin' directory. The |
---|
118 | - # 'tahoe-script.py' file is exactly the same as the |
---|
119 | - # 'tahoe-script.template' script except that the shebang line is |
---|
120 | - # rewritten to use our sys.executable for the interpreter. On |
---|
121 | - # Windows, create a tahoe.exe will execute it. On non-Windows, make a |
---|
122 | - # symlink to it from 'tahoe'. The tahoe.exe will be copied from the |
---|
123 | - # setuptools egg's cli.exe and this will work from a zip-safe and |
---|
124 | - # non-zip-safe setuptools egg. |
---|
125 | + if sys.platform == 'win32': |
---|
126 | + # 'tahoe' script is needed for cygwin |
---|
127 | + script_names = ["tahoe.pyscript", "tahoe"] |
---|
128 | + else: |
---|
129 | + script_names = ["tahoe"] |
---|
130 | + |
---|
131 | + # Create the tahoe script file under the 'bin' directory. This |
---|
132 | + # file is exactly the same as the 'tahoe-script.template' script |
---|
133 | + # except that the shebang line is rewritten to use our sys.executable |
---|
134 | + # for the interpreter. |
---|
135 | f = open(bin_tahoe_template, "rU") |
---|
136 | script_lines = f.readlines() |
---|
137 | f.close() |
---|
138 | hunk ./setup.py 251 |
---|
139 | - script_lines[0] = "#!%s\n" % sys.executable |
---|
140 | - tahoe_script = os.path.join("bin", "tahoe-script.py") |
---|
141 | - f = open(tahoe_script, "w") |
---|
142 | - for line in script_lines: |
---|
143 | - f.write(line) |
---|
144 | - f.close() |
---|
145 | - if sys.platform == "win32": |
---|
146 | - from pkg_resources import require |
---|
147 | - setuptools_egg = require("setuptools")[0].location |
---|
148 | - if os.path.isfile(setuptools_egg): |
---|
149 | - z = zipfile.ZipFile(setuptools_egg, 'r') |
---|
150 | - for filename in z.namelist(): |
---|
151 | - if 'cli.exe' in filename: |
---|
152 | - cli_exe = z.read(filename) |
---|
153 | - else: |
---|
154 | - cli_exe = os.path.join(setuptools_egg, 'setuptools', 'cli.exe') |
---|
155 | - tahoe_exe = os.path.join("bin", "tahoe.exe") |
---|
156 | - if os.path.isfile(setuptools_egg): |
---|
157 | - f = open(tahoe_exe, 'wb') |
---|
158 | - f.write(cli_exe) |
---|
159 | - f.close() |
---|
160 | - else: |
---|
161 | - shutil.copy(cli_exe, tahoe_exe) |
---|
162 | - else: |
---|
163 | + script_lines[0] = '#!%s\n' % sys.executable |
---|
164 | + for script_name in script_names: |
---|
165 | + tahoe_script = os.path.join("bin", script_name) |
---|
166 | try: |
---|
167 | hunk ./setup.py 255 |
---|
168 | - os.remove(os.path.join('bin', 'tahoe')) |
---|
169 | - except: |
---|
170 | - # okay, probably it was already gone |
---|
171 | - pass |
---|
172 | - os.symlink('tahoe-script.py', os.path.join('bin', 'tahoe')) |
---|
173 | + os.remove(tahoe_script) |
---|
174 | + except Exception: |
---|
175 | + if os.path.exists(tahoe_script): |
---|
176 | + raise |
---|
177 | + f = open(tahoe_script, "wb") |
---|
178 | + for line in script_lines: |
---|
179 | + f.write(line) |
---|
180 | + f.close() |
---|
181 | + |
---|
182 | + # chmod +x |
---|
183 | + old_mode = stat.S_IMODE(os.stat(tahoe_script)[stat.ST_MODE]) |
---|
184 | + new_mode = old_mode | (stat.S_IXUSR | stat.S_IRUSR | |
---|
185 | + stat.S_IXGRP | stat.S_IRGRP | |
---|
186 | + stat.S_IXOTH | stat.S_IROTH ) |
---|
187 | + os.chmod(tahoe_script, new_mode) |
---|
188 | |
---|
189 | hunk ./setup.py 271 |
---|
190 | - # chmod +x bin/tahoe-script.py |
---|
191 | - old_mode = stat.S_IMODE(os.stat(tahoe_script)[stat.ST_MODE]) |
---|
192 | - new_mode = old_mode | (stat.S_IXUSR | stat.S_IRUSR | |
---|
193 | - stat.S_IXGRP | stat.S_IRGRP | |
---|
194 | - stat.S_IXOTH | stat.S_IROTH ) |
---|
195 | - os.chmod(tahoe_script, new_mode) |
---|
196 | + old_tahoe_exe = os.path.join("bin", "tahoe.exe") |
---|
197 | + try: |
---|
198 | + os.remove(old_tahoe_exe) |
---|
199 | + except Exception: |
---|
200 | + if os.path.exists(old_tahoe_exe): |
---|
201 | + raise |
---|
202 | |
---|
203 | class MySdist(sdist.sdist): |
---|
204 | """ A hook in the sdist command so that we can determine whether this the |
---|
205 | hunk ./src/allmydata/scripts/runner.py 12 |
---|
206 | pkg_resources.require('allmydata-tahoe') |
---|
207 | from allmydata.scripts.common import BaseOptions |
---|
208 | from allmydata.scripts import debug, create_node, startstop_node, cli, keygen, stats_gatherer |
---|
209 | +from allmydata.util.encodingutil import quote_output, get_argv_encoding |
---|
210 | |
---|
211 | def GROUP(s): |
---|
212 | # Usage.parseOptions compares argv[1] against command[0], so it will |
---|
213 | hunk ./src/allmydata/scripts/runner.py 22 |
---|
214 | |
---|
215 | |
---|
216 | class Options(BaseOptions, usage.Options): |
---|
217 | - synopsis = "Usage: tahoe <command> [command options]" |
---|
218 | + synopsis = "\nUsage: tahoe <command> [command options]" |
---|
219 | subCommands = ( GROUP("Administration") |
---|
220 | + create_node.subCommands |
---|
221 | + keygen.subCommands |
---|
222 | hunk ./src/allmydata/scripts/runner.py 45 |
---|
223 | |
---|
224 | def runner(argv, |
---|
225 | run_by_human=True, |
---|
226 | - stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr, |
---|
227 | + stdin=None, stdout=None, stderr=None, |
---|
228 | install_node_control=True, additional_commands=None): |
---|
229 | |
---|
230 | hunk ./src/allmydata/scripts/runner.py 48 |
---|
231 | + stdin = stdin or sys.stdin |
---|
232 | + stdout = stdout or sys.stdout |
---|
233 | + stderr = stderr or sys.stderr |
---|
234 | + |
---|
235 | config = Options() |
---|
236 | if install_node_control: |
---|
237 | config.subCommands.extend(startstop_node.subCommands) |
---|
238 | hunk ./src/allmydata/scripts/runner.py 70 |
---|
239 | c = config |
---|
240 | while hasattr(c, 'subOptions'): |
---|
241 | c = c.subOptions |
---|
242 | - print str(c) |
---|
243 | - print "%s: %s" % (sys.argv[0], e) |
---|
244 | + print >>stdout, str(c) |
---|
245 | + try: |
---|
246 | + msg = e.args[0].decode(get_argv_encoding()) |
---|
247 | + except Exception: |
---|
248 | + msg = repr(e) |
---|
249 | + print >>stdout, "%s: %s\n" % (sys.argv[0], quote_output(msg, quotemarks=False)) |
---|
250 | return 1 |
---|
251 | |
---|
252 | command = config.subCommand |
---|
253 | hunk ./src/allmydata/scripts/runner.py 110 |
---|
254 | |
---|
255 | return rc |
---|
256 | |
---|
257 | + |
---|
258 | def run(install_node_control=True): |
---|
259 | hunk ./src/allmydata/scripts/runner.py 112 |
---|
260 | - rc = runner(sys.argv[1:]) |
---|
261 | + if sys.platform == "win32": |
---|
262 | + from allmydata.windows.fixups import initialize |
---|
263 | + initialize() |
---|
264 | + |
---|
265 | + rc = runner(sys.argv[1:], install_node_control=install_node_control) |
---|
266 | sys.exit(rc) |
---|
267 | hunk ./src/allmydata/test/__init__.py 28 |
---|
268 | # we disable incident reporting for all unit tests. |
---|
269 | disable_foolscap_incidents() |
---|
270 | |
---|
271 | +import sys |
---|
272 | +if sys.platform == "win32": |
---|
273 | + from allmydata.windows.fixups import initialize |
---|
274 | + initialize() |
---|
275 | + |
---|
276 | hunk ./src/allmydata/test/test_encodingutil.py 28 |
---|
277 | print |
---|
278 | print "class MyWeirdOS(EncodingUtil, unittest.TestCase):" |
---|
279 | print " uname = '%s'" % ' '.join(platform.uname()) |
---|
280 | - if sys.platform != "win32": |
---|
281 | - print " argv = %s" % repr(sys.argv[1]) |
---|
282 | + print " argv = %s" % repr(sys.argv[1]) |
---|
283 | print " platform = '%s'" % sys.platform |
---|
284 | print " filesystem_encoding = '%s'" % sys.getfilesystemencoding() |
---|
285 | hunk ./src/allmydata/test/test_encodingutil.py 31 |
---|
286 | - print " output_encoding = '%s'" % sys.stdout.encoding |
---|
287 | - print " argv_encoding = '%s'" % (sys.platform == "win32" and 'ascii' or sys.stdout.encoding) |
---|
288 | + print " output_encoding = '%s'" % (sys.platform == "win32" and 'utf-8' or sys.stdout.encoding) |
---|
289 | + print " argv_encoding = '%s'" % (sys.platform == "win32" and 'utf-8' or sys.stdout.encoding) |
---|
290 | |
---|
291 | try: |
---|
292 | tmpdir = tempfile.mkdtemp() |
---|
293 | hunk ./src/allmydata/test/test_encodingutil.py 80 |
---|
294 | self.failUnlessReallyEqual(get_output_encoding(), 'utf-8') |
---|
295 | |
---|
296 | mock_stdout.encoding = 'koi8-r' |
---|
297 | + expected = sys.platform == "win32" and 'utf-8' or 'koi8-r' |
---|
298 | _reload() |
---|
299 | hunk ./src/allmydata/test/test_encodingutil.py 82 |
---|
300 | - self.failUnlessReallyEqual(get_output_encoding(), 'koi8-r') |
---|
301 | + self.failUnlessReallyEqual(get_output_encoding(), expected) |
---|
302 | |
---|
303 | mock_stdout.encoding = 'nonexistent_encoding' |
---|
304 | hunk ./src/allmydata/test/test_encodingutil.py 85 |
---|
305 | - self.failUnlessRaises(AssertionError, _reload) |
---|
306 | + if sys.platform == "win32": |
---|
307 | + _reload() |
---|
308 | + self.failUnlessReallyEqual(get_output_encoding(), 'utf-8') |
---|
309 | + else: |
---|
310 | + self.failUnlessRaises(AssertionError, _reload) |
---|
311 | |
---|
312 | @patch('locale.getpreferredencoding') |
---|
313 | def test_get_output_encoding_not_from_stdout(self, mock_locale_getpreferredencoding): |
---|
314 | hunk ./src/allmydata/test/test_encodingutil.py 101 |
---|
315 | old_stdout = sys.stdout |
---|
316 | sys.stdout = DummyStdout() |
---|
317 | try: |
---|
318 | + expected = sys.platform == "win32" and 'utf-8' or 'koi8-r' |
---|
319 | _reload() |
---|
320 | hunk ./src/allmydata/test/test_encodingutil.py 103 |
---|
321 | - self.failUnlessReallyEqual(get_output_encoding(), 'koi8-r') |
---|
322 | + self.failUnlessReallyEqual(get_output_encoding(), expected) |
---|
323 | |
---|
324 | sys.stdout.encoding = None |
---|
325 | _reload() |
---|
326 | hunk ./src/allmydata/test/test_encodingutil.py 107 |
---|
327 | - self.failUnlessReallyEqual(get_output_encoding(), 'koi8-r') |
---|
328 | + self.failUnlessReallyEqual(get_output_encoding(), expected) |
---|
329 | |
---|
330 | mock_locale_getpreferredencoding.return_value = None |
---|
331 | _reload() |
---|
332 | hunk ./src/allmydata/test/test_encodingutil.py 129 |
---|
333 | # Encoding koi8-r cannot represent e-grave |
---|
334 | mock.encoding = 'koi8-r' |
---|
335 | _reload() |
---|
336 | - self.failUnlessRaises(UnicodeEncodeError, unicode_to_output, lumiere_nfc) |
---|
337 | + if sys.platform == "win32": |
---|
338 | + self.failUnlessReallyEqual(unicode_to_output(lumiere_nfc), 'lumi\xc3\xa8re') |
---|
339 | + else: |
---|
340 | + self.failUnlessRaises(UnicodeEncodeError, unicode_to_output, lumiere_nfc) |
---|
341 | |
---|
342 | @patch('os.listdir') |
---|
343 | def test_no_unicode_normalization(self, mock): |
---|
344 | hunk ./src/allmydata/test/test_encodingutil.py 182 |
---|
345 | listdir_unicode, |
---|
346 | u'/' + lumiere_nfc) |
---|
347 | |
---|
348 | + |
---|
349 | class EncodingUtil(ReallyEqualMixin): |
---|
350 | def setUp(self): |
---|
351 | # Mock sys.platform because unicode_platform() uses it |
---|
352 | hunk ./src/allmydata/test/test_encodingutil.py 214 |
---|
353 | |
---|
354 | mock.encoding = self.output_encoding |
---|
355 | _reload() |
---|
356 | - self.failUnlessReallyEqual(unicode_to_output(lumiere_nfc), self.output) |
---|
357 | + self.failUnlessReallyEqual(unicode_to_output(lumiere_nfc), self.argv) |
---|
358 | |
---|
359 | def test_unicode_platform(self): |
---|
360 | matrix = { |
---|
361 | hunk ./src/allmydata/test/test_encodingutil.py 296 |
---|
362 | |
---|
363 | class UbuntuKarmicUTF8(EncodingUtil, unittest.TestCase): |
---|
364 | uname = 'Linux korn 2.6.31-14-generic #48-Ubuntu SMP Fri Oct 16 14:05:01 UTC 2009 x86_64' |
---|
365 | - output = 'lumi\xc3\xa8re' |
---|
366 | argv = 'lumi\xc3\xa8re' |
---|
367 | platform = 'linux2' |
---|
368 | filesystem_encoding = 'UTF-8' |
---|
369 | hunk ./src/allmydata/test/test_encodingutil.py 305 |
---|
370 | |
---|
371 | class UbuntuKarmicLatin1(EncodingUtil, unittest.TestCase): |
---|
372 | uname = 'Linux korn 2.6.31-14-generic #48-Ubuntu SMP Fri Oct 16 14:05:01 UTC 2009 x86_64' |
---|
373 | - output = 'lumi\xe8re' |
---|
374 | argv = 'lumi\xe8re' |
---|
375 | platform = 'linux2' |
---|
376 | filesystem_encoding = 'ISO-8859-1' |
---|
377 | hunk ./src/allmydata/test/test_encodingutil.py 312 |
---|
378 | argv_encoding = 'ISO-8859-1' |
---|
379 | dirlist = ['test_file', 'Blah blah.txt', '\xc4rtonwall.mp3'] |
---|
380 | |
---|
381 | -class WindowsXP(EncodingUtil, unittest.TestCase): |
---|
382 | +class Windows(EncodingUtil, unittest.TestCase): |
---|
383 | uname = 'Windows XP 5.1.2600 x86 x86 Family 15 Model 75 Step ping 2, AuthenticAMD' |
---|
384 | hunk ./src/allmydata/test/test_encodingutil.py 314 |
---|
385 | - output = 'lumi\x8are' |
---|
386 | - platform = 'win32' |
---|
387 | - filesystem_encoding = 'mbcs' |
---|
388 | - output_encoding = 'cp850' |
---|
389 | - argv_encoding = 'ascii' |
---|
390 | - dirlist = [u'Blah blah.txt', u'test_file', u'\xc4rtonwall.mp3'] |
---|
391 | - |
---|
392 | -class WindowsXP_UTF8(EncodingUtil, unittest.TestCase): |
---|
393 | - uname = 'Windows XP 5.1.2600 x86 x86 Family 15 Model 75 Step ping 2, AuthenticAMD' |
---|
394 | - output = 'lumi\xc3\xa8re' |
---|
395 | - platform = 'win32' |
---|
396 | - filesystem_encoding = 'mbcs' |
---|
397 | - output_encoding = 'cp65001' |
---|
398 | - argv_encoding = 'ascii' |
---|
399 | - dirlist = [u'Blah blah.txt', u'test_file', u'\xc4rtonwall.mp3'] |
---|
400 | - |
---|
401 | -class WindowsVista(EncodingUtil, unittest.TestCase): |
---|
402 | - uname = 'Windows Vista 6.0.6000 x86 x86 Family 6 Model 15 Stepping 11, GenuineIntel' |
---|
403 | - output = 'lumi\x8are' |
---|
404 | + argv = 'lumi\xc3\xa8re' |
---|
405 | platform = 'win32' |
---|
406 | filesystem_encoding = 'mbcs' |
---|
407 | hunk ./src/allmydata/test/test_encodingutil.py 317 |
---|
408 | - output_encoding = 'cp850' |
---|
409 | - argv_encoding = 'ascii' |
---|
410 | + output_encoding = 'utf-8' |
---|
411 | + argv_encoding = 'utf-8' |
---|
412 | dirlist = [u'Blah blah.txt', u'test_file', u'\xc4rtonwall.mp3'] |
---|
413 | |
---|
414 | class MacOSXLeopard(EncodingUtil, unittest.TestCase): |
---|
415 | hunk ./src/allmydata/test/test_encodingutil.py 323 |
---|
416 | uname = 'Darwin g5.local 9.8.0 Darwin Kernel Version 9.8.0: Wed Jul 15 16:57:01 PDT 2009; root:xnu-1228.15.4~1/RELEASE_PPC Power Macintosh powerpc' |
---|
417 | - output = 'lumi\xc3\xa8re' |
---|
418 | argv = 'lumi\xc3\xa8re' |
---|
419 | platform = 'darwin' |
---|
420 | filesystem_encoding = 'utf-8' |
---|
421 | hunk ./src/allmydata/test/test_runner.py 11 |
---|
422 | from cStringIO import StringIO |
---|
423 | from allmydata.util import fileutil, pollmixin |
---|
424 | from allmydata.scripts import runner |
---|
425 | +from allmydata.util.encodingutil import unicode_to_argv, unicode_to_output |
---|
426 | |
---|
427 | from allmydata.test import common_util |
---|
428 | import allmydata |
---|
429 | hunk ./src/allmydata/test/test_runner.py 17 |
---|
430 | |
---|
431 | bintahoe = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(allmydata.__file__))), 'bin', 'tahoe') |
---|
432 | -if sys.platform == "win32": # TODO: should this include cygwin? |
---|
433 | - bintahoe += ".exe" |
---|
434 | +if sys.platform == "win32": |
---|
435 | + bintahoe += ".pyscript" |
---|
436 | |
---|
437 | |
---|
438 | class SkipMixin: |
---|
439 | hunk ./src/allmydata/test/test_runner.py 52 |
---|
440 | d.addCallback(_cb) |
---|
441 | return d |
---|
442 | |
---|
443 | + def test_unicode_arguments_and_output(self): |
---|
444 | + self.skip_if_cannot_run_bintahoe() |
---|
445 | + |
---|
446 | + tricky = u"\u2621" |
---|
447 | + try: |
---|
448 | + tricky_arg = unicode_to_argv(tricky) |
---|
449 | + tricky_out = unicode_to_output(tricky) |
---|
450 | + except UnicodeEncodeError: |
---|
451 | + raise unittest.SkipTest("A non-ASCII argument/output could not be encoded on this platform.") |
---|
452 | + |
---|
453 | + d = utils.getProcessOutputAndValue(bintahoe, args=[tricky_arg], env=os.environ) |
---|
454 | + def _cb(res): |
---|
455 | + out, err, rc_or_sig = res |
---|
456 | + self.failUnlessEqual(rc_or_sig, 1, str((out, err, rc_or_sig))) |
---|
457 | + self.failUnlessIn("Unknown command: "+tricky_out, out) |
---|
458 | + d.addCallback(_cb) |
---|
459 | + return d |
---|
460 | + |
---|
461 | |
---|
462 | class CreateNode(unittest.TestCase): |
---|
463 | # exercise "tahoe create-node", create-introducer, |
---|
464 | hunk ./src/allmydata/util/encodingutil.py 15 |
---|
465 | from allmydata.util import log |
---|
466 | |
---|
467 | |
---|
468 | -def _canonical_encoding(encoding): |
---|
469 | +def canonical_encoding(encoding): |
---|
470 | if encoding is None: |
---|
471 | log.msg("Warning: falling back to UTF-8 encoding.", level=log.WEIRD) |
---|
472 | encoding = 'utf-8' |
---|
473 | hunk ./src/allmydata/util/encodingutil.py 25 |
---|
474 | elif encoding == "us-ascii" or encoding == "646" or encoding == "ansi_x3.4-1968": |
---|
475 | encoding = 'ascii' |
---|
476 | |
---|
477 | + return encoding |
---|
478 | + |
---|
479 | +def check_encoding(encoding): |
---|
480 | # sometimes Python returns an encoding name that it doesn't support for conversion |
---|
481 | # fail early if this happens |
---|
482 | try: |
---|
483 | hunk ./src/allmydata/util/encodingutil.py 35 |
---|
484 | except (LookupError, AttributeError): |
---|
485 | raise AssertionError("The character encoding '%s' is not supported for conversion." % (encoding,)) |
---|
486 | |
---|
487 | - return encoding |
---|
488 | - |
---|
489 | filesystem_encoding = None |
---|
490 | output_encoding = None |
---|
491 | argv_encoding = None |
---|
492 | hunk ./src/allmydata/util/encodingutil.py 43 |
---|
493 | def _reload(): |
---|
494 | global filesystem_encoding, output_encoding, argv_encoding, is_unicode_platform |
---|
495 | |
---|
496 | - filesystem_encoding = _canonical_encoding(sys.getfilesystemencoding()) |
---|
497 | - |
---|
498 | - outenc = None |
---|
499 | - if hasattr(sys.stdout, 'encoding'): |
---|
500 | - outenc = sys.stdout.encoding |
---|
501 | - if outenc is None: |
---|
502 | - try: |
---|
503 | - outenc = locale.getpreferredencoding() |
---|
504 | - except Exception: |
---|
505 | - pass # work around <http://bugs.python.org/issue1443504> |
---|
506 | - output_encoding = _canonical_encoding(outenc) |
---|
507 | + filesystem_encoding = canonical_encoding(sys.getfilesystemencoding()) |
---|
508 | + check_encoding(filesystem_encoding) |
---|
509 | |
---|
510 | if sys.platform == 'win32': |
---|
511 | hunk ./src/allmydata/util/encodingutil.py 47 |
---|
512 | - # Unicode arguments are not supported on Windows yet; see #565 and #1074. |
---|
513 | - argv_encoding = 'ascii' |
---|
514 | + # On Windows we install UTF-8 stream wrappers for sys.stdout and |
---|
515 | + # sys.stderr, and reencode the arguments as UTF-8 (see scripts/runner.py). |
---|
516 | + output_encoding = 'utf-8' |
---|
517 | else: |
---|
518 | hunk ./src/allmydata/util/encodingutil.py 51 |
---|
519 | - argv_encoding = output_encoding |
---|
520 | + outenc = None |
---|
521 | + if hasattr(sys.stdout, 'encoding'): |
---|
522 | + outenc = sys.stdout.encoding |
---|
523 | + if outenc is None: |
---|
524 | + try: |
---|
525 | + outenc = locale.getpreferredencoding() |
---|
526 | + except Exception: |
---|
527 | + pass # work around <http://bugs.python.org/issue1443504> |
---|
528 | + output_encoding = canonical_encoding(outenc) |
---|
529 | + |
---|
530 | + check_encoding(output_encoding) |
---|
531 | + argv_encoding = output_encoding |
---|
532 | + |
---|
533 | is_unicode_platform = sys.platform in ["win32", "darwin"] |
---|
534 | |
---|
535 | _reload() |
---|
536 | hunk ./src/allmydata/util/encodingutil.py 99 |
---|
537 | raise usage.UsageError("Argument %s cannot be decoded as %s." % |
---|
538 | (quote_output(s), argv_encoding)) |
---|
539 | |
---|
540 | +def unicode_to_argv(s): |
---|
541 | + """ |
---|
542 | + Encode the given Unicode argument as a bytestring. On Windows, this uses a |
---|
543 | + mangled encoding that will be reversed by code in runner.py. |
---|
544 | + """ |
---|
545 | + precondition(isinstance(s, unicode), s) |
---|
546 | + |
---|
547 | + if sys.platform == "win32": |
---|
548 | + # This must be the same as 'mangle' in bin/tahoe-script.template. |
---|
549 | + return str(re.sub(ur'[^\x20-\x7F]', lambda m: u'\x7F%x;' % (ord(m.group(0)),), s)) |
---|
550 | + else: |
---|
551 | + return s.decode(argv_encoding) |
---|
552 | + |
---|
553 | def unicode_to_url(s): |
---|
554 | """ |
---|
555 | Encode an unicode object used in an URL. |
---|
556 | addfile ./src/allmydata/windows/fixups.py |
---|
557 | hunk ./src/allmydata/windows/fixups.py 1 |
---|
558 | + |
---|
559 | +done = False |
---|
560 | + |
---|
561 | +def initialize(): |
---|
562 | + global done |
---|
563 | + import sys |
---|
564 | + if sys.platform != "win32" or done: |
---|
565 | + return True |
---|
566 | + done = True |
---|
567 | + |
---|
568 | + import codecs, re |
---|
569 | + from ctypes import WINFUNCTYPE, windll, CFUNCTYPE, cdll, POINTER, byref, \ |
---|
570 | + c_wchar_p, c_char_p, c_void_p, c_int, c_size_t |
---|
571 | + from allmydata.util import log |
---|
572 | + from allmydata.util.encodingutil import canonical_encoding |
---|
573 | + |
---|
574 | + # Work around <http://bugs.python.org/issue6058>. |
---|
575 | + codecs.register(lambda name: name == 'cp65001' and codecs.lookup('utf-8') or None) |
---|
576 | + |
---|
577 | + # Make Unicode console output work independently of the current code page. |
---|
578 | + # This also fixes <http://bugs.python.org/issue1602>. |
---|
579 | + try: |
---|
580 | + STDOUT_FILENO = 1 |
---|
581 | + STDERR_FILENO = 2 |
---|
582 | + real_stdout = hasattr(sys.stdout, 'fileno') and sys.stdout.fileno() == STDOUT_FILENO |
---|
583 | + real_stderr = hasattr(sys.stderr, 'fileno') and sys.stderr.fileno() == STDERR_FILENO |
---|
584 | + |
---|
585 | + def force_utf8(stream, name): |
---|
586 | + if hasattr(stream, 'encoding') and canonical_encoding(stream.encoding) != 'utf-8': |
---|
587 | + log.msg("%s (%r) had encoding %r, but we're going to write UTF-8 to it" % |
---|
588 | + (name, stream, stream.encoding), level=log.CURIOUS) |
---|
589 | + stream.encoding = 'utf-8' |
---|
590 | + |
---|
591 | + if not real_stdout: |
---|
592 | + force_utf8(sys.stdout, "sys.stdout") |
---|
593 | + |
---|
594 | + if not real_stderr: |
---|
595 | + force_utf8(sys.stderr, "sys.stderr") |
---|
596 | + |
---|
597 | + if real_stdout or real_stderr: |
---|
598 | + # FILE * _fdopen(int fd, const char *mode); |
---|
599 | + # #define _IOLBF 0x0040 |
---|
600 | + # int setvbuf(FILE *stream, char *buffer, int mode, size_t size); |
---|
601 | + # #define _O_U8TEXT 0x40000 |
---|
602 | + # int _setmode(int fd, int mode); |
---|
603 | + # int fputws(const wchar_t *ws, FILE *stream); |
---|
604 | + # int fflush(FILE *stream); |
---|
605 | + |
---|
606 | + c_runtime = cdll.msvcrt |
---|
607 | + NULL = None |
---|
608 | + _fdopen = CFUNCTYPE(c_void_p, c_int, c_char_p)(("_fdopen", c_runtime)) |
---|
609 | + _IOLBF = 0x0040 |
---|
610 | + setvbuf = CFUNCTYPE(c_int, c_void_p, c_char_p, c_int, c_size_t)(("setvbuf", c_runtime)) |
---|
611 | + _O_U8TEXT = 0x40000 |
---|
612 | + _setmode = CFUNCTYPE(c_int, c_int, c_int)(("_setmode", c_runtime)) |
---|
613 | + fputws = CFUNCTYPE(c_int, c_wchar_p, c_void_p)(("fputws", c_runtime)); |
---|
614 | + fflush = CFUNCTYPE(c_int, c_void_p)(("fflush", c_runtime)); |
---|
615 | + |
---|
616 | + buffer_chars = 1024 |
---|
617 | + |
---|
618 | + class UnicodeOutput: |
---|
619 | + def __init__(self, fileno, name): |
---|
620 | + self._stream = _fdopen(fileno, "w") |
---|
621 | + assert self._stream is not NULL |
---|
622 | + |
---|
623 | + # Deep magic. MSVCRT supports writing wide-oriented output to stdout/stderr |
---|
624 | + # to the console using the Unicode APIs, but it does the conversion in the |
---|
625 | + # stdio buffer, so you need that buffer to be as large as the maximum amount |
---|
626 | + # you're going to write in a single call (in bytes, not characters). |
---|
627 | + setvbuf(self._stream, NULL, _IOLBF, buffer_chars*4 + 100) |
---|
628 | + _setmode(fileno, _O_U8TEXT) |
---|
629 | + |
---|
630 | + self._fileno = fileno |
---|
631 | + self.closed = False |
---|
632 | + self.softspace = False |
---|
633 | + self.mode = 'w' |
---|
634 | + self.encoding = 'utf-8' |
---|
635 | + self.name = name |
---|
636 | + |
---|
637 | + def isatty(self): |
---|
638 | + return False |
---|
639 | + def close(self): |
---|
640 | + self.closed = True |
---|
641 | + self.flush() |
---|
642 | + def fileno(self): |
---|
643 | + return self._fileno |
---|
644 | + def flush(self): |
---|
645 | + fflush(self._stream) |
---|
646 | + |
---|
647 | + def write(self, text): |
---|
648 | + if not isinstance(text, unicode): |
---|
649 | + text = str(text).decode('utf-8') |
---|
650 | + for i in xrange(0, len(text), buffer_chars): |
---|
651 | + fputws(text[i:(i+buffer_chars)], self._stream) |
---|
652 | + fflush(self._stream) |
---|
653 | + |
---|
654 | + def writelines(self, lines): |
---|
655 | + for line in lines: |
---|
656 | + self.write(line) |
---|
657 | + |
---|
658 | + if real_stdout: |
---|
659 | + sys.stdout = UnicodeOutput(STDOUT_FILENO, '<Unicode stdout>') |
---|
660 | + |
---|
661 | + if real_stderr: |
---|
662 | + sys.stderr = UnicodeOutput(STDERR_FILENO, '<Unicode stderr>') |
---|
663 | + except Exception, e: |
---|
664 | + log.msg("exception %r while fixing up sys.stdout and sys.stderr" % (e,), level=log.WEIRD) |
---|
665 | + |
---|
666 | + # Unmangle command-line arguments. |
---|
667 | + GetCommandLineW = WINFUNCTYPE(c_wchar_p)(("GetCommandLineW", windll.kernel32)) |
---|
668 | + CommandLineToArgvW = WINFUNCTYPE(POINTER(c_wchar_p), c_wchar_p, POINTER(c_int)) \ |
---|
669 | + (("CommandLineToArgvW", windll.shell32)) |
---|
670 | + |
---|
671 | + argc = c_int(0) |
---|
672 | + argv_unicode = CommandLineToArgvW(GetCommandLineW(), byref(argc)) |
---|
673 | + |
---|
674 | + def unmangle(s): |
---|
675 | + return re.sub(ur'\x7f[0-9a-fA-F]*\;', lambda m: unichr(int(m.group(0)[1:-1], 16)), s) |
---|
676 | + |
---|
677 | + try: |
---|
678 | + sys.argv = [unmangle(argv_unicode[i]).encode('utf-8') for i in xrange(1, argc.value)] |
---|
679 | + except Exception, e: |
---|
680 | + print >>sys.stderr, "%s: could not unmangle Unicode arguments" % (sys.argv[0],) |
---|
681 | + print >>sys.stderr, [argv_unicode[i] for i in xrange(1, argc.value)] |
---|
682 | + raise |
---|
683 | } |
---|
684 | |
---|
685 | Context: |
---|
686 | |
---|
687 | [Rename stringutils to encodingutil, and drop open_unicode (since the Python 'open' function works fine with Unicode paths). |
---|
688 | david-sarah@jacaranda.org**20100713042715 |
---|
689 | Ignore-this: fa2bb6b5d48ce9ba7ea2b1afc9f3b7b4 |
---|
690 | ] |
---|
691 | [Resolve conflicts between NFC normalization changes, and post-1.7 branch. |
---|
692 | david-sarah@jacaranda.org**20100618021642 |
---|
693 | Ignore-this: ed3d0d71b761b1317cff9a4c92c2f5cb |
---|
694 | ] |
---|
695 | [trivial: a copy of tiny code-cleanups suggested by Kevan in reviewing #967 |
---|
696 | zooko@zooko.com**20100617045339 |
---|
697 | Ignore-this: 274b3fdbf1531aa053f484a5d47d7447 |
---|
698 | ] |
---|
699 | [minor code clean-up in dirnode.py |
---|
700 | zooko@zooko.com**20100221052527 |
---|
701 | Ignore-this: b01bfb47638f5a64256bb306e0022066 |
---|
702 | Impose micro-POLA by passing only the writekey instead of the whole node object to {{{_encrypt_rw_uri()}}}. Remove DummyImmutableFileNode in nodemaker.py, which is obviated by this. Add micro-optimization by precomputing the netstring of the empty string and branching on whether the writekey is present or not outside of {{{_encrypt_rw_uri()}}}. Add doc about writekey to docstring. |
---|
703 | ] |
---|
704 | [Move EncryptedTemporaryFile from SFTP frontend to allmydata.util.fileutil, and make the FTP frontend also use it (fixing #1083). |
---|
705 | david-sarah@jacaranda.org**20100711213721 |
---|
706 | Ignore-this: e452e8ca66391aa2a1a49afe0114f317 |
---|
707 | ] |
---|
708 | [Add tests of caps from the future that have non-ASCII characters in them (encoded as UTF-8). The changes to test_uri.py, test_client.py, and test_dirnode.py add tests of non-ASCII future caps in addition to the current tests. The changes to test_web.py just replace the tests of all-ASCII future caps with tests of non-ASCII future caps. We also change uses of failUnlessEqual to failUnlessReallyEqual, in order to catch cases where the type of a string is not as expected. |
---|
709 | david-sarah@jacaranda.org**20100711200252 |
---|
710 | Ignore-this: c2f193352369d32e06865f8f3e951894 |
---|
711 | ] |
---|
712 | [Allow URIs passed in the initial JSON for t=mkdir-with-children, t=mkdir-immutable to be Unicode. Also pass the name of each child into nodemaker.create_from_cap for error reporting. |
---|
713 | david-sarah@jacaranda.org**20100711195525 |
---|
714 | Ignore-this: deac32d8b91ba26ede18905d3f7d2b93 |
---|
715 | ] |
---|
716 | [docs/logging.txt: note that setting flogging vars might affect tests with race conditions. |
---|
717 | david-sarah@jacaranda.org**20100712050721 |
---|
718 | Ignore-this: fc1609d215fcd5561a57fd1226206f27 |
---|
719 | ] |
---|
720 | [test_storage.py: potential fix for failures when logging is enabled. |
---|
721 | david-sarah@jacaranda.org**19700713040546 |
---|
722 | Ignore-this: 5815693a0df3e64c52c3c6b7be2846c7 |
---|
723 | ] |
---|
724 | [SFTP: address some of the comments in zooko's review (#1106). |
---|
725 | david-sarah@jacaranda.org**20100712025537 |
---|
726 | Ignore-this: c3921638a2d4f1de2a776ae78e4dc37e |
---|
727 | ] |
---|
728 | [upcase_since_on_welcome |
---|
729 | terrellrussell@gmail.com**20100708193903] |
---|
730 | [server_version_on_welcome_page.dpatch.txt |
---|
731 | freestorm77@gmail.com**20100605191721 |
---|
732 | Ignore-this: b450c76dc875f5ac8cca229a666cbd0a |
---|
733 | |
---|
734 | |
---|
735 | - The storage server version is 0 for all storage nodes in the Welcome Page |
---|
736 | |
---|
737 | |
---|
738 | ] |
---|
739 | [NEWS: add NEWS snippets about two recent patches |
---|
740 | zooko@zooko.com**20100708162058 |
---|
741 | Ignore-this: 6c9da6a0ad7351a960bdd60f81532899 |
---|
742 | ] |
---|
743 | [directory_html_top_banner.dpatch |
---|
744 | freestorm77@gmail.com**20100622205301 |
---|
745 | Ignore-this: 1d770d975e0c414c996564774f049bca |
---|
746 | |
---|
747 | The div tag with the link "Return to Welcome page" on the directory.xhtml page is not correct |
---|
748 | |
---|
749 | ] |
---|
750 | [tahoe_css_toolbar.dpatch |
---|
751 | freestorm77@gmail.com**20100622210046 |
---|
752 | Ignore-this: 5b3ebb2e0f52bbba718a932f80c246c0 |
---|
753 | |
---|
754 | CSS modification to be correctly diplayed with Internet Explorer 8 |
---|
755 | |
---|
756 | The links on the top of page directory.xhtml are not diplayed in the same line as display with Firefox. |
---|
757 | |
---|
758 | ] |
---|
759 | [runnin_test_tahoe_css.dpatch |
---|
760 | freestorm77@gmail.com**20100622214714 |
---|
761 | Ignore-this: e0db73d68740aad09a7b9ae60a08c05c |
---|
762 | |
---|
763 | Runnin test for changes in tahoe.css file |
---|
764 | |
---|
765 | ] |
---|
766 | [runnin_test_directory_xhtml.dpatch |
---|
767 | freestorm77@gmail.com**20100622201403 |
---|
768 | Ignore-this: f8962463fce50b9466405cb59fe11d43 |
---|
769 | |
---|
770 | Runnin test for diretory.xhtml top banner |
---|
771 | |
---|
772 | ] |
---|
773 | [stringutils.py: tolerate sys.stdout having no 'encoding' attribute. |
---|
774 | david-sarah@jacaranda.org**20100626040817 |
---|
775 | Ignore-this: f42cad81cef645ee38ac1df4660cc850 |
---|
776 | ] |
---|
777 | [quickstart.html: python 2.5 -> 2.6 as recommended version |
---|
778 | david-sarah@jacaranda.org**20100705175858 |
---|
779 | Ignore-this: bc3a14645ea1d5435002966ae903199f |
---|
780 | ] |
---|
781 | [SFTP: don't call .stopProducing on the producer registered with OverwriteableFileConsumer (which breaks with warner's new downloader). |
---|
782 | david-sarah@jacaranda.org**20100628231926 |
---|
783 | Ignore-this: 131b7a5787bc85a9a356b5740d9d996f |
---|
784 | ] |
---|
785 | [docs/how_to_make_a_tahoe-lafs_release.txt: trivial correction, install.html should now be quickstart.html. |
---|
786 | david-sarah@jacaranda.org**20100625223929 |
---|
787 | Ignore-this: 99a5459cac51bd867cc11ad06927ff30 |
---|
788 | ] |
---|
789 | [setup: in the Makefile, refuse to upload tarballs unless someone has passed the environment variable "BB_BRANCH" with value "trunk" |
---|
790 | zooko@zooko.com**20100619034928 |
---|
791 | Ignore-this: 276ddf9b6ad7ec79e27474862e0f7d6 |
---|
792 | ] |
---|
793 | [trivial: tiny update to in-line comment |
---|
794 | zooko@zooko.com**20100614045715 |
---|
795 | Ignore-this: 10851b0ed2abfed542c97749e5d280bc |
---|
796 | (I'm actually committing this patch as a test of the new eager-annotation-computation of trac-darcs.) |
---|
797 | ] |
---|
798 | [docs: about.html link to home page early on, and be decentralized storage instead of cloud storage this time around |
---|
799 | zooko@zooko.com**20100619065318 |
---|
800 | Ignore-this: dc6db03f696e5b6d2848699e754d8053 |
---|
801 | ] |
---|
802 | [docs: update about.html, especially to have a non-broken link to quickstart.html, and also to comment out the broken links to "for Paranoids" and "for Corporates" |
---|
803 | zooko@zooko.com**20100619065124 |
---|
804 | Ignore-this: e292c7f51c337a84ebfeb366fbd24d6c |
---|
805 | ] |
---|
806 | [TAG allmydata-tahoe-1.7.0 |
---|
807 | zooko@zooko.com**20100619052631 |
---|
808 | Ignore-this: d21e27afe6d85e2e3ba6a3292ba2be1 |
---|
809 | ] |
---|
810 | Patch bundle hash: |
---|
811 | bc0e02a3adc819e598af6dda619dcf5a10d23985 |
---|