Merge lp:~brian.curtin/ubuntuone-windows-installer/patching into lp:ubuntuone-windows-installer

Proposed by Brian Curtin
Status: Merged
Approved by: Brian Curtin
Approved revision: 174
Merged at revision: 171
Proposed branch: lp:~brian.curtin/ubuntuone-windows-installer/patching
Merge into: lp:ubuntuone-windows-installer
Prerequisite: lp:~brian.curtin/ubuntuone-windows-installer/no-u1to-deps
Diff against target: 756 lines (+698/-7)
4 files modified
scripts/devsetup/buildout.cfg (+33/-7)
scripts/devsetup/patches/keyring/pykeyring-delete-password.diff (+517/-0)
scripts/devsetup/patches/twisted/5726.diff (+91/-0)
scripts/devsetup/patches/twisted/6462.diff (+57/-0)
To merge this branch: bzr merge lp:~brian.curtin/ubuntuone-windows-installer/patching
Reviewer Review Type Date Requested Status
Mike McCracken (community) Approve
dobey (community) Approve
Review via email: mp+162647@code.launchpad.net

Commit message

- Use collective.recipe.patch to more consistently setup buildout environments with dependencies we need to patch.

Description of the change

Using the collective.recipe.patch buildout recipe, we can take the unmodified versions of our dependencies and patch them when the buildout environment is setup. This change applies two patches to Twisted and one to keyring until versions are released which include these changes.

Note that the default operation of using the included patch.py library does not work for these diffs for whatever reason. It was easier to just set the patch-binary manually to be the "patch" application found on the path. On Windows, this requires that you have some sort of patch library installed, probably via gnuwin32 utils.

I have an outstanding pull request to collective.recipe.patch due to how it uses the close_fds argument to subprocess.Popen, which is incompatible on Windows. If the pull request is not accepted relatively soon, I will probably have to upload a fork to PyPI.

To post a comment you must log in.
Revision history for this message
Mike McCracken (mikemc) wrote :

when testing this, I get this:

Download error on http://keyring-python.org/: [Errno 8] nodename nor servname provided, or not known -- Some packages may not be found!
Download error on http://home.python-keyring.org/: [Errno 8] nodename nor servname provided, or not known -- Some packages may not be found!
Download error on http://home.python-keyring.org/: [Errno 8] nodename nor servname provided, or not known -- Some packages may not be found!
Download error on http://home.python-keyring.org/: [Errno 8] nodename nor servname provided, or not known -- Some packages may not be found!
Getting distribution for 'keyring==0.7'.

not sure where that URL is coming from, but it definitely doesn't exist.
do I have an out of date setuptools or distribute or something?

review: Needs Information
Revision history for this message
dobey (dobey) :
review: Approve
Revision history for this message
Mike McCracken (mikemc) wrote :

