Merge lp:~jameinel/bzr/2.1.0b2-471193-st-and-chk-map into lp:bzr
- 2.1.0b2-471193-st-and-chk-map
- Merge into bzr.dev
Proposed by
John A Meinel
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | not available | ||||
Proposed branch: | lp:~jameinel/bzr/2.1.0b2-471193-st-and-chk-map | ||||
Merge into: | lp:bzr | ||||
Diff against target: |
565 lines 10 files modified
NEWS (+12/-3) bzrlib/chk_map.py (+3/-3) bzrlib/help_topics/en/debug-flags.txt (+1/-0) bzrlib/plugins/launchpad/__init__.py (+12/-23) bzrlib/plugins/launchpad/lp_registration.py (+25/-4) bzrlib/plugins/launchpad/test_lp_directory.py (+144/-1) bzrlib/static_tuple.py (+17/-0) bzrlib/tests/test__static_tuple.py (+26/-0) bzrlib/tests/test_http.py (+55/-1) bzrlib/transport/http/_urllib2_wrappers.py (+19/-7) |
||||
To merge this branch: | bzr merge lp:~jameinel/bzr/2.1.0b2-471193-st-and-chk-map | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Vincent Ladeuil | Approve | ||
Review via email: mp+14309@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
John A Meinel (jameinel) wrote : | # |
Revision history for this message
Vincent Ladeuil (vila) wrote : | # |
Don't forget to land in 2.1.0b2 and not bzr.dev !
The long term intent being to get rid of expect_
please say so in the doc string.
Also, you may want to mail bzr.dev users about setting the flag in their bazaar.conf
debug_flags defaults.
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'NEWS' | |||
2 | --- NEWS 2009-11-01 05:41:57 +0000 | |||
3 | +++ NEWS 2009-11-02 18:26:13 +0000 | |||
4 | @@ -101,9 +101,10 @@ | |||
5 | 101 | 101 | ||
6 | 102 | Key highlights in this release are: improved handling of | 102 | Key highlights in this release are: improved handling of |
7 | 103 | failures-during-cleanup for commit, fixing a long-standing bug with | 103 | failures-during-cleanup for commit, fixing a long-standing bug with |
11 | 104 | ``bzr+http`` and shared repositories, and a new StaticTuple datatype, | 104 | ``bzr+http`` and shared repositories, all ``lp:`` urls to be resolved |
12 | 105 | allowing us to reduce memory consumption (50%) and garbage collector | 105 | behind proxies, and a new StaticTuple datatype, allowing us to reduce |
13 | 106 | overhead (40% faster) for many operations. | 106 | memory consumption (50%) and garbage collector overhead (40% faster) for |
14 | 107 | many operations. | ||
15 | 107 | 108 | ||
16 | 108 | Bug Fixes | 109 | Bug Fixes |
17 | 109 | ********* | 110 | ********* |
18 | @@ -116,6 +117,14 @@ | |||
19 | 116 | they do occur. This fixes some causes of ``TooManyConcurrentRequests`` | 117 | they do occur. This fixes some causes of ``TooManyConcurrentRequests`` |
20 | 117 | and similar errors. (Andrew Bennetts, #429747, #243391) | 118 | and similar errors. (Andrew Bennetts, #429747, #243391) |
21 | 118 | 119 | ||
22 | 120 | * Launchpad urls can now be resolved from behind proxies. | ||
23 | 121 | (Gordon Tyler, Vincent Ladeuil, #198920) | ||
24 | 122 | |||
25 | 123 | * Reduce the strictness for StaticTuple, instead add a debug flag | ||
26 | 124 | ``-Dstatic_tuple`` which will change apis to be strict and raise errors. | ||
27 | 125 | This way, most users won't see failures, but developers can improve | ||
28 | 126 | internals. (John Arbash Meinel, #471193) | ||
29 | 127 | |||
30 | 119 | * TreeTransform.adjust_path updates the limbo paths of descendants of adjusted | 128 | * TreeTransform.adjust_path updates the limbo paths of descendants of adjusted |
31 | 120 | files. (Aaron Bentley) | 129 | files. (Aaron Bentley) |
32 | 121 | 130 | ||
33 | 122 | 131 | ||
34 | === modified file 'bzrlib/chk_map.py' | |||
35 | --- bzrlib/chk_map.py 2009-10-23 18:46:03 +0000 | |||
36 | +++ bzrlib/chk_map.py 2009-11-02 18:26:13 +0000 | |||
37 | @@ -50,6 +50,7 @@ | |||
38 | 50 | lru_cache, | 50 | lru_cache, |
39 | 51 | osutils, | 51 | osutils, |
40 | 52 | registry, | 52 | registry, |
41 | 53 | static_tuple, | ||
42 | 53 | trace, | 54 | trace, |
43 | 54 | ) | 55 | ) |
44 | 55 | from bzrlib.static_tuple import StaticTuple | 56 | from bzrlib.static_tuple import StaticTuple |
45 | @@ -720,6 +721,7 @@ | |||
46 | 720 | :param bytes: The bytes of the node. | 721 | :param bytes: The bytes of the node. |
47 | 721 | :param key: The key that the serialised node has. | 722 | :param key: The key that the serialised node has. |
48 | 722 | """ | 723 | """ |
49 | 724 | key = static_tuple.expect_static_tuple(key) | ||
50 | 723 | return _deserialise_leaf_node(bytes, key, | 725 | return _deserialise_leaf_node(bytes, key, |
51 | 724 | search_key_func=search_key_func) | 726 | search_key_func=search_key_func) |
52 | 725 | 727 | ||
53 | @@ -1018,9 +1020,7 @@ | |||
54 | 1018 | :param key: The key that the serialised node has. | 1020 | :param key: The key that the serialised node has. |
55 | 1019 | :return: An InternalNode instance. | 1021 | :return: An InternalNode instance. |
56 | 1020 | """ | 1022 | """ |
60 | 1021 | if type(key) is not StaticTuple: | 1023 | key = static_tuple.expect_static_tuple(key) |
58 | 1022 | raise AssertionError('deserialise should be called with a' | ||
59 | 1023 | ' StaticTuple not %s' % (type(key),)) | ||
61 | 1024 | return _deserialise_internal_node(bytes, key, | 1024 | return _deserialise_internal_node(bytes, key, |
62 | 1025 | search_key_func=search_key_func) | 1025 | search_key_func=search_key_func) |
63 | 1026 | 1026 | ||
64 | 1027 | 1027 | ||
65 | === modified file 'bzrlib/help_topics/en/debug-flags.txt' | |||
66 | --- bzrlib/help_topics/en/debug-flags.txt 2009-10-07 08:19:19 +0000 | |||
67 | +++ bzrlib/help_topics/en/debug-flags.txt 2009-11-02 18:26:13 +0000 | |||
68 | @@ -30,6 +30,7 @@ | |||
69 | 30 | -Drelock Emit a message every time a branch or repository object is | 30 | -Drelock Emit a message every time a branch or repository object is |
70 | 31 | unlocked then relocked the same way. | 31 | unlocked then relocked the same way. |
71 | 32 | -Dsftp Trace SFTP internals. | 32 | -Dsftp Trace SFTP internals. |
72 | 33 | -Dstatic_tuple Error when a tuple is used where a StaticTuple is expected | ||
73 | 33 | -Dstream Trace fetch streams. | 34 | -Dstream Trace fetch streams. |
74 | 34 | -Dstrict_locks Trace when OS locks are potentially used in a non-portable | 35 | -Dstrict_locks Trace when OS locks are potentially used in a non-portable |
75 | 35 | manner. | 36 | manner. |
76 | 36 | 37 | ||
77 | === modified file 'bzrlib/plugins/launchpad/__init__.py' | |||
78 | --- bzrlib/plugins/launchpad/__init__.py 2009-07-06 13:00:23 +0000 | |||
79 | +++ bzrlib/plugins/launchpad/__init__.py 2009-11-02 18:26:13 +0000 | |||
80 | @@ -262,30 +262,19 @@ | |||
81 | 262 | _register_directory() | 262 | _register_directory() |
82 | 263 | 263 | ||
83 | 264 | 264 | ||
95 | 265 | def test_suite(): | 265 | def load_tests(basic_tests, module, loader): |
96 | 266 | """Called by bzrlib to fetch tests for this plugin""" | 266 | testmod_names = [ |
97 | 267 | from unittest import TestSuite, TestLoader | 267 | 'test_account', |
98 | 268 | from bzrlib.plugins.launchpad import ( | 268 | 'test_register', |
99 | 269 | test_account, | 269 | 'test_lp_directory', |
100 | 270 | test_lp_directory, | 270 | 'test_lp_login', |
101 | 271 | test_lp_login, | 271 | 'test_lp_open', |
102 | 272 | test_lp_open, | 272 | 'test_lp_service', |
103 | 273 | test_lp_service, | 273 | ] |
104 | 274 | test_register, | 274 | basic_tests.addTest(loader.loadTestsFromModuleNames( |
105 | 275 | ) | 275 | ["%s.%s" % (__name__, tmn) for tmn in testmod_names])) |
106 | 276 | return basic_tests | ||
107 | 276 | 277 | ||
108 | 277 | loader = TestLoader() | ||
109 | 278 | suite = TestSuite() | ||
110 | 279 | for module in [ | ||
111 | 280 | test_account, | ||
112 | 281 | test_register, | ||
113 | 282 | test_lp_directory, | ||
114 | 283 | test_lp_login, | ||
115 | 284 | test_lp_open, | ||
116 | 285 | test_lp_service, | ||
117 | 286 | ]: | ||
118 | 287 | suite.addTests(loader.loadTestsFromModule(module)) | ||
119 | 288 | return suite | ||
120 | 289 | 278 | ||
121 | 290 | _launchpad_help = """Integration with Launchpad.net | 279 | _launchpad_help = """Integration with Launchpad.net |
122 | 291 | 280 | ||
123 | 292 | 281 | ||
124 | === modified file 'bzrlib/plugins/launchpad/lp_registration.py' | |||
125 | --- bzrlib/plugins/launchpad/lp_registration.py 2009-07-04 16:22:16 +0000 | |||
126 | +++ bzrlib/plugins/launchpad/lp_registration.py 2009-11-02 18:26:13 +0000 | |||
127 | @@ -31,6 +31,7 @@ | |||
128 | 31 | errors, | 31 | errors, |
129 | 32 | __version__ as _bzrlib_version, | 32 | __version__ as _bzrlib_version, |
130 | 33 | ) | 33 | ) |
131 | 34 | from bzrlib.transport.http import _urllib2_wrappers | ||
132 | 34 | 35 | ||
133 | 35 | # for testing, do | 36 | # for testing, do |
134 | 36 | ''' | 37 | ''' |
135 | @@ -53,6 +54,29 @@ | |||
136 | 53 | errors.BzrError.__init__(self, url=url) | 54 | errors.BzrError.__init__(self, url=url) |
137 | 54 | 55 | ||
138 | 55 | 56 | ||
139 | 57 | class XMLRPCTransport(xmlrpclib.Transport): | ||
140 | 58 | |||
141 | 59 | def __init__(self, scheme, use_datetime=0): | ||
142 | 60 | xmlrpclib.Transport.__init__(self, use_datetime=use_datetime) | ||
143 | 61 | self._scheme = scheme | ||
144 | 62 | self._opener = _urllib2_wrappers.Opener() | ||
145 | 63 | self.verbose = 0 | ||
146 | 64 | |||
147 | 65 | def request(self, host, handler, request_body, verbose=0): | ||
148 | 66 | self.verbose = verbose | ||
149 | 67 | url = self._scheme + "://" + host + handler | ||
150 | 68 | request = _urllib2_wrappers.Request("POST", url, request_body) | ||
151 | 69 | # FIXME: _urllib2_wrappers will override user-agent with its own | ||
152 | 70 | # request.add_header("User-Agent", self.user_agent) | ||
153 | 71 | request.add_header("Content-Type", "text/xml") | ||
154 | 72 | |||
155 | 73 | response = self._opener.open(request) | ||
156 | 74 | if response.code != 200: | ||
157 | 75 | raise xmlrpclib.ProtocolError(host + handler, response.code, | ||
158 | 76 | response.msg, response.info()) | ||
159 | 77 | return self.parse_response(response) | ||
160 | 78 | |||
161 | 79 | |||
162 | 56 | class LaunchpadService(object): | 80 | class LaunchpadService(object): |
163 | 57 | """A service to talk to Launchpad via XMLRPC. | 81 | """A service to talk to Launchpad via XMLRPC. |
164 | 58 | 82 | ||
165 | @@ -90,10 +114,7 @@ | |||
166 | 90 | self._lp_instance = lp_instance | 114 | self._lp_instance = lp_instance |
167 | 91 | if transport is None: | 115 | if transport is None: |
168 | 92 | uri_type = urllib.splittype(self.service_url)[0] | 116 | uri_type = urllib.splittype(self.service_url)[0] |
173 | 93 | if uri_type == 'https': | 117 | transport = XMLRPCTransport(uri_type) |
170 | 94 | transport = xmlrpclib.SafeTransport() | ||
171 | 95 | else: | ||
172 | 96 | transport = xmlrpclib.Transport() | ||
174 | 97 | transport.user_agent = 'bzr/%s (xmlrpclib/%s)' \ | 118 | transport.user_agent = 'bzr/%s (xmlrpclib/%s)' \ |
175 | 98 | % (_bzrlib_version, xmlrpclib.__version__) | 119 | % (_bzrlib_version, xmlrpclib.__version__) |
176 | 99 | self.transport = transport | 120 | self.transport = transport |
177 | 100 | 121 | ||
178 | === modified file 'bzrlib/plugins/launchpad/test_lp_directory.py' | |||
179 | --- bzrlib/plugins/launchpad/test_lp_directory.py 2009-03-23 14:59:43 +0000 | |||
180 | +++ bzrlib/plugins/launchpad/test_lp_directory.py 2009-11-02 18:26:13 +0000 | |||
181 | @@ -16,10 +16,12 @@ | |||
182 | 16 | 16 | ||
183 | 17 | """Tests for directory lookup through Launchpad.net""" | 17 | """Tests for directory lookup through Launchpad.net""" |
184 | 18 | 18 | ||
185 | 19 | import os | ||
186 | 19 | import xmlrpclib | 20 | import xmlrpclib |
187 | 20 | 21 | ||
188 | 21 | from bzrlib import ( | 22 | from bzrlib import ( |
189 | 22 | errors, | 23 | errors, |
190 | 24 | tests, | ||
191 | 23 | ) | 25 | ) |
192 | 24 | from bzrlib.branch import Branch | 26 | from bzrlib.branch import Branch |
193 | 25 | from bzrlib.directory_service import directories | 27 | from bzrlib.directory_service import directories |
194 | @@ -28,10 +30,38 @@ | |||
195 | 28 | TestCaseWithMemoryTransport | 30 | TestCaseWithMemoryTransport |
196 | 29 | ) | 31 | ) |
197 | 30 | from bzrlib.transport import get_transport | 32 | from bzrlib.transport import get_transport |
199 | 31 | from bzrlib.plugins.launchpad import _register_directory | 33 | from bzrlib.plugins.launchpad import ( |
200 | 34 | _register_directory, | ||
201 | 35 | lp_registration, | ||
202 | 36 | ) | ||
203 | 32 | from bzrlib.plugins.launchpad.lp_directory import ( | 37 | from bzrlib.plugins.launchpad.lp_directory import ( |
204 | 33 | LaunchpadDirectory) | 38 | LaunchpadDirectory) |
205 | 34 | from bzrlib.plugins.launchpad.account import get_lp_login | 39 | from bzrlib.plugins.launchpad.account import get_lp_login |
206 | 40 | from bzrlib.tests import ( | ||
207 | 41 | http_server, | ||
208 | 42 | http_utils, | ||
209 | 43 | ) | ||
210 | 44 | |||
211 | 45 | |||
212 | 46 | def load_tests(standard_tests, module, loader): | ||
213 | 47 | result = loader.suiteClass() | ||
214 | 48 | t_tests, remaining_tests = tests.split_suite_by_condition( | ||
215 | 49 | standard_tests, tests.condition_isinstance(( | ||
216 | 50 | TestXMLRPCTransport, | ||
217 | 51 | ))) | ||
218 | 52 | transport_scenarios = [ | ||
219 | 53 | ('http', dict(server_class=PreCannedHTTPServer,)), | ||
220 | 54 | ] | ||
221 | 55 | if tests.HTTPSServerFeature.available(): | ||
222 | 56 | transport_scenarios.append( | ||
223 | 57 | ('https', dict(server_class=PreCannedHTTPSServer,)), | ||
224 | 58 | ) | ||
225 | 59 | tests.multiply_tests(t_tests, transport_scenarios, result) | ||
226 | 60 | |||
227 | 61 | # No parametrization for the remaining tests | ||
228 | 62 | result.addTests(remaining_tests) | ||
229 | 63 | |||
230 | 64 | return result | ||
231 | 35 | 65 | ||
232 | 36 | 66 | ||
233 | 37 | class FakeResolveFactory(object): | 67 | class FakeResolveFactory(object): |
234 | @@ -190,3 +220,116 @@ | |||
235 | 190 | transport = get_transport('lp:///apt') | 220 | transport = get_transport('lp:///apt') |
236 | 191 | branch = Branch.open_from_transport(transport) | 221 | branch = Branch.open_from_transport(transport) |
237 | 192 | self.assertEqual(target_branch.base, branch.base) | 222 | self.assertEqual(target_branch.base, branch.base) |
238 | 223 | |||
239 | 224 | |||
240 | 225 | class PredefinedRequestHandler(http_server.TestingHTTPRequestHandler): | ||
241 | 226 | """Request handler for a unique and pre-defined request. | ||
242 | 227 | |||
243 | 228 | The only thing we care about here is that we receive a connection. But | ||
244 | 229 | since we want to dialog with a real http client, we have to send it correct | ||
245 | 230 | responses. | ||
246 | 231 | |||
247 | 232 | We expect to receive a *single* request nothing more (and we won't even | ||
248 | 233 | check what request it is), the tests will recognize us from our response. | ||
249 | 234 | """ | ||
250 | 235 | |||
251 | 236 | def handle_one_request(self): | ||
252 | 237 | tcs = self.server.test_case_server | ||
253 | 238 | requestline = self.rfile.readline() | ||
254 | 239 | headers = self.MessageClass(self.rfile, 0) | ||
255 | 240 | if requestline.startswith('POST'): | ||
256 | 241 | # The body should be a single line (or we don't know where it ends | ||
257 | 242 | # and we don't want to issue a blocking read) | ||
258 | 243 | body = self.rfile.readline() | ||
259 | 244 | |||
260 | 245 | self.wfile.write(tcs.canned_response) | ||
261 | 246 | |||
262 | 247 | |||
263 | 248 | class PreCannedServerMixin(object): | ||
264 | 249 | |||
265 | 250 | def __init__(self): | ||
266 | 251 | super(PreCannedServerMixin, self).__init__( | ||
267 | 252 | request_handler=PredefinedRequestHandler) | ||
268 | 253 | # Bytes read and written by the server | ||
269 | 254 | self.bytes_read = 0 | ||
270 | 255 | self.bytes_written = 0 | ||
271 | 256 | self.canned_response = None | ||
272 | 257 | |||
273 | 258 | |||
274 | 259 | class PreCannedHTTPServer(PreCannedServerMixin, http_server.HttpServer): | ||
275 | 260 | pass | ||
276 | 261 | |||
277 | 262 | |||
278 | 263 | if tests.HTTPSServerFeature.available(): | ||
279 | 264 | from bzrlib.tests import https_server | ||
280 | 265 | class PreCannedHTTPSServer(PreCannedServerMixin, https_server.HTTPSServer): | ||
281 | 266 | pass | ||
282 | 267 | |||
283 | 268 | |||
284 | 269 | class TestXMLRPCTransport(tests.TestCase): | ||
285 | 270 | |||
286 | 271 | # set by load_tests | ||
287 | 272 | server_class = None | ||
288 | 273 | |||
289 | 274 | def setUp(self): | ||
290 | 275 | tests.TestCase.setUp(self) | ||
291 | 276 | self.server = self.server_class() | ||
292 | 277 | self.server.setUp() | ||
293 | 278 | # Ensure we don't clobber env | ||
294 | 279 | self._captureVar('BZR_LP_XMLRPC_URL', None) | ||
295 | 280 | |||
296 | 281 | def tearDown(self): | ||
297 | 282 | self.server.tearDown() | ||
298 | 283 | tests.TestCase.tearDown(self) | ||
299 | 284 | |||
300 | 285 | def set_canned_response(self, server, path): | ||
301 | 286 | response_format = '''HTTP/1.1 200 OK\r | ||
302 | 287 | Date: Tue, 11 Jul 2006 04:32:56 GMT\r | ||
303 | 288 | Server: Apache/2.0.54 (Fedora)\r | ||
304 | 289 | Last-Modified: Sun, 23 Apr 2006 19:35:20 GMT\r | ||
305 | 290 | ETag: "56691-23-38e9ae00"\r | ||
306 | 291 | Accept-Ranges: bytes\r | ||
307 | 292 | Content-Length: %(length)d\r | ||
308 | 293 | Connection: close\r | ||
309 | 294 | Content-Type: text/plain; charset=UTF-8\r | ||
310 | 295 | \r | ||
311 | 296 | <?xml version='1.0'?> | ||
312 | 297 | <methodResponse> | ||
313 | 298 | <params> | ||
314 | 299 | <param> | ||
315 | 300 | <value><struct> | ||
316 | 301 | <member> | ||
317 | 302 | <name>urls</name> | ||
318 | 303 | <value><array><data> | ||
319 | 304 | <value><string>bzr+ssh://bazaar.launchpad.net/%(path)s</string></value> | ||
320 | 305 | <value><string>http://bazaar.launchpad.net/%(path)s</string></value> | ||
321 | 306 | </data></array></value> | ||
322 | 307 | </member> | ||
323 | 308 | </struct></value> | ||
324 | 309 | </param> | ||
325 | 310 | </params> | ||
326 | 311 | </methodResponse> | ||
327 | 312 | ''' | ||
328 | 313 | length = 334 + 2 * len(path) | ||
329 | 314 | server.canned_response = response_format % dict(length=length, | ||
330 | 315 | path=path) | ||
331 | 316 | |||
332 | 317 | def do_request(self, server_url): | ||
333 | 318 | os.environ['BZR_LP_XMLRPC_URL'] = self.server.get_url() | ||
334 | 319 | service = lp_registration.LaunchpadService() | ||
335 | 320 | resolve = lp_registration.ResolveLaunchpadPathRequest('bzr') | ||
336 | 321 | result = resolve.submit(service) | ||
337 | 322 | return result | ||
338 | 323 | |||
339 | 324 | def test_direct_request(self): | ||
340 | 325 | self.set_canned_response(self.server, '~bzr-pqm/bzr/bzr.dev') | ||
341 | 326 | result = self.do_request(self.server.get_url()) | ||
342 | 327 | urls = result.get('urls', None) | ||
343 | 328 | self.assertIsNot(None, urls) | ||
344 | 329 | self.assertEquals( | ||
345 | 330 | ['bzr+ssh://bazaar.launchpad.net/~bzr-pqm/bzr/bzr.dev', | ||
346 | 331 | 'http://bazaar.launchpad.net/~bzr-pqm/bzr/bzr.dev'], | ||
347 | 332 | urls) | ||
348 | 333 | # FIXME: we need to test with a real proxy, I can't find a way so simulate | ||
349 | 334 | # CONNECT without leaving one server hanging the test :-/ Since that maybe | ||
350 | 335 | # related to the leaking tests problems, I'll punt for now -- vila 20091030 | ||
351 | 193 | 336 | ||
352 | === modified file 'bzrlib/static_tuple.py' | |||
353 | --- bzrlib/static_tuple.py 2009-10-12 20:02:27 +0000 | |||
354 | +++ bzrlib/static_tuple.py 2009-11-02 18:26:13 +0000 | |||
355 | @@ -16,6 +16,8 @@ | |||
356 | 16 | 16 | ||
357 | 17 | """Interface thunk for a StaticTuple implementation.""" | 17 | """Interface thunk for a StaticTuple implementation.""" |
358 | 18 | 18 | ||
359 | 19 | from bzrlib import debug | ||
360 | 20 | |||
361 | 19 | try: | 21 | try: |
362 | 20 | from bzrlib._static_tuple_c import StaticTuple | 22 | from bzrlib._static_tuple_c import StaticTuple |
363 | 21 | except ImportError, e: | 23 | except ImportError, e: |
364 | @@ -23,3 +25,18 @@ | |||
365 | 23 | osutils.failed_to_load_extension(e) | 25 | osutils.failed_to_load_extension(e) |
366 | 24 | from bzrlib._static_tuple_py import StaticTuple | 26 | from bzrlib._static_tuple_py import StaticTuple |
367 | 25 | 27 | ||
368 | 28 | |||
369 | 29 | def expect_static_tuple(obj): | ||
370 | 30 | """Check if the passed object is a StaticTuple. | ||
371 | 31 | |||
372 | 32 | Cast it if necessary, but if the 'static_tuple' debug flag is set, raise an | ||
373 | 33 | error instead. | ||
374 | 34 | |||
375 | 35 | As apis are improved, we will probably eventually stop calling this as it | ||
376 | 36 | adds overhead we shouldn't need. | ||
377 | 37 | """ | ||
378 | 38 | if 'static_tuple' not in debug.debug_flags: | ||
379 | 39 | return StaticTuple.from_sequence(obj) | ||
380 | 40 | if type(obj) is not StaticTuple: | ||
381 | 41 | raise TypeError('We expected a StaticTuple not a %s' % (type(obj),)) | ||
382 | 42 | return obj | ||
383 | 26 | 43 | ||
384 | === modified file 'bzrlib/tests/test__static_tuple.py' | |||
385 | --- bzrlib/tests/test__static_tuple.py 2009-10-27 14:07:16 +0000 | |||
386 | +++ bzrlib/tests/test__static_tuple.py 2009-11-02 18:26:13 +0000 | |||
387 | @@ -22,6 +22,7 @@ | |||
388 | 22 | 22 | ||
389 | 23 | from bzrlib import ( | 23 | from bzrlib import ( |
390 | 24 | _static_tuple_py, | 24 | _static_tuple_py, |
391 | 25 | debug, | ||
392 | 25 | errors, | 26 | errors, |
393 | 26 | osutils, | 27 | osutils, |
394 | 27 | static_tuple, | 28 | static_tuple, |
395 | @@ -620,3 +621,28 @@ | |||
396 | 620 | return | 621 | return |
397 | 621 | self.assertIs(static_tuple.StaticTuple, | 622 | self.assertIs(static_tuple.StaticTuple, |
398 | 622 | self.module.StaticTuple) | 623 | self.module.StaticTuple) |
399 | 624 | |||
400 | 625 | |||
401 | 626 | class TestEnsureStaticTuple(tests.TestCase): | ||
402 | 627 | |||
403 | 628 | def test_is_static_tuple(self): | ||
404 | 629 | st = static_tuple.StaticTuple('foo') | ||
405 | 630 | st2 = static_tuple.expect_static_tuple(st) | ||
406 | 631 | self.assertIs(st, st2) | ||
407 | 632 | |||
408 | 633 | def test_is_tuple(self): | ||
409 | 634 | t = ('foo',) | ||
410 | 635 | st = static_tuple.expect_static_tuple(t) | ||
411 | 636 | self.assertIsInstance(st, static_tuple.StaticTuple) | ||
412 | 637 | self.assertEqual(t, st) | ||
413 | 638 | |||
414 | 639 | def test_flagged_is_static_tuple(self): | ||
415 | 640 | debug.debug_flags.add('static_tuple') | ||
416 | 641 | st = static_tuple.StaticTuple('foo') | ||
417 | 642 | st2 = static_tuple.expect_static_tuple(st) | ||
418 | 643 | self.assertIs(st, st2) | ||
419 | 644 | |||
420 | 645 | def test_flagged_is_tuple(self): | ||
421 | 646 | debug.debug_flags.add('static_tuple') | ||
422 | 647 | t = ('foo',) | ||
423 | 648 | self.assertRaises(TypeError, static_tuple.expect_static_tuple, t) | ||
424 | 623 | 649 | ||
425 | === modified file 'bzrlib/tests/test_http.py' | |||
426 | --- bzrlib/tests/test_http.py 2009-09-17 11:54:41 +0000 | |||
427 | +++ bzrlib/tests/test_http.py 2009-11-02 18:26:13 +0000 | |||
428 | @@ -1956,7 +1956,7 @@ | |||
429 | 1956 | pass | 1956 | pass |
430 | 1957 | 1957 | ||
431 | 1958 | 1958 | ||
433 | 1959 | class TestActivity(tests.TestCase): | 1959 | class TestActivityMixin(object): |
434 | 1960 | """Test socket activity reporting. | 1960 | """Test socket activity reporting. |
435 | 1961 | 1961 | ||
436 | 1962 | We use a special purpose server to control the bytes sent and received and | 1962 | We use a special purpose server to control the bytes sent and received and |
437 | @@ -2100,3 +2100,57 @@ | |||
438 | 2100 | code, f = t._post('abc def end-of-body\n') | 2100 | code, f = t._post('abc def end-of-body\n') |
439 | 2101 | self.assertEqual('lalala whatever as long as itsssss\n', f.read()) | 2101 | self.assertEqual('lalala whatever as long as itsssss\n', f.read()) |
440 | 2102 | self.assertActivitiesMatch() | 2102 | self.assertActivitiesMatch() |
441 | 2103 | |||
442 | 2104 | |||
443 | 2105 | class TestActivity(tests.TestCase, TestActivityMixin): | ||
444 | 2106 | |||
445 | 2107 | def setUp(self): | ||
446 | 2108 | tests.TestCase.setUp(self) | ||
447 | 2109 | self.server = self._activity_server(self._protocol_version) | ||
448 | 2110 | self.server.setUp() | ||
449 | 2111 | self.activities = {} | ||
450 | 2112 | def report_activity(t, bytes, direction): | ||
451 | 2113 | count = self.activities.get(direction, 0) | ||
452 | 2114 | count += bytes | ||
453 | 2115 | self.activities[direction] = count | ||
454 | 2116 | |||
455 | 2117 | # We override at class level because constructors may propagate the | ||
456 | 2118 | # bound method and render instance overriding ineffective (an | ||
457 | 2119 | # alternative would be to define a specific ui factory instead...) | ||
458 | 2120 | self.orig_report_activity = self._transport._report_activity | ||
459 | 2121 | self._transport._report_activity = report_activity | ||
460 | 2122 | |||
461 | 2123 | def tearDown(self): | ||
462 | 2124 | self._transport._report_activity = self.orig_report_activity | ||
463 | 2125 | self.server.tearDown() | ||
464 | 2126 | tests.TestCase.tearDown(self) | ||
465 | 2127 | |||
466 | 2128 | |||
467 | 2129 | class TestNoReportActivity(tests.TestCase, TestActivityMixin): | ||
468 | 2130 | |||
469 | 2131 | def setUp(self): | ||
470 | 2132 | tests.TestCase.setUp(self) | ||
471 | 2133 | # Unlike TestActivity, we are really testing ReportingFileSocket and | ||
472 | 2134 | # ReportingSocket, so we don't need all the parametrization. Since | ||
473 | 2135 | # ReportingFileSocket and ReportingSocket are wrappers, it's easier to | ||
474 | 2136 | # test them through their use by the transport than directly (that's a | ||
475 | 2137 | # bit less clean but far more simpler and effective). | ||
476 | 2138 | self.server = ActivityHTTPServer('HTTP/1.1') | ||
477 | 2139 | self._transport=_urllib.HttpTransport_urllib | ||
478 | 2140 | |||
479 | 2141 | self.server.setUp() | ||
480 | 2142 | |||
481 | 2143 | # We override at class level because constructors may propagate the | ||
482 | 2144 | # bound method and render instance overriding ineffective (an | ||
483 | 2145 | # alternative would be to define a specific ui factory instead...) | ||
484 | 2146 | self.orig_report_activity = self._transport._report_activity | ||
485 | 2147 | self._transport._report_activity = None | ||
486 | 2148 | |||
487 | 2149 | def tearDown(self): | ||
488 | 2150 | self._transport._report_activity = self.orig_report_activity | ||
489 | 2151 | self.server.tearDown() | ||
490 | 2152 | tests.TestCase.tearDown(self) | ||
491 | 2153 | |||
492 | 2154 | def assertActivitiesMatch(self): | ||
493 | 2155 | # Nothing to check here | ||
494 | 2156 | pass | ||
495 | 2103 | 2157 | ||
496 | === modified file 'bzrlib/transport/http/_urllib2_wrappers.py' | |||
497 | --- bzrlib/transport/http/_urllib2_wrappers.py 2009-08-19 16:33:39 +0000 | |||
498 | +++ bzrlib/transport/http/_urllib2_wrappers.py 2009-11-02 18:26:13 +0000 | |||
499 | @@ -80,10 +80,13 @@ | |||
500 | 80 | self.filesock = filesock | 80 | self.filesock = filesock |
501 | 81 | self._report_activity = report_activity | 81 | self._report_activity = report_activity |
502 | 82 | 82 | ||
503 | 83 | def report_activity(self, size, direction): | ||
504 | 84 | if self._report_activity: | ||
505 | 85 | self._report_activity(size, direction) | ||
506 | 83 | 86 | ||
507 | 84 | def read(self, size=1): | 87 | def read(self, size=1): |
508 | 85 | s = self.filesock.read(size) | 88 | s = self.filesock.read(size) |
510 | 86 | self._report_activity(len(s), 'read') | 89 | self.report_activity(len(s), 'read') |
511 | 87 | return s | 90 | return s |
512 | 88 | 91 | ||
513 | 89 | def readline(self): | 92 | def readline(self): |
514 | @@ -93,7 +96,7 @@ | |||
515 | 93 | # don't *need* the size parameter we'll stay with readline(self) | 96 | # don't *need* the size parameter we'll stay with readline(self) |
516 | 94 | # -- vila 20090209 | 97 | # -- vila 20090209 |
517 | 95 | s = self.filesock.readline() | 98 | s = self.filesock.readline() |
519 | 96 | self._report_activity(len(s), 'read') | 99 | self.report_activity(len(s), 'read') |
520 | 97 | return s | 100 | return s |
521 | 98 | 101 | ||
522 | 99 | def __getattr__(self, name): | 102 | def __getattr__(self, name): |
523 | @@ -106,13 +109,17 @@ | |||
524 | 106 | self.sock = sock | 109 | self.sock = sock |
525 | 107 | self._report_activity = report_activity | 110 | self._report_activity = report_activity |
526 | 108 | 111 | ||
527 | 112 | def report_activity(self, size, direction): | ||
528 | 113 | if self._report_activity: | ||
529 | 114 | self._report_activity(size, direction) | ||
530 | 115 | |||
531 | 109 | def sendall(self, s, *args): | 116 | def sendall(self, s, *args): |
532 | 110 | self.sock.sendall(s, *args) | 117 | self.sock.sendall(s, *args) |
534 | 111 | self._report_activity(len(s), 'write') | 118 | self.report_activity(len(s), 'write') |
535 | 112 | 119 | ||
536 | 113 | def recv(self, *args): | 120 | def recv(self, *args): |
537 | 114 | s = self.sock.recv(*args) | 121 | s = self.sock.recv(*args) |
539 | 115 | self._report_activity(len(s), 'read') | 122 | self.report_activity(len(s), 'read') |
540 | 116 | return s | 123 | return s |
541 | 117 | 124 | ||
542 | 118 | def makefile(self, mode='r', bufsize=-1): | 125 | def makefile(self, mode='r', bufsize=-1): |
543 | @@ -219,8 +226,7 @@ | |||
544 | 219 | # we want to warn. But not below a given thresold. | 226 | # we want to warn. But not below a given thresold. |
545 | 220 | _range_warning_thresold = 1024 * 1024 | 227 | _range_warning_thresold = 1024 * 1024 |
546 | 221 | 228 | ||
549 | 222 | def __init__(self, | 229 | def __init__(self, report_activity=None): |
548 | 223 | report_activity=None): | ||
550 | 224 | self._response = None | 230 | self._response = None |
551 | 225 | self._report_activity = report_activity | 231 | self._report_activity = report_activity |
552 | 226 | self._ranges_received_whole_file = None | 232 | self._ranges_received_whole_file = None |
553 | @@ -360,7 +366,13 @@ | |||
554 | 360 | 366 | ||
555 | 361 | def set_proxy(self, proxy, type): | 367 | def set_proxy(self, proxy, type): |
556 | 362 | """Set the proxy and remember the proxied host.""" | 368 | """Set the proxy and remember the proxied host.""" |
558 | 363 | self.proxied_host = self.get_host() | 369 | # We need to set the default port ourselves way before it gets set |
559 | 370 | # in the HTTP[S]Connection object at build time. | ||
560 | 371 | if self.type == 'https': | ||
561 | 372 | conn_class = HTTPSConnection | ||
562 | 373 | else: | ||
563 | 374 | conn_class = HTTPConnection | ||
564 | 375 | self.proxied_host = '%s:%s' % (self.get_host(), conn_class.default_port) | ||
565 | 364 | urllib2.Request.set_proxy(self, proxy, type) | 376 | urllib2.Request.set_proxy(self, proxy, type) |
566 | 365 | 377 | ||
567 | 366 | 378 |
This fixes a critical bug for the 2.1.0b2 series.
Basically, there are some code paths by-which regular tuples can end up where we may not want them. We *could* change all of our apis to auto-cast, but that will end up negating the performance advantage. (As then you have 2 objects a tuple *and* a StaticTuple, referring to the same things.)
However, we need a migration strategy. So for now, I'm adding a debug flag that I can set, similar to '-Dhpss'. When set, I chose to cause an exception rather than just printing a traceback. We can change our minds later.