Ticket #1074: unicode-args-and-rm-tahoe-exe.4.dpatch

File unicode-args-and-rm-tahoe-exe.4.dpatch, 32.8 KB (added by davidsarah, at 2010-07-15T05:48:02Z)

Changes to Tahoe needed to work with new zetuptoolz (that does not use .exe wrappers on Windows), and to support Uni code arguments and stdout/stderr -- v4.

Line 
1Thu 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
4New 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.
7david-sarah@jacaranda.org**20100715053115
8 Ignore-this: 8bcab59c644bda66493c9e51e91ccc29
9] {
10hunk ./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"
22hunk ./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.
35hunk ./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":
43hunk ./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:
71hunk ./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
77hunk ./bin/tahoe-script.template 80
78+
79         print '''\
80 I just tried to run and could not find my brother, named
81hunk ./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
93hunk ./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.
98hunk ./bin/tahoe-script.template 93
99-'''
100+''' % (local_tahoe,)
101     raise
102 else:
103     sys.exit(res)
104hunk ./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>
113hunk ./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()
138hunk ./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:
167hunk ./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 
189hunk ./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
205hunk ./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
213hunk ./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
222hunk ./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 
230hunk ./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)
238hunk ./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
253hunk ./src/allmydata/scripts/runner.py 110
254 
255     return rc
256 
257+
258 def run(install_node_control=True):
259hunk ./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)
267hunk ./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+
276hunk ./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()
285hunk ./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()
293hunk ./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()
299hunk ./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'
304hunk ./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):
314hunk ./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()
320hunk ./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()
326hunk ./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()
332hunk ./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):
344hunk ./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
352hunk ./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 = {
361hunk ./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'
369hunk ./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'
377hunk ./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'
384hunk ./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'
407hunk ./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):
415hunk ./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'
421hunk ./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
429hunk ./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:
439hunk ./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,
464hunk ./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'
473hunk ./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:
483hunk ./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
492hunk ./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':
511hunk ./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:
518hunk ./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()
536hunk ./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.
556addfile ./src/allmydata/windows/fixups.py
557hunk ./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
685Context:
686
687[Rename stringutils to encodingutil, and drop open_unicode (since the Python 'open' function works fine with Unicode paths).
688david-sarah@jacaranda.org**20100713042715
689 Ignore-this: fa2bb6b5d48ce9ba7ea2b1afc9f3b7b4
690] 
691[Resolve conflicts between NFC normalization changes, and post-1.7 branch.
692david-sarah@jacaranda.org**20100618021642
693 Ignore-this: ed3d0d71b761b1317cff9a4c92c2f5cb
694] 
695[trivial: a copy of tiny code-cleanups suggested by Kevan in reviewing #967
696zooko@zooko.com**20100617045339
697 Ignore-this: 274b3fdbf1531aa053f484a5d47d7447
698] 
699[minor code clean-up in dirnode.py
700zooko@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).
705david-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.
709david-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.
713david-sarah@jacaranda.org**20100711195525
714 Ignore-this: deac32d8b91ba26ede18905d3f7d2b93
715] 
716[docs/logging.txt: note that setting flogging vars might affect tests with race conditions.
717david-sarah@jacaranda.org**20100712050721
718 Ignore-this: fc1609d215fcd5561a57fd1226206f27
719] 
720[test_storage.py: potential fix for failures when logging is enabled.
721david-sarah@jacaranda.org**19700713040546
722 Ignore-this: 5815693a0df3e64c52c3c6b7be2846c7
723] 
724[SFTP: address some of the comments in zooko's review (#1106).
725david-sarah@jacaranda.org**20100712025537
726 Ignore-this: c3921638a2d4f1de2a776ae78e4dc37e
727] 
728[upcase_since_on_welcome
729terrellrussell@gmail.com**20100708193903] 
730[server_version_on_welcome_page.dpatch.txt
731freestorm77@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
740zooko@zooko.com**20100708162058
741 Ignore-this: 6c9da6a0ad7351a960bdd60f81532899
742] 
743[directory_html_top_banner.dpatch
744freestorm77@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
751freestorm77@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
760freestorm77@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
767freestorm77@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.
774david-sarah@jacaranda.org**20100626040817
775 Ignore-this: f42cad81cef645ee38ac1df4660cc850
776] 
777[quickstart.html: python 2.5 -> 2.6 as recommended version
778david-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).
782david-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.
786david-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"
790zooko@zooko.com**20100619034928
791 Ignore-this: 276ddf9b6ad7ec79e27474862e0f7d6
792] 
793[trivial: tiny update to in-line comment
794zooko@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
799zooko@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"
803zooko@zooko.com**20100619065124
804 Ignore-this: e292c7f51c337a84ebfeb366fbd24d6c
805] 
806[TAG allmydata-tahoe-1.7.0
807zooko@zooko.com**20100619052631
808 Ignore-this: d21e27afe6d85e2e3ba6a3292ba2be1
809] 
810Patch bundle hash:
811bc0e02a3adc819e598af6dda619dcf5a10d23985