Merge lp:~allenap/maas/drive-bys-2016-09-06 into lp:~maas-committers/maas/trunk

Proposed by Gavin Panella
Status: Merged
Approved by: Gavin Panella
Approved revision: no longer in the source branch.
Merged at revision: 5343
Proposed branch: lp:~allenap/maas/drive-bys-2016-09-06
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 529 lines (+102/-56)
18 files modified
src/maasserver/dhcp.py (+11/-3)
src/maasserver/fields.py (+13/-1)
src/maasserver/models/dnsdata.py (+3/-2)
src/maasserver/models/staticipaddress.py (+4/-2)
src/maasserver/storage_layouts.py (+9/-6)
src/maasserver/tests/test_bootsources.py (+4/-3)
src/maasserver/utils/orm.py (+11/-6)
src/maasserver/websockets/protocol.py (+2/-2)
src/maasserver/websockets/tests/test_protocol.py (+9/-4)
src/metadataserver/fields.py (+2/-4)
src/metadataserver/tests/test_api.py (+7/-1)
src/provisioningserver/cluster_config_command.py (+3/-3)
src/provisioningserver/dns/tests/test_zoneconfig.py (+2/-2)
src/provisioningserver/rackdservices/tests/test_ntp.py (+5/-3)
src/provisioningserver/refresh/__init__.py (+3/-2)
src/provisioningserver/utils/services.py (+3/-1)
src/provisioningserver/utils/tests/test_services.py (+4/-4)
src/provisioningserver/utils/znums.py (+7/-7)
To merge this branch: bzr merge lp:~allenap/maas/drive-bys-2016-09-06
Reviewer Review Type Date Requested Status
Mike Pontillo (community) Approve
Review via email: mp+305121@code.launchpad.net

Commit message

Eliminate most/all mutable default arguments, and other fixes.

Description of the change

Accumulated drive-by fixes.

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