Sorry for the delay, this worked for me yesterday

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'scripts/devsetup/buildout.cfg'
--- scripts/devsetup/buildout.cfg 2013-05-06 19:02:26 +0000
+++ scripts/devsetup/buildout.cfg 2013-05-06 19:02:26 +0000
@@ -26,8 +26,6 @@
26find-links =26find-links =
27 https://github.com/ghtdak/qtreactor/tarball/master#egg=qt4reactor27 https://github.com/ghtdak/qtreactor/tarball/master#egg=qt4reactor
28 http://launchpad.net/python-distutils-extra/trunk/2.31/+download/python-distutils-extra-2.31.tar.gz#egg=python-distutils-extra28 http://launchpad.net/python-distutils-extra/trunk/2.31/+download/python-distutils-extra-2.31.tar.gz#egg=python-distutils-extra
29# Mandel's patched keyring, already built
30 http://www.briancurtin.com/canonical/deps/keyring-0.7-py2.7.egg#egg=keyring
31# Pycrypto already built using python -c "import setuptools; execfile('setup.py')" bdist_egg29# Pycrypto already built using python -c "import setuptools; execfile('setup.py')" bdist_egg
32 http://www.briancurtin.com/canonical/deps/pycrypto-2.6-py2.7-win32.egg#egg=pycrypto30 http://www.briancurtin.com/canonical/deps/pycrypto-2.6-py2.7-win32.egg#egg=pycrypto
33# Py2exe already build using python -c "import setuptools; execfile('setup.py')" bdist_egg31# Py2exe already build using python -c "import setuptools; execfile('setup.py')" bdist_egg
@@ -38,9 +36,6 @@
38# http://sourceforge.net/tracker/index.php?func=detail&aid=3036368&group_id=115265&atid=69294036# http://sourceforge.net/tracker/index.php?func=detail&aid=3036368&group_id=115265&atid=692940
39 http://www.briancurtin.com/canonical/deps/comtypes-0.6.2.zip#egg=comtypes37 http://www.briancurtin.com/canonical/deps/comtypes-0.6.2.zip#egg=comtypes
40 http://launchpad.net/configglue/trunk/1.0/+download/configglue-1.0.tar.gz#egg=configglue38 http://launchpad.net/configglue/trunk/1.0/+download/configglue-1.0.tar.gz#egg=configglue
41# This Twisted build includes alecu's patch which has not yet been accepted
42# http://www.twistedmatrix.com/trac/ticket/5726
43 http://www.briancurtin.com/canonical/deps/Twisted-12.3.0-py2.7-win32.egg#egg=twisted
44 http://people.canonical.com/~dobey/pyflakes-0.5.0-2ubuntu2.bo2.tar.gz#egg=pyflakes39 http://people.canonical.com/~dobey/pyflakes-0.5.0-2ubuntu2.bo2.tar.gz#egg=pyflakes
45 http://u1.to/~mikemc/g/pyobjc_core-2.5.0b1-py2.7-macosx-10.6-x86_64.egg#egg=pyobjc-core-2.5.0b140 http://u1.to/~mikemc/g/pyobjc_core-2.5.0b1-py2.7-macosx-10.6-x86_64.egg#egg=pyobjc-core-2.5.0b1
46 http://u1.to/~mikemc/7/pyobjc_framework_Cocoa-2.5.0b1-py2.7-macosx-10.6-x86_64.egg#egg=pyobjc-framework-Cocoa-2.5.0b141 http://u1.to/~mikemc/7/pyobjc_framework_Cocoa-2.5.0b1-py2.7-macosx-10.6-x86_64.egg#egg=pyobjc-framework-Cocoa-2.5.0b1
@@ -78,10 +73,10 @@
78recipe = z3c.recipe.scripts73recipe = z3c.recipe.scripts
79eggs =74eggs =
80 ${scripty:eggs}75 ${scripty:eggs}
81 twisted76 ${patched-twisted:egg}
77 ${patched-keyring:egg}
82 qt4reactor78 qt4reactor
83 python-distutils-extra79 python-distutils-extra
84 keyring
85 pycrypto80 pycrypto
86 pyOpenSSL81 pyOpenSSL
87 pil82 pil
@@ -110,6 +105,37 @@
110 ${buildout:parts-directory}/ubuntuone-dev-tools105 ${buildout:parts-directory}/ubuntuone-dev-tools
111 ${buildout:parts-directory}/dirspec106 ${buildout:parts-directory}/dirspec
112107
108[twisted]
109recipe = z3c.recipe.scripts
110eggs =
111 twisted
112versions = versions
113interpreter = python
114
115[keyring]
116recipe = z3c.recipe.scripts
117eggs =
118 keyring
119versions = versions
120interpreter = python
121
122[patched-twisted]
123recipe = collective.recipe.patch
124patch-binary = patch
125egg = ${twisted:eggs}
126# Patches for http://twistedmatrix.com/trac/ticket/6462
127# and http://twistedmatrix.com/trac/ticket/5726
128patches = patches/twisted/6462.diff
129 patches/twisted/5726.diff
130
131[patched-keyring]
132recipe = collective.recipe.patch
133patch-binary = patch
134egg = ${keyring:eggs}
135# Patch generated between 0.7 and mandel's branch
136# https://bitbucket.org/mandel/pykeyring-delete-password
137patches = patches/keyring/pykeyring-delete-password.diff
138
113# Install various scripts. These are all separate sections because the139# Install various scripts. These are all separate sections because the
114# initialization and arguments values apply to every entry point you140# initialization and arguments values apply to every entry point you
115# declare in a section, and these four are incompatible. Only pyflakes141# declare in a section, and these four are incompatible. Only pyflakes
116142
=== added directory 'scripts/devsetup/patches'
=== added directory 'scripts/devsetup/patches/keyring'
=== added file 'scripts/devsetup/patches/keyring/pykeyring-delete-password.diff'
--- scripts/devsetup/patches/keyring/pykeyring-delete-password.diff 1970-01-01 00:00:00 +0000
+++ scripts/devsetup/patches/keyring/pykeyring-delete-password.diff 2013-05-06 19:02:26 +0000
@@ -0,0 +1,517 @@
1--- keyring/__init__.py 2013-04-16 10:04:36.000000000 -0500
2+++ keyring/__init__.py 2013-04-12 09:26:11.848796300 -0500
3@@ -6,5 +6,6 @@
4 import logging
5 logger = logging.getLogger('keyring')
6
7-from keyring.core import set_keyring, get_keyring, set_password, get_password
8+from core import (set_keyring, get_keyring, set_password, get_password,
9+ delete_password)
10 from keyring.getpassbackend import get_password as get_pass_get_password
11--- keyring/backend.py 2013-04-16 10:04:36.000000000 -0500
12+++ keyring/backend.py 2013-04-12 09:26:11.877792300 -0500
13@@ -8,7 +8,6 @@
14 import os
15 import sys
16 import ConfigParser
17-import base64
18
19 from keyring.util.escape import escape as escape_for_ini
20 from keyring.util import properties
21@@ -36,10 +35,17 @@
22 _BLOCK_SIZE = 32
23 _PADDING = '0'
24
25+
26 class PasswordSetError(Exception):
27 """Raised when the password can't be set.
28 """
29
30+
31+class PasswordDeleteError(Exception):
32+ """Raised when the password can't be deleted.
33+ """
34+
35+
36 class KeyringBackend(object):
37 """The abstract base class of the keyring, every backend must implement
38 this interface.
39@@ -67,6 +73,13 @@
40 """
41 raise PasswordSetError("reason")
42
43+ @abstractmethod
44+ def delete_password(self, service, username):
45+ """Delete the password for the username of the service.
46+ """
47+ raise PasswordDeleteError("reason")
48+
49+
50 class _ExtensionKeyring(KeyringBackend):
51 """_ExtensionKeyring is a adaptor class for the platform related keyring
52 backends.
53@@ -112,7 +125,16 @@
54 try:
55 self.keyring_impl.password_set(service, username, password)
56 except OSError, e:
57- raise PasswordSetError(e.message)
58+ raise PasswordSetError(e)
59+
60+ def delete_password(self, service, username):
61+ """Override the delete_password in KeyringBackend.
62+ """
63+ try:
64+ self.keyring_impl.password_delete(service, username)
65+ except OSError, e:
66+ raise PasswordDeleteError(e)
67+
68
69 class OSXKeychain(_ExtensionKeyring):
70 """Mac OS X Keychain"""
71@@ -127,6 +149,7 @@
72 """
73 return sys.platform == 'darwin'
74
75+
76 class GnomeKeyring(KeyringBackend):
77 """Gnome Keyring"""
78
79@@ -174,8 +197,22 @@
80 # The user pressed "Cancel" when prompted to unlock their keyring.
81 raise PasswordSetError("cancelled by user")
82
83+ def delete_password(self, service, username):
84+ """Delete the password for the username of the service.
85+ """
86+ try:
87+ items = gnomekeyring.find_network_password_sync(username, service)
88+ for current in items:
89+ gnomekeyring.item_delete_sync(current['keyring'],
90+ current['item_id'])
91+ except gnomekeyring.NoMatchError:
92+ raise PasswordDeleteError("can't found the password")
93+ except gnomekeyring.CancelledError:
94+ raise PasswordDeleteError("cancelled by user")
95+
96 kwallet = None
97
98+
99 def open_kwallet(kwallet_module=None, qt_module=None):
100
101 # If we specified the kwallet_module and/or qt_module, surely we won't need
102@@ -224,7 +261,7 @@
103 """KDE KWallet"""
104
105 def supported(self):
106- if kwallet_support and os.environ.has_key('KDE_SESSION_UID'):
107+ if kwallet_support and 'KDE_SESSION_UID' in os.environ:
108 return 1
109 elif kwallet_support:
110 return 0
111@@ -249,7 +286,16 @@
112 """Set password for the username of the service
113 """
114 wallet = open_kwallet()
115- wallet.writePassword(username+'@'+service, password)
116+ wallet.writePassword(username + '@' + service, password)
117+
118+ def delete_password(self, service, username):
119+ """Delete the password for the username of the service.
120+ """
121+ key = username + '@' + service
122+ if kwallet.keyDoesNotExist(kwallet.walletName(), 'Python', key):
123+ raise PasswordDeleteError("can't found the password")
124+ kwallet.removeEntry(key)
125+
126
127 class BasicFileKeyring(KeyringBackend):
128 """BasicFileKeyring is a file-based implementation of keyring.
129@@ -297,11 +343,11 @@
130
131 # fetch the password
132 try:
133- password_base64 = config.get(service, username).encode()
134+ password_base64 = config.get(service, username)
135 # decode with base64
136- password_encrypted = base64.decodestring(password_base64)
137+ password_encrypted = password_base64.decode("base64")
138 # decrypted the password
139- password = self.decrypt(password_encrypted).decode('utf-8')
140+ password = self.decrypt(password_encrypted)
141 except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
142 password = None
143 return password
144@@ -313,21 +359,35 @@
145 username = escape_for_ini(username)
146
147 # encrypt the password
148- password_encrypted = self.encrypt(password.encode('utf-8'))
149+ password_encrypted = self.encrypt(password)
150 # load the password from the disk
151 config = ConfigParser.RawConfigParser()
152 if os.path.exists(self.file_path):
153 config.read(self.file_path)
154
155 # encode with base64
156- password_base64 = base64.encodestring(password_encrypted).decode()
157+ password_base64 = password_encrypted.encode("base64")
158 # write the modification
159 if not config.has_section(service):
160 config.add_section(service)
161 config.set(service, username, password_base64)
162- config_file = open(self.file_path,'w')
163+ config_file = open(self.file_path, 'w')
164 config.write(config_file)
165
166+ def delete_password(self, service, username):
167+ """Delete the password for the username of the service.
168+ """
169+ service = escape_for_ini(service)
170+ config = ConfigParser.RawConfigParser()
171+ if os.path.exists(self.file_path):
172+ config.read(self.file_path)
173+ if not config.remove_section(service):
174+ raise PasswordDeleteError("can't found the password")
175+ # update the file
176+ config_file = open(self.file_path, 'w')
177+ config.write(config_file)
178+
179+
180 class UncryptedFileKeyring(BasicFileKeyring):
181 """Uncrypted File Keyring"""
182
183@@ -348,6 +408,7 @@
184 """
185 return 0
186
187+
188 class CryptedFileKeyring(BasicFileKeyring):
189 """PyCrypto File Keyring"""
190
191@@ -377,7 +438,8 @@
192 password = None
193 while 1:
194 if not password:
195- password = self._getpass("Please set a password for your new keyring")
196+ password = self._getpass("Please set a password"
197+ " for your new keyring")
198 password2 = self._getpass('Password (again): ')
199 if password != password2:
200 sys.stderr.write("Error: Your passwords didn't match\n")
201@@ -404,7 +466,7 @@
202 config.add_section(_KEYRING_SETTING)
203 config.set(_KEYRING_SETTING, _CRYPTED_PASSWORD, self.crypted_password)
204
205- config_file = open(self.file_path,'w')
206+ config_file = open(self.file_path, 'w')
207 config.write(config_file)
208
209 if config_file:
210@@ -519,7 +581,9 @@
211 def __init__(self):
212 super(WinVaultKeyring, self).__init__()
213 try:
214- import pywintypes, win32cred
215+ import pywintypes
216+ import win32cred
217+
218 self.win32cred = win32cred
219 self.pywintypes = pywintypes
220 except ImportError:
221@@ -558,7 +622,7 @@
222 TargetName=target,
223 )
224 except self.pywintypes.error, e:
225- if e.winerror == 1168 and e.funcname == 'CredRead': # not found
226+ if e[:2] == (1168, 'CredRead'): # not found
227 return None
228 raise
229 return res
230@@ -583,6 +647,8 @@
231 self.win32cred.CredWrite(credential, 0)
232
233 def delete_password(self, service, username):
234+ """Delete the password for the username of the service.
235+ """
236 compound = self._compound_name(username, service)
237 for target in service, compound:
238 existing_pw = self._get_password(target)
239@@ -590,11 +656,13 @@
240 self._delete_password(target)
241
242 def _delete_password(self, target):
243+ """Call win32 api to delete the password."""
244 self.win32cred.CredDelete(
245 Type=self.win32cred.CRED_TYPE_GENERIC,
246 TargetName=target,
247 )
248
249+
250 class Win32CryptoRegistry(KeyringBackend):
251 """Win32CryptoRegistry is a keyring which use Windows CryptAPI to encrypt
252 the user's passwords and store them under registry keys
253@@ -633,33 +701,47 @@
254 hkey = OpenKey(HKEY_CURRENT_USER, key)
255 password_base64 = QueryValueEx(hkey, username)[0]
256 # decode with base64
257- password_encrypted = base64.encodestring(password_base64)
258+ password_encrypted = password_base64.decode("base64")
259 # decrypted the password
260 password = self.crypt_handler.decrypt(password_encrypted)
261 except EnvironmentError:
262 password = None
263 return password
264
265-
266 def set_password(self, service, username, password):
267 """Write the password to the registry
268 """
269 # encrypt the password
270 password_encrypted = self.crypt_handler.encrypt(password)
271 # encode with base64
272- password_base64 = base64.encodestring(password_encrypted)
273+ password_base64 = password_encrypted.encode("base64")
274
275 # store the password
276 from _winreg import HKEY_CURRENT_USER, CreateKey, SetValueEx, REG_SZ
277 hkey = CreateKey(HKEY_CURRENT_USER, r'Software\%s\Keyring' % service)
278 SetValueEx(hkey, username, 0, REG_SZ, password_base64)
279
280+ def delete_password(self, service, username):
281+ """Delete the password for the username of the service.
282+ """
283+ from _winreg import (KEY_ALL_ACCESS, HKEY_CURRENT_USER, DeleteValue,
284+ OpenKey)
285+ try:
286+ key = r'Software\%s\Keyring' % service
287+ hkey = OpenKey(HKEY_CURRENT_USER, key, 0, KEY_ALL_ACCESS)
288+ DeleteValue(hkey, username)
289+ except WindowsError, e:
290+ raise PasswordDeleteError(e)
291+
292+
293 def select_windows_backend():
294 if os.name != 'nt':
295 return None
296 major, minor, build, platform, text = sys.getwindowsversion()
297 try:
298- import pywintypes, win32cred
299+ import pywintypes
300+ import win32cred
301+
302 if (major, minor) >= (5, 1):
303 # recommend for windows xp+
304 return 'cred'
305@@ -683,14 +765,14 @@
306
307 _all_keyring = None
308
309+
310 def get_all_keyring():
311 """Return the list of all keyrings in the lib
312 """
313 global _all_keyring
314 if _all_keyring is None:
315- _all_keyring = [ OSXKeychain(), GnomeKeyring(), KDEKWallet(),
316- CryptedFileKeyring(), UncryptedFileKeyring(),
317- Win32CryptoKeyring(), Win32CryptoRegistry(),
318- WinVaultKeyring()]
319+ _all_keyring = [OSXKeychain(), GnomeKeyring(), KDEKWallet(),
320+ CryptedFileKeyring(), UncryptedFileKeyring(),
321+ Win32CryptoKeyring(), Win32CryptoRegistry(),
322+ WinVaultKeyring()]
323 return _all_keyring
324-
325--- keyring/cli.py 2013-04-16 10:04:36.000000000 -0500
326+++ keyring/cli.py 2013-04-12 09:26:11.906770400 -0500
327@@ -11,7 +11,8 @@
328
329 class CommandLineTool(object):
330 def __init__(self):
331- self.parser = OptionParser(usage="%prog [get|set] SERVICE USERNAME")
332+ self.parser = OptionParser(
333+ usage="%prog [get|set|del] SERVICE USERNAME")
334 self.parser.add_option("-p", "--keyring-path",
335 dest="keyring_path", default=None,
336 help="Path to the keyring backend")
337@@ -45,7 +46,6 @@
338 # So, we play on the safe side, and catch everything.
339 self.parser.error("Unable to load specified keyring: %s" % e)
340
341-
342 if kind == 'get':
343 password = keyring.get_password(service, username)
344 if password is None:
345@@ -60,8 +60,14 @@
346 keyring.set_password(service, username, password)
347 return 0
348
349+ elif kid == 'del':
350+ password = input_password("Deleting password for '%s' in '%s': " %
351+ (username, service))
352+ keyring.delete_password(service, username)
353+ return 0
354+
355 else:
356- self.parser.error("You can only 'get' or 'set' a password.")
357+ self.parser.error("You can only 'get', 'del' or 'set' a password.")
358 pass
359
360 def input_password(self, prompt):
361@@ -72,7 +78,6 @@
362
363 return getpass.getpass(prompt)
364
365-
366 def output_password(self, password):
367 """Output the password to the user.
368
369@@ -85,7 +90,6 @@
370 def main(argv=None):
371 """Main command line interface."""
372
373-
374 if argv is None:
375 argv = sys.argv[1:]
376
377--- keyring/core.py 2013-04-16 10:04:36.000000000 -0500
378+++ keyring/core.py 2013-04-12 09:26:11.937796900 -0500
379@@ -4,16 +4,14 @@
380 Created by Kang Zhang on 2009-07-09
381 """
382 import os
383-try:
384- import configparser as config_parser
385-except ImportError:
386- import ConfigParser as config_parser
387+import ConfigParser
388 import imp
389 import sys
390
391 from keyring import logger
392 from keyring import backend
393
394+
395 def set_keyring(keyring):
396 """Set current keyring backend.
397 """
398@@ -23,21 +21,31 @@
399 else:
400 raise TypeError("The keyring must be a subclass of KeyringBackend")
401
402+
403 def get_keyring():
404 """Get current keyring backend.
405 """
406 return _keyring_backend
407
408+
409 def get_password(service_name, username):
410- """Get password from the specified service
411+ """Get password from the specified service.
412 """
413 return _keyring_backend.get_password(service_name, username)
414
415+
416 def set_password(service_name, username, password):
417- """Set password for the user in the specified service
418+ """Set password for the user in the specified service.
419 """
420 _keyring_backend.set_password(service_name, username, password)
421
422+
423+def delete_password(service_name, username):
424+ """Delete the password for the user in the specified service.
425+ """
426+ _keyring_backend.delete_password(service_name, username)
427+
428+
429 def init_backend():
430 """Load a keyring from a config file or for the default platform.
431
432@@ -52,7 +60,7 @@
433
434 keyrings = backend.get_all_keyring()
435 # rank according to the supported result
436- keyrings.sort(key = lambda x: -x.supported())
437+ keyrings.sort(lambda x, y: y.supported() - x.supported())
438 # get the most recommended one
439 keyring = keyrings[0]
440
441@@ -95,10 +103,10 @@
442 # avoid import the imported modules
443 module = sys.modules[keyring_name[:keyring_name.rfind('.')]]
444 except KeyError:
445- module = load_module(keyring_name, sys.path+[keyring_path])
446+ module = load_module(keyring_name, sys.path + [keyring_path])
447
448 keyring_class = keyring_name.split('.')[-1].strip()
449- keyring_temp = getattr(module, keyring_class)()
450+ exec "keyring_temp = module." + keyring_class + "() " in locals()
451
452 return keyring_temp
453
454@@ -123,7 +131,7 @@
455 break
456
457 if os.path.exists(keyring_cfg):
458- config = config_parser.RawConfigParser()
459+ config = ConfigParser.RawConfigParser()
460 config.read(keyring_cfg)
461 # load the keyring-path option
462 try:
463@@ -131,7 +139,7 @@
464 keyring_path = config.get("backend", "keyring-path").strip()
465 else:
466 keyring_path = None
467- except config_parser.NoOptionError:
468+ except ConfigParser.NoOptionError:
469 keyring_path = None
470
471 # load the keyring class name, and then load this keyring
472@@ -139,10 +147,10 @@
473 if config.has_section("backend"):
474 keyring_name = config.get("backend", "default-keyring").strip()
475 else:
476- raise config_parser.NoOptionError('backend', 'default-keyring')
477+ raise ConfigParser.NoOptionError('backend', 'default-keyring')
478
479 keyring = load_keyring(keyring_path, keyring_name)
480- except (config_parser.NoOptionError, ImportError):
481+ except (ConfigParser.NoOptionError, ImportError):
482 logger.warning("Keyring config file contains incorrect values.\n" +
483 "Config file: %s" % keyring_cfg)
484
485--- keyring/getpassbackend.py 2013-04-16 10:04:36.000000000 -0500
486+++ keyring/getpassbackend.py 2013-04-12 09:26:11.962790900 -0500
487@@ -4,10 +4,10 @@
488
489 import keyring.core
490
491+
492 def get_password(prompt='Password: ', stream=None,
493 service_name='Python',
494 username=None):
495 if username is None:
496 username = getpass.getuser()
497 return keyring.core.get_password(service_name, username)
498-
499--- keyring/http.py 2013-04-16 10:04:36.000000000 -0500
500+++ keyring/http.py 2013-04-12 09:26:11.989799600 -0500
501@@ -7,7 +7,7 @@
502 handlers = [urllib2.HTTPBasicAuthHandler(PasswordMgr())]
503 urllib2.install_opener(handlers)
504 urllib2.urlopen(...)
505-
506+
507 This will prompt for a password if one is required and isn't already
508 in the keyring. Then, it adds it to the keyring for subsequent use.
509 """
510@@ -15,6 +15,7 @@
511 import keyring
512 import getpass
513
514+
515 class PasswordMgr(object):
516 def get_username(self, realm, authuri):
517 return getpass.getuser()
0518
=== added directory 'scripts/devsetup/patches/twisted'
=== added file 'scripts/devsetup/patches/twisted/5726.diff'
--- scripts/devsetup/patches/twisted/5726.diff 1970-01-01 00:00:00 +0000
+++ scripts/devsetup/patches/twisted/5726.diff 2013-05-06 19:02:26 +0000
@@ -0,0 +1,91 @@
1=== modified file 'twisted/internet/_dumbwin32proc.py'
2--- twisted/internet/_dumbwin32proc.py 2011-10-02 01:01:47 +0000
3+++ twisted/internet/_dumbwin32proc.py 2012-07-16 21:39:49 +0000
4@@ -172,8 +172,9 @@
5 cmdline = quoteArguments(args)
6 # TODO: error detection here. See #2787 and #4184.
7 def doCreate():
8+ flags = win32con.CREATE_NO_WINDOW
9 self.hProcess, self.hThread, self.pid, dwTid = win32process.CreateProcess(
10- command, cmdline, None, None, 1, 0, env, path, StartupInfo)
11+ command, cmdline, None, None, 1, flags, env, path, StartupInfo)
12 try:
13 try:
14 doCreate()
15
16=== modified file 'twisted/test/test_process.py'
17--- twisted/test/test_process.py 2012-07-08 18:39:59 +0000
18+++ twisted/test/test_process.py 2012-07-19 05:27:43 +0000
19@@ -27,7 +27,7 @@
20 from twisted.python.log import msg
21 from twisted.internet import reactor, protocol, error, interfaces, defer
22 from twisted.trial import unittest
23-from twisted.python import util, runtime, procutils
24+from twisted.python import filepath, util, runtime, procutils
25 from twisted.python.compat import set
26
27
28@@ -2350,6 +2350,48 @@
29
30
31
32+class Win32CreateProcessFlagsTest(unittest.TestCase):
33+ """
34+ Check the flags passed to CreateProcess.
35+ """
36+
37+ @defer.inlineCallbacks
38+ def test_flags(self):
39+ """
40+ Verify that the flags passed to win32process.CreateProcess() prevent a
41+ new console window from being created. See bug #5726 for a script to
42+ test this interactively.
43+ """
44+ from twisted.internet import _dumbwin32proc
45+ flags = []
46+ real_CreateProcess = _dumbwin32proc.win32process.CreateProcess
47+
48+ def fake_createprocess(_appName, _commandLine, _processAttributes,
49+ _threadAttributes, _bInheritHandles, creationFlags,
50+ _newEnvironment, _currentDirectory, startupinfo):
51+ """Store the creationFlags for later comparing."""
52+ flags.append(creationFlags)
53+ return real_CreateProcess(_appName, _commandLine,
54+ _processAttributes, _threadAttributes,
55+ _bInheritHandles, creationFlags, _newEnvironment,
56+ _currentDirectory, startupinfo)
57+
58+ self.patch(_dumbwin32proc.win32process, "CreateProcess",
59+ fake_createprocess)
60+ exe = sys.executable
61+ scriptPath = filepath.FilePath(__file__).sibling("process_cmdline.py")
62+
63+ d = defer.Deferred()
64+ processProto = TrivialProcessProtocol(d)
65+ comspec = str(os.environ["COMSPEC"])
66+ cmd = [comspec, "/c", exe, scriptPath.path]
67+ _dumbwin32proc.Process(reactor, processProto, None, cmd, {}, None)
68+ yield d
69+ self.assertEqual(flags,
70+ [_dumbwin32proc.win32process.CREATE_NO_WINDOW])
71+
72+
73+
74 class UtilTestCase(unittest.TestCase):
75 """
76 Tests for process-related helper functions (currently only
77@@ -2553,6 +2595,7 @@
78 Win32ProcessTestCase.skip = skipMessage
79 TestTwoProcessesNonPosix.skip = skipMessage
80 Dumbwin32procPidTest.skip = skipMessage
81+ Win32CreateProcessFlagsTest.skip = skipMessage
82 Win32UnicodeEnvironmentTest.skip = skipMessage
83
84 if not interfaces.IReactorProcess(reactor, None):
85
86=== added file 'twisted/topfiles/5726.bugfix'
87--- twisted/topfiles/5726.bugfix 1970-01-01 00:00:00 +0000
88+++ twisted/topfiles/5726.bugfix 2012-07-16 22:43:20 +0000
89@@ -0,0 +1,1 @@
90+spawnProcess no longer opens an unwanted console on Windows
91
092
=== added file 'scripts/devsetup/patches/twisted/6462.diff'
--- scripts/devsetup/patches/twisted/6462.diff 1970-01-01 00:00:00 +0000
+++ scripts/devsetup/patches/twisted/6462.diff 2013-05-06 19:02:26 +0000
@@ -0,0 +1,57 @@
1Index: twisted/internet/_dumbwin32proc.py
2===================================================================
3--- twisted/internet/_dumbwin32proc.py (revision 38219)
4+++ twisted/internet/_dumbwin32proc.py (working copy)
5@@ -7,6 +7,7 @@
6 """
7
8 import os
9+import sys
10
11 # Win32 imports
12 import win32api
13@@ -184,8 +185,9 @@
14 'all must be unicode',):
15 raise
16 newenv = {}
17+ encoding = sys.getfilesystemencoding()
18 for key, value in env.items():
19- newenv[unicode(key)] = unicode(value)
20+ newenv[key.decode(encoding)] = value.decode(encoding)
21 env = newenv
22 doCreate()
23 except pywintypes.error, pwte:
24Index: twisted/test/test_process.py
25===================================================================
26--- twisted/test/test_process.py (revision 38219)
27+++ twisted/test/test_process.py (working copy)
28@@ -2293,6 +2293,7 @@
29 """
30 goodKey = u'UNICODE'
31 goodValue = u'UNICODE'
32+ encodedValue = u'jalape\xf1o'.encode("mbcs")
33
34 def test_encodableUnicodeEnvironment(self):
35 """
36@@ -2311,8 +2312,21 @@
37 self.goodValue.encode('ascii'))
38 return p.getResult().addCallback(gotEnvironment)
39
40+ def test_decodableUnicodeEnvironment(self):
41+ """
42+ Test C{os.environ} (inherited by every subprocess on Windows) that
43+ contains an MBCS encoded byte string value."""
44+ os.environ[self.goodKey] = self.encodedValue
45+ self.addCleanup(operator.delitem, os.environ, self.goodKey)
46
47+ p = GetEnvironmentDictionary.run(reactor, [], {})
48+ def gotEnvironment(environ):
49+ self.assertEqual(
50+ environ[self.goodKey.encode('ascii')],
51+ self.encodedValue)
52+ return p.getResult().addCallback(gotEnvironment)
53
54+
55 class Dumbwin32procPidTest(unittest.TestCase):
56 """
57 Simple test for the pid attribute of Process on win32.

Subscribers

People subscribed via source and target branches