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