Looks good; thanks for the fixes!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/maasserver/dhcp.py'
2--- src/maasserver/dhcp.py 2016-07-30 01:17:54 +0000
3+++ src/maasserver/dhcp.py 2016-09-07 21:49:08 +0000
4@@ -235,10 +235,13 @@
5 }
6
7
8-def make_hosts_for_subnets(subnets, nodes_dhcp_snippets=[]):
9+def make_hosts_for_subnets(subnets, nodes_dhcp_snippets: list=None):
10 """Return list of host entries to create in the DHCP configuration for the
11 given `subnets`.
12 """
13+ if nodes_dhcp_snippets is None:
14+ nodes_dhcp_snippets = []
15+
16 def get_dhcp_snippets_for_interface(interface):
17 dhcp_snippets = list()
18 for dhcp_snippet in nodes_dhcp_snippets:
19@@ -317,7 +320,7 @@
20
21 def make_subnet_config(
22 rack_controller, subnet, maas_dns_server, ntp_servers, default_domain,
23- failover_peer=None, subnets_dhcp_snippets=[]):
24+ failover_peer=None, subnets_dhcp_snippets: list=None):
25 """Return DHCP subnet configuration dict for a rack interface."""
26 ip_network = subnet.get_ipnetwork()
27 if subnet.dns_servers is not None and len(subnet.dns_servers) > 0:
28@@ -327,6 +330,8 @@
29 dns_servers = maas_dns_server
30 else:
31 dns_servers = ""
32+ if subnets_dhcp_snippets is None:
33+ subnets_dhcp_snippets = []
34 return {
35 'subnet': str(ip_network.network),
36 'subnet_mask': str(ip_network.netmask),
37@@ -369,7 +374,7 @@
38
39 def get_dhcp_configure_for(
40 ip_version, rack_controller, vlan, subnets, ntp_servers, domain,
41- dhcp_snippets=[]):
42+ dhcp_snippets: list=None):
43 """Get the DHCP configuration for `ip_version`."""
44 # Circular imports.
45 from maasserver.dns.zonegenerator import get_dns_server_address
46@@ -399,6 +404,9 @@
47 else:
48 peer_name, peer_config = None, None
49
50+ if dhcp_snippets is None:
51+ dhcp_snippets = []
52+
53 subnets_dhcp_snippets = [
54 dhcp_snippet for dhcp_snippet in dhcp_snippets
55 if dhcp_snippet.subnet is not None]
56
57=== modified file 'src/maasserver/fields.py'
58--- src/maasserver/fields.py 2016-09-01 01:33:31 +0000
59+++ src/maasserver/fields.py 2016-09-07 21:49:08 +0000
60@@ -6,6 +6,7 @@
61 __all__ = [
62 "CIDRField",
63 "EditableBinaryField",
64+ "Field",
65 "HostListFormField",
66 "IPListFormField",
67 "IPv4CIDRField",
68@@ -35,7 +36,7 @@
69 from django.db.models import (
70 BinaryField,
71 CharField,
72- Field,
73+ Field as _BrokenField,
74 GenericIPAddressField,
75 IntegerField,
76 Q,
77@@ -64,6 +65,17 @@
78 import psycopg2.extensions
79
80
81+class Field(_BrokenField):
82+ """Django's `Field` has a mutable default argument, hence is broken.
83+
84+ This fixes it.
85+ """
86+
87+ def __init__(self, *args, validators=None, **kwargs):
88+ kwargs["validators"] = [] if validators is None else validators
89+ super(Field, self).__init__(*args, **kwargs)
90+
91+
92 MAC_RE = re.compile(r'^\s*([0-9a-fA-F]{1,2}[:-]){5}[0-9a-fA-F]{1,2}\s*$')
93
94 MAC_ERROR_MSG = "'%(value)s' is not a valid MAC address."
95
96=== modified file 'src/maasserver/models/dnsdata.py'
97--- src/maasserver/models/dnsdata.py 2016-05-11 19:01:48 +0000
98+++ src/maasserver/models/dnsdata.py 2016-09-07 21:49:08 +0000
99@@ -66,10 +66,11 @@
100 """This is used to return non-address information for a hostname in a way
101 that keeps life simple for the allers. Rrset is a set of (ttl, rrtype,
102 rrdata) tuples."""
103- def __init__(self, system_id=None, rrset=set(), node_type=None):
104+
105+ def __init__(self, system_id=None, rrset: set=None, node_type=None):
106 self.system_id = system_id
107 self.node_type = node_type
108- self.rrset = rrset.copy()
109+ self.rrset = set() if rrset is None else rrset.copy()
110
111 def __repr__(self):
112 return "HostnameRRSetMapping(%r, %r, %r)" % (
113
114=== modified file 'src/maasserver/models/staticipaddress.py'
115--- src/maasserver/models/staticipaddress.py 2016-09-06 17:02:03 +0000
116+++ src/maasserver/models/staticipaddress.py 2016-09-07 21:49:08 +0000
117@@ -70,11 +70,13 @@
118 class HostnameIPMapping:
119 """This is used to return address information for a host in a way that
120 keeps life simple for the callers."""
121- def __init__(self, system_id=None, ttl=None, ips=set(), node_type=None):
122+
123+ def __init__(
124+ self, system_id=None, ttl=None, ips: set=None, node_type=None):
125 self.system_id = system_id
126 self.node_type = node_type
127 self.ttl = ttl
128- self.ips = ips.copy()
129+ self.ips = set() if ips is None else ips.copy()
130
131 def __repr__(self):
132 return "HostnameIPMapping(%r, %r, %r, %r)" % (
133
134=== modified file 'src/maasserver/storage_layouts.py'
135--- src/maasserver/storage_layouts.py 2016-04-12 22:25:44 +0000
136+++ src/maasserver/storage_layouts.py 2016-09-07 21:49:08 +0000
137@@ -55,8 +55,9 @@
138 boot_size = BytesOrPercentageField(required=False)
139 root_size = BytesOrPercentageField(required=False)
140
141- def __init__(self, node, params={}):
142- super(StorageLayoutBase, self).__init__(data=params)
143+ def __init__(self, node, params: dict=None):
144+ super(StorageLayoutBase, self).__init__(
145+ data=({} if params is None else params))
146 self.node = node
147 self.block_devices = self._load_physical_block_devices()
148 self.boot_disk = node.get_boot_disk()
149@@ -501,8 +502,9 @@
150 bcache0 99.5G disk ext4 /
151 """
152
153- def __init__(self, node, params={}):
154- super(BcacheStorageLayout, self).__init__(node, params=params)
155+ def __init__(self, node, params: dict=None):
156+ super(BcacheStorageLayout, self).__init__(
157+ node, params=({} if params is None else params))
158 self.setup_cache_device_field()
159
160 def configure_storage(self, allow_fallback):
161@@ -556,10 +558,11 @@
162 ]
163
164
165-def get_storage_layout_for_node(name, node, params={}):
166+def get_storage_layout_for_node(name, node, params: dict=None):
167 """Get the storage layout object from its name."""
168 if name in STORAGE_LAYOUTS:
169- return STORAGE_LAYOUTS[name][1](node, params=params)
170+ return STORAGE_LAYOUTS[name][1](
171+ node, params=({} if params is None else params))
172 else:
173 return None
174
175
176=== modified file 'src/maasserver/tests/test_bootsources.py'
177--- src/maasserver/tests/test_bootsources.py 2016-06-16 20:51:55 +0000
178+++ src/maasserver/tests/test_bootsources.py 2016-09-07 21:49:08 +0000
179@@ -88,10 +88,11 @@
180 )
181
182
183-def make_boot_image_mapping(image_specs=[]):
184+def make_boot_image_mapping(image_specs=None):
185 mapping = BootImageMapping()
186- for image_spec in image_specs:
187- mapping.setdefault(image_spec, {})
188+ if image_specs is not None:
189+ for image_spec in image_specs:
190+ mapping.setdefault(image_spec, {})
191 return mapping
192
193
194
195=== modified file 'src/maasserver/utils/orm.py'
196--- src/maasserver/utils/orm.py 2016-06-16 17:07:19 +0000
197+++ src/maasserver/utils/orm.py 2016-09-07 21:49:08 +0000
198@@ -44,6 +44,7 @@
199 import threading
200 from time import sleep
201 import types
202+from typing import Container
203
204 from django.core.exceptions import (
205 MultipleObjectsReturned,
206@@ -634,15 +635,18 @@
207 "Savepoints cannot be created outside of a transaction.")
208
209
210-def in_transaction(connection=connection):
211- """Is `connection` in the midst of a transaction?
212+def in_transaction(_connection=None):
213+ """Is `_connection` in the midst of a transaction?
214
215 This only enquires as to Django's perspective on the situation. It does
216 not actually check that the database agrees with Django.
217
218 :return: bool
219 """
220- return connection.in_atomic_block
221+ if _connection is None:
222+ return connection.in_atomic_block
223+ else:
224+ return _connection.in_atomic_block
225
226
227 def validate_in_transaction(connection):
228@@ -826,7 +830,8 @@
229 return specifier, op
230
231
232-def parse_item_specifier_type(specifier, spec_types={}, separator=':'):
233+def parse_item_specifier_type(
234+ specifier, spec_types: Container=None, separator=':'):
235 """
236 Returns a tuple that splits the string int a specifier, and its specifier
237 type.
238@@ -835,8 +840,8 @@
239 be found in the set, returns None in place of the specifier_type.
240
241 :param specifier: The specifier string, such as "ip:10.0.0.1".
242- :param spec_types: A dict whose keys are strings that will be recognized
243- as specifier types.
244+ :param spec_types: A container whose elements are strings that will be
245+ recognized as specifier types.
246 :param separator: Optional specifier. Defaults to ':'.
247 :return: tuple
248 """
249
250=== modified file 'src/maasserver/websockets/protocol.py'
251--- src/maasserver/websockets/protocol.py 2016-03-28 13:54:47 +0000
252+++ src/maasserver/websockets/protocol.py 2016-09-07 21:49:08 +0000
253@@ -24,7 +24,6 @@
254 )
255 from django.contrib.auth.models import User
256 from django.core.exceptions import ValidationError
257-from django.utils.importlib import import_module
258 from maasserver.eventloop import services
259 from maasserver.utils.orm import transactional
260 from maasserver.utils.threads import deferToDatabase
261@@ -44,6 +43,7 @@
262 Protocol,
263 )
264 from twisted.python import log
265+from twisted.python.modules import getModule
266 from twisted.web.server import NOT_DONE_YET
267
268
269@@ -376,7 +376,7 @@
270
271 Used by the protocol to validate the sessionid.
272 """
273- return import_module(settings.SESSION_ENGINE)
274+ return getModule(settings.SESSION_ENGINE).load()
275
276 def cacheHandlers(self):
277 """Cache all the websocket handlers."""
278
279=== modified file 'src/maasserver/websockets/tests/test_protocol.py'
280--- src/maasserver/websockets/tests/test_protocol.py 2016-09-02 20:31:39 +0000
281+++ src/maasserver/websockets/tests/test_protocol.py 2016-09-07 21:49:08 +0000
282@@ -737,12 +737,17 @@
283 self.assertItemsEqual(ALL_HANDLERS, factory.handlers.keys())
284
285 def test_get_SessionEngine_calls_import_module_with_SESSION_ENGINE(self):
286- mock_import = self.patch_autospec(protocol_module, "import_module")
287+ getModule = self.patch_autospec(protocol_module, "getModule")
288 factory = self.make_factory()
289 factory.getSessionEngine()
290- self.assertThat(
291- mock_import,
292- MockCalledOnceWith(protocol_module.settings.SESSION_ENGINE))
293+ # A reference to the module was obtained via getModule.
294+ self.assertThat(
295+ getModule, MockCalledOnceWith(
296+ protocol_module.settings.SESSION_ENGINE))
297+ # It was then loaded via that reference.
298+ self.assertThat(
299+ getModule.return_value.load,
300+ MockCalledOnceWith())
301
302 def test_getHandler_returns_None_on_missing_handler(self):
303 factory = self.make_factory()
304
305=== modified file 'src/metadataserver/fields.py'
306--- src/metadataserver/fields.py 2016-07-30 01:17:54 +0000
307+++ src/metadataserver/fields.py 2016-09-07 21:49:08 +0000
308@@ -13,10 +13,8 @@
309 )
310
311 from django.db import connection
312-from django.db.models import (
313- Field,
314- SubfieldBase,
315-)
316+from django.db.models import SubfieldBase
317+from maasserver.fields import Field
318
319
320 class Bin(bytes):
321
322=== modified file 'src/metadataserver/tests/test_api.py'
323--- src/metadataserver/tests/test_api.py 2016-08-31 14:00:09 +0000
324+++ src/metadataserver/tests/test_api.py 2016-09-07 21:49:08 +0000
325@@ -226,7 +226,9 @@
326 return MAASSensibleOAuthClient(get_node_init_user(), token)
327
328
329-def call_signal(client=None, version='latest', files={}, headers={}, **kwargs):
330+def call_signal(
331+ client=None, version='latest', files: dict=None, headers: dict=None,
332+ **kwargs):
333 """Call the API's signal method.
334
335 :param client: Optional client to POST with. If omitted, will create
336@@ -237,6 +239,10 @@
337 :param **kwargs: Any other keyword parameters are passed on directly
338 to the "signal" call.
339 """
340+ if files is None:
341+ files = {}
342+ if headers is None:
343+ headers = {}
344 if client is None:
345 client = make_node_client(factory.make_Node(
346 status=NODE_STATUS.COMMISSIONING))
347
348=== modified file 'src/provisioningserver/cluster_config_command.py'
349--- src/provisioningserver/cluster_config_command.py 2015-12-10 09:52:21 +0000
350+++ src/provisioningserver/cluster_config_command.py 2016-09-07 21:49:08 +0000
351@@ -1,4 +1,4 @@
352-# Copyright 2015 Canonical Ltd. This software is licensed under the
353+# Copyright 2015-2016 Canonical Ltd. This software is licensed under the
354 # GNU Affero General Public License version 3 (see the file LICENSE).
355
356 """Change cluster controller configuration settings.
357@@ -68,8 +68,8 @@
358 uuid_group = parser.add_mutually_exclusive_group()
359 uuid_group.add_argument(
360 '--uuid', action='store', required=False,
361- help=('Change the cluster UUID. Pass AUTO to generate a new UUID if '
362- 'one is not already set.'))
363+ help=('Change the cluster UUID. Use --init instead to generate a '
364+ 'new UUID if one is not already set.'))
365 uuid_group.add_argument(
366 '--init', action='store_true', required=False,
367 help=('Generate a new UUID for this cluster controller.'))
368
369=== modified file 'src/provisioningserver/dns/tests/test_zoneconfig.py'
370--- src/provisioningserver/dns/tests/test_zoneconfig.py 2016-08-22 15:26:25 +0000
371+++ src/provisioningserver/dns/tests/test_zoneconfig.py 2016-09-07 21:49:08 +0000
372@@ -38,7 +38,7 @@
373 class HostnameIPMapping:
374 """This is used to return address information for a host in a way that
375 keeps life simple for the callers."""
376- def __init__(self, system_id=None, ttl=None, ips=set()):
377+ def __init__(self, system_id=None, ttl=None, ips=frozenset()):
378 self.system_id = system_id
379 self.ttl = ttl
380 self.ips = ips.copy()
381@@ -55,7 +55,7 @@
382 """This is used to return non-address information for a hostname in a way
383 that keeps life simple for the allers. Rrset is a set of (ttl, rrtype,
384 rrdata) tuples."""
385- def __init__(self, system_id=None, rrset=set()):
386+ def __init__(self, system_id=None, rrset=frozenset()):
387 self.system_id = system_id
388 self.rrset = rrset.copy()
389
390
391=== modified file 'src/provisioningserver/rackdservices/tests/test_ntp.py'
392--- src/provisioningserver/rackdservices/tests/test_ntp.py 2016-08-19 09:38:08 +0000
393+++ src/provisioningserver/rackdservices/tests/test_ntp.py 2016-09-07 21:49:08 +0000
394@@ -47,12 +47,13 @@
395 @attr.s
396 class FakeConnectionToRegionForGetControllerType(FakeConnectionToRegion):
397
398- controller_type = attr.ib(default={"is_region": False, "is_rack": False})
399+ controller_type = attr.ib(
400+ default=(("is_region", False), ("is_rack", False)))
401
402 def callRemote(self, cmd, system_id):
403 assert cmd is region.GetControllerType, (
404 "cmd must be GetControllerType, got: %r" % (cmd,))
405- return succeed(self.controller_type)
406+ return succeed(dict(self.controller_type))
407
408
409 @attr.s
410@@ -60,7 +61,8 @@
411 """A stub `ClusterClientService` service."""
412
413 addresses = attr.ib(default=frozenset(), convert=frozenset)
414- controller_type = attr.ib(default={"is_region": False, "is_rack": False})
415+ controller_type = attr.ib(
416+ default=(("is_region", False), ("is_rack", False)))
417
418 def getAllClients(self):
419 return [
420
421=== modified file 'src/provisioningserver/refresh/__init__.py'
422--- src/provisioningserver/refresh/__init__.py 2016-06-14 18:01:02 +0000
423+++ src/provisioningserver/refresh/__init__.py 2016-09-07 21:49:08 +0000
424@@ -78,7 +78,7 @@
425
426
427 def signal(
428- url, creds, status, message, files={}, script_result=None,
429+ url, creds, status, message, files: dict=None, script_result=None,
430 extra_headers=None):
431 """Send a node signal to a given maas_url."""
432 if isinstance(status, int):
433@@ -93,7 +93,8 @@
434 script_result = str(script_result)
435 params[b'script_result'] = script_result.encode("utf-8")
436
437- data, headers = encode_multipart_data(params, files)
438+ data, headers = encode_multipart_data(
439+ params, ({} if files is None else files))
440
441 if extra_headers is not None:
442 headers.update(extra_headers)
443
444=== modified file 'src/provisioningserver/utils/services.py'
445--- src/provisioningserver/utils/services.py 2016-08-31 17:59:55 +0000
446+++ src/provisioningserver/utils/services.py 2016-09-07 21:49:08 +0000
447@@ -139,6 +139,8 @@
448 env = select_c_utf8_bytes_locale()
449 log.msg("%s started." % self.description)
450 args = self.getProcessParameters()
451+ assert all(isinstance(arg, bytes) for arg in args), (
452+ "Process arguments must all be bytes, got: %s" % repr(args))
453 self.process = reactor.spawnProcess(
454 self.protocol, args[0], args, env=env)
455 return self.protocol.done.addErrback(
456@@ -205,7 +207,7 @@
457
458 # The last successfully recorded interfaces.
459 _recorded = None
460- _monitored = set()
461+ _monitored = frozenset()
462
463 # Use a named filesystem lock to prevent more than one monitoring service
464 # running on each host machine. This service attempts to acquire this lock
465
466=== modified file 'src/provisioningserver/utils/tests/test_services.py'
467--- src/provisioningserver/utils/tests/test_services.py 2016-08-31 17:59:55 +0000
468+++ src/provisioningserver/utils/tests/test_services.py 2016-09-07 21:49:08 +0000
469@@ -384,25 +384,25 @@
470 class TrueProcessProtocolService(ProcessProtocolService):
471
472 def getProcessParameters(self):
473- return ["/bin/true"]
474+ return [b"/bin/true"]
475
476
477 class FalseProcessProtocolService(ProcessProtocolService):
478
479 def getProcessParameters(self):
480- return ["/bin/false"]
481+ return [b"/bin/false"]
482
483
484 class CatProcessProtocolService(ProcessProtocolService):
485
486 def getProcessParameters(self):
487- return ["/bin/cat"]
488+ return [b"/bin/cat"]
489
490
491 class EchoProcessProtocolService(ProcessProtocolService):
492
493 def getProcessParameters(self):
494- return ["/bin/echo", "{}\n"]
495+ return [b"/bin/echo", b"{}\n"]
496
497
498 class MockJSONProtocol(JSONPerLineProtocol):
499
500=== modified file 'src/provisioningserver/utils/znums.py'
501--- src/provisioningserver/utils/znums.py 2016-03-28 13:54:47 +0000
502+++ src/provisioningserver/utils/znums.py 2016-09-07 21:49:08 +0000
503@@ -19,19 +19,19 @@
504 znums = dict(zip(zchars, count(0)))
505
506
507-def from_int(num, chars=zchars):
508- parts, div = [], len(chars)
509+def from_int(num, _chars=zchars):
510+ parts, div = [], len(_chars)
511 while num > 0:
512 num, rem = divmod(num, div)
513- parts.append(chars[rem])
514+ parts.append(_chars[rem])
515 if len(parts) == 0:
516- return chars[0]
517+ return _chars[0]
518 else:
519 return "".join(parts[::-1])
520
521
522-def to_int(zs, nums=znums):
523- total, div = 0, len(nums)
524+def to_int(zs, _nums=znums):
525+ total, div = 0, len(_nums)
526 for char, exp in zip(zs[::-1], count(0)):
527- total += nums[char] * (div ** exp)
528+ total += _nums[char] * (div ** exp)
529 return total