Merge lp:~adeuring/launchpad/hwdb-refactor-haldevice-3 into lp:launchpad

Proposed by Abel Deuring
Status: Merged
Approved by: Barry Warsaw
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~adeuring/launchpad/hwdb-refactor-haldevice-3
Merge into: lp:launchpad
Diff against target: 604 lines
2 files modified
lib/canonical/launchpad/scripts/hwdbsubmissions.py (+258/-209)
lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py (+54/-0)
To merge this branch: bzr merge lp:~adeuring/launchpad/hwdb-refactor-haldevice-3
Reviewer Review Type Date Requested Status
Barry Warsaw (community) Approve
Review via email: mp+12733@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Abel Deuring (adeuring) wrote :

This branch is the third and last part to refactor class
l.c.l.scripts.hwdbsubmissions.HALDevice. See
https://code.edge.launchpad.net/~adeuring/launchpad/hwdb-refactor-haldevice/+merge/12669
for the reason of the refactoring.

The branch adds new properties scsi_vendor, scsi_model and driver_name
to BaseDevice and HALDevice (including short tests), and it defines some
"dummy properties" in class BaseDevice that correspond to exsting
properties in class HALDevice.

Finally, it moves methods that can be used both for class HALDevice and
the not-yet-existing class UdevDevice into class BaseDevice.

test: ./bin/test -t test_hwdb_submission_processing

= Launchpad lint =

Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.

Linting changed files:
  lib/canonical/launchpad/scripts/hwdbsubmissions.py
  lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py

== Pyflakes notices ==

lib/canonical/launchpad/scripts/hwdbsubmissions.py
    22: redefinition of unused 'etree' from line 20

== Pylint notices ==

lib/canonical/launchpad/scripts/hwdbsubmissions.py
    20: [F0401] Unable to import 'xml.etree.cElementTree' (No module
named etree)

A complaint caused by an unrelated branch that prepares Launchpad for
Python 2.5

Revision history for this message
Barry Warsaw (barry) wrote :

<barry> adeuring: when you raise NotImplementedError in your properties, you
        don't need to instantiate them if they have no arguments. Python will
        DTRT for you here, and it's also somewhat more efficient [11:27]

with that fix, r=me

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/canonical/launchpad/scripts/hwdbsubmissions.py'
2--- lib/canonical/launchpad/scripts/hwdbsubmissions.py 2009-10-01 11:14:19 +0000
3+++ lib/canonical/launchpad/scripts/hwdbsubmissions.py 2009-10-02 15:43:14 +0000
4@@ -1281,28 +1281,94 @@
5 @property
6 def device_id(self):
7 """A unique ID for this device."""
8- raise NotImplementedError()
9+ raise NotImplementedError
10
11 @property
12 def pci_class(self):
13 """The PCI device class of the device or None for Non-PCI devices."""
14- raise NotImplementedError()
15+ raise NotImplementedError
16
17 @property
18 def pci_subclass(self):
19 """The PCI device sub-class of the device or None for Non-PCI devices.
20 """
21- raise NotImplementedError()
22+ raise NotImplementedError
23
24 @property
25 def usb_vendor_id(self):
26 """The USB vendor ID of the device or None for Non-USB devices."""
27- raise NotImplementedError()
28+ raise NotImplementedError
29
30 @property
31 def usb_product_id(self):
32 """The USB product ID of the device or None for Non-USB devices."""
33- raise NotImplementedError()
34+ raise NotImplementedError
35+
36+ @property
37+ def scsi_vendor(self):
38+ """The SCSI vendor name of the device or None for Non-SCSI devices."""
39+ raise NotImplementedError
40+
41+ @property
42+ def scsi_model(self):
43+ """The SCSI model name of the device or None for Non-SCSI devices."""
44+ raise NotImplementedError
45+
46+ @property
47+ def vendor(self):
48+ """The vendor of this device."""
49+ raise NotImplementedError
50+
51+ @property
52+ def product(self):
53+ """The vendor of this device."""
54+ raise NotImplementedError
55+
56+ @property
57+ def vendor_id(self):
58+ """The vendor ID of this device."""
59+ raise NotImplementedError
60+
61+ @property
62+ def product_id(self):
63+ """The product ID of this device."""
64+ raise NotImplementedError
65+
66+ @property
67+ def vendor_id_for_db(self):
68+ """The vendor ID in the representation needed for the HWDB tables.
69+
70+ USB and PCI IDs are represented in the database in hexadecimal,
71+ while the IDs provided by HAL are integers.
72+
73+ The SCSI vendor name is right-padded with spaces to 8 bytes.
74+ """
75+ bus = self.raw_bus
76+ format = DB_FORMAT_FOR_VENDOR_ID.get(bus)
77+ if format is None:
78+ return self.vendor_id
79+ else:
80+ return format % self.vendor_id
81+
82+ @property
83+ def product_id_for_db(self):
84+ """The product ID in the representation needed for the HWDB tables.
85+
86+ USB and PCI IDs are represented in the database in hexadecimal,
87+ while the IDs provided by HAL are integers.
88+
89+ The SCSI product name is right-padded with spaces to 16 bytes.
90+ """
91+ bus = self.raw_bus
92+ format = DB_FORMAT_FOR_PRODUCT_ID.get(bus)
93+ if format is None:
94+ return self.product_id
95+ else:
96+ return format % self.product_id
97+
98+ @property
99+ def driver_name(self):
100+ """The name of the driver contolling this device. May be None."""
101
102 def translateScsiBus(self):
103 """Return the real bus of a device where raw_bus=='scsi'.
104@@ -1403,7 +1469,7 @@
105 @property
106 def raw_bus(self):
107 """Return the device bus as specified by HAL or udev."""
108- raise NotImplementedError()
109+ raise NotImplementedError
110
111 @property
112 def real_bus(self):
113@@ -1734,88 +1800,6 @@
114 return False
115 return True
116
117-
118-class HALDevice(BaseDevice):
119- """The representation of a HAL device node."""
120-
121- def __init__(self, id, udi, properties, parser):
122- """HALDevice constructor.
123-
124- :param id: The ID of the HAL device in the submission data as
125- specified in <device id=...>.
126- :type id: int
127- :param udi: The UDI of the HAL device.
128- :type udi: string
129- :param properties: The HAL properties of the device.
130- :type properties: dict
131- :param parser: The parser processing a submission.
132- :type parser: SubmissionParser
133- """
134- super(HALDevice, self).__init__(parser)
135- self.id = id
136- self.udi = udi
137- self.properties = properties
138-
139- def getProperty(self, property_name):
140- """Return the HAL property property_name.
141-
142- Note that there is no check of the property type.
143- """
144- if property_name not in self.properties:
145- return None
146- name, type_ = self.properties[property_name]
147- return name
148-
149- @property
150- def parent_udi(self):
151- """The UDI of the parent device."""
152- return self.getProperty('info.parent')
153-
154- @property
155- def device_id(self):
156- """See `BaseDevice`."""
157- return self.udi
158-
159- @property
160- def pci_class(self):
161- """See `BaseDevice`."""
162- return self.getProperty('pci.device_class')
163-
164- @property
165- def pci_subclass(self):
166- """The PCI device sub-class of the device or None for Non-PCI devices.
167- """
168- return self.getProperty('pci.device_subclass')
169-
170- @property
171- def usb_vendor_id(self):
172- """See `BaseDevice`."""
173- return self.getProperty('usb_device.vendor_id')
174-
175- @property
176- def usb_product_id(self):
177- """See `BaseDevice`."""
178- return self.getProperty('usb_device.product_id')
179-
180- @property
181- def raw_bus(self):
182- """See `BaseDevice`."""
183- # Older versions of HAL stored this value in the property
184- # info.bus; newer versions store it in info.subsystem.
185- #
186- # Note that info.bus is gone for all devices except the
187- # USB bus. For USB devices, the property info.bus returns more
188- # detailed data: info.subsystem has the value 'usb' for all
189- # HAL nodes belonging to USB devices, while info.bus has the
190- # value 'usb_device' for the root node of a USB device, and the
191- # value 'usb' for sub-nodes of a USB device. We use these
192- # different value to to find the root USB device node, hence
193- # try to read info.bus first.
194- result = self.getProperty('info.bus')
195- if result is not None:
196- return result
197- return self.getProperty('info.subsystem')
198-
199 def getScsiVendorAndModelName(self):
200 """Separate vendor and model name of SCSI decvices.
201
202@@ -1839,132 +1823,17 @@
203
204 In all other cases, vendor and model name are returned unmodified.
205 """
206- vendor = self.getProperty('scsi.vendor')
207+ vendor = self.scsi_vendor
208 if vendor == 'ATA':
209 # The assumption below that the vendor name does not
210 # contain any spaces is not necessarily correct, but
211 # it is hard to find a better heuristic to separate
212 # the vendor name from the product name.
213- splitted_name = self.getProperty('scsi.model').split(' ', 1)
214+ splitted_name = self.scsi_model.split(' ', 1)
215 if len(splitted_name) < 2:
216 return 'ATA', splitted_name[0]
217 return splitted_name
218- return (vendor, self.getProperty('scsi.model'))
219-
220- def getVendorOrProduct(self, type_):
221- """Return the vendor or product of this device.
222-
223- :return: The vendor or product data for this device.
224- :param type_: 'vendor' or 'product'
225- """
226- # HAL does not store vendor data very consistently. Try to find
227- # the data in several places.
228- assert type_ in ('vendor', 'product'), (
229- 'Unexpected value of type_: %r' % type_)
230-
231- bus = self.raw_bus
232- if self.udi == ROOT_UDI:
233- # HAL sets info.product to "Computer", provides no property
234- # info.vendor and raw_bus is "unknown", hence the logic
235- # below does not work properly.
236- return self.getProperty('system.hardware.' + type_)
237- elif bus == 'scsi':
238- vendor, product = self.getScsiVendorAndModelName()
239- if type_ == 'vendor':
240- return vendor
241- else:
242- return product
243- else:
244- result = self.getProperty('info.' + type_)
245- if result is None:
246- if bus is None:
247- return None
248- else:
249- return self.getProperty('%s.%s' % (bus, type_))
250- else:
251- return result
252-
253- @property
254- def vendor(self):
255- """The vendor of this device."""
256- return self.getVendorOrProduct('vendor')
257-
258- @property
259- def product(self):
260- """The vendor of this device."""
261- return self.getVendorOrProduct('product')
262-
263- def getVendorOrProductID(self, type_):
264- """Return the vendor or product ID for this device.
265-
266- :return: The vendor or product ID for this device.
267- :param type_: 'vendor' or 'product'
268- """
269- assert type_ in ('vendor', 'product'), (
270- 'Unexpected value of type_: %r' % type_)
271- bus = self.raw_bus
272- if self.udi == ROOT_UDI:
273- # HAL does not provide IDs for a system itself, we use the
274- # vendor resp. product name instead.
275- return self.getVendorOrProduct(type_)
276- elif bus is None:
277- return None
278- elif bus == 'scsi' or self.udi == ROOT_UDI:
279- # The SCSI specification does not distinguish between a
280- # vendor/model ID and vendor/model name: the SCSI INQUIRY
281- # command returns an 8 byte string as the vendor name and
282- # a 16 byte string as the model name. We use these strings
283- # as the vendor/product name as well as the vendor/product
284- # ID.
285- #
286- # Similary, HAL does not provide a vendor or product ID
287- # for the host system itself, so we use the vendor resp.
288- # product name as the vendor/product ID for systems too.
289- return self.getVendorOrProduct(type_)
290- else:
291- return self.getProperty('%s.%s_id' % (bus, type_))
292-
293- @property
294- def vendor_id(self):
295- """The vendor ID of this device."""
296- return self.getVendorOrProductID('vendor')
297-
298- @property
299- def product_id(self):
300- """The product ID of this device."""
301- return self.getVendorOrProductID('product')
302-
303- @property
304- def vendor_id_for_db(self):
305- """The vendor ID in the representation needed for the HWDB tables.
306-
307- USB and PCI IDs are represented in the database in hexadecimal,
308- while the IDs provided by HAL are integers.
309-
310- The SCSI vendor name is right-padded with spaces to 8 bytes.
311- """
312- bus = self.raw_bus
313- format = DB_FORMAT_FOR_VENDOR_ID.get(bus)
314- if format is None:
315- return self.vendor_id
316- else:
317- return format % self.vendor_id
318-
319- @property
320- def product_id_for_db(self):
321- """The product ID in the representation needed for the HWDB tables.
322-
323- USB and PCI IDs are represented in the database in hexadecimal,
324- while the IDs provided by HAL are integers.
325-
326- The SCSI product name is right-padded with spaces to 16 bytes.
327- """
328- bus = self.raw_bus
329- format = DB_FORMAT_FOR_PRODUCT_ID.get(bus)
330- if format is None:
331- return self.product_id
332- else:
333- return format % self.product_id
334+ return (vendor, self.scsi_model)
335
336 def getDriver(self):
337 """Return the HWDriver instance associated with this device.
338@@ -1974,11 +1843,11 @@
339 # HAL and the HWDB client know at present only about kernel
340 # drivers, so there is currently no need to search for
341 # for user space printer drivers, for example.
342- driver_name = self.getProperty('info.linux.driver')
343- if driver_name is not None:
344+ if self.driver_name is not None:
345 kernel_package_name = self.parser.getKernelPackageName()
346 db_driver_set = getUtility(IHWDriverSet)
347- return db_driver_set.getOrCreate(kernel_package_name, driver_name)
348+ return db_driver_set.getOrCreate(
349+ kernel_package_name, self.driver_name)
350 else:
351 return None
352
353@@ -2069,6 +1938,186 @@
354 submission_device)
355
356
357+class HALDevice(BaseDevice):
358+ """The representation of a HAL device node."""
359+
360+ def __init__(self, id, udi, properties, parser):
361+ """HALDevice constructor.
362+
363+ :param id: The ID of the HAL device in the submission data as
364+ specified in <device id=...>.
365+ :type id: int
366+ :param udi: The UDI of the HAL device.
367+ :type udi: string
368+ :param properties: The HAL properties of the device.
369+ :type properties: dict
370+ :param parser: The parser processing a submission.
371+ :type parser: SubmissionParser
372+ """
373+ super(HALDevice, self).__init__(parser)
374+ self.id = id
375+ self.udi = udi
376+ self.properties = properties
377+
378+ def getProperty(self, property_name):
379+ """Return the HAL property property_name.
380+
381+ Note that there is no check of the property type.
382+ """
383+ if property_name not in self.properties:
384+ return None
385+ name, type_ = self.properties[property_name]
386+ return name
387+
388+ @property
389+ def parent_udi(self):
390+ """The UDI of the parent device."""
391+ return self.getProperty('info.parent')
392+
393+ @property
394+ def device_id(self):
395+ """See `BaseDevice`."""
396+ return self.udi
397+
398+ @property
399+ def pci_class(self):
400+ """See `BaseDevice`."""
401+ return self.getProperty('pci.device_class')
402+
403+ @property
404+ def pci_subclass(self):
405+ """The PCI device sub-class of the device or None for Non-PCI devices.
406+ """
407+ return self.getProperty('pci.device_subclass')
408+
409+ @property
410+ def usb_vendor_id(self):
411+ """See `BaseDevice`."""
412+ return self.getProperty('usb_device.vendor_id')
413+
414+ @property
415+ def usb_product_id(self):
416+ """See `BaseDevice`."""
417+ return self.getProperty('usb_device.product_id')
418+
419+ @property
420+ def scsi_vendor(self):
421+ """See `BaseDevice`."""
422+ return self.getProperty('scsi.vendor')
423+
424+ @property
425+ def scsi_model(self):
426+ """See `BaseDevice`."""
427+ return self.getProperty('scsi.model')
428+
429+ @property
430+ def driver_name(self):
431+ """See `BaseDevice`."""
432+ return self.getProperty('info.linux.driver')
433+
434+ @property
435+ def raw_bus(self):
436+ """See `BaseDevice`."""
437+ # Older versions of HAL stored this value in the property
438+ # info.bus; newer versions store it in info.subsystem.
439+ #
440+ # Note that info.bus is gone for all devices except the
441+ # USB bus. For USB devices, the property info.bus returns more
442+ # detailed data: info.subsystem has the value 'usb' for all
443+ # HAL nodes belonging to USB devices, while info.bus has the
444+ # value 'usb_device' for the root node of a USB device, and the
445+ # value 'usb' for sub-nodes of a USB device. We use these
446+ # different value to to find the root USB device node, hence
447+ # try to read info.bus first.
448+ result = self.getProperty('info.bus')
449+ if result is not None:
450+ return result
451+ return self.getProperty('info.subsystem')
452+
453+ def getVendorOrProduct(self, type_):
454+ """Return the vendor or product of this device.
455+
456+ :return: The vendor or product data for this device.
457+ :param type_: 'vendor' or 'product'
458+ """
459+ # HAL does not store vendor data very consistently. Try to find
460+ # the data in several places.
461+ assert type_ in ('vendor', 'product'), (
462+ 'Unexpected value of type_: %r' % type_)
463+
464+ bus = self.raw_bus
465+ if self.udi == ROOT_UDI:
466+ # HAL sets info.product to "Computer", provides no property
467+ # info.vendor and raw_bus is "unknown", hence the logic
468+ # below does not work properly.
469+ return self.getProperty('system.hardware.' + type_)
470+ elif bus == 'scsi':
471+ vendor, product = self.getScsiVendorAndModelName()
472+ if type_ == 'vendor':
473+ return vendor
474+ else:
475+ return product
476+ else:
477+ result = self.getProperty('info.' + type_)
478+ if result is None:
479+ if bus is None:
480+ return None
481+ else:
482+ return self.getProperty('%s.%s' % (bus, type_))
483+ else:
484+ return result
485+
486+ @property
487+ def vendor(self):
488+ """See `BaseDevice`."""
489+ return self.getVendorOrProduct('vendor')
490+
491+ @property
492+ def product(self):
493+ """See `BaseDevice`."""
494+ return self.getVendorOrProduct('product')
495+
496+ def getVendorOrProductID(self, type_):
497+ """Return the vendor or product ID for this device.
498+
499+ :return: The vendor or product ID for this device.
500+ :param type_: 'vendor' or 'product'
501+ """
502+ assert type_ in ('vendor', 'product'), (
503+ 'Unexpected value of type_: %r' % type_)
504+ bus = self.raw_bus
505+ if self.udi == ROOT_UDI:
506+ # HAL does not provide IDs for a system itself, we use the
507+ # vendor resp. product name instead.
508+ return self.getVendorOrProduct(type_)
509+ elif bus is None:
510+ return None
511+ elif bus == 'scsi' or self.udi == ROOT_UDI:
512+ # The SCSI specification does not distinguish between a
513+ # vendor/model ID and vendor/model name: the SCSI INQUIRY
514+ # command returns an 8 byte string as the vendor name and
515+ # a 16 byte string as the model name. We use these strings
516+ # as the vendor/product name as well as the vendor/product
517+ # ID.
518+ #
519+ # Similary, HAL does not provide a vendor or product ID
520+ # for the host system itself, so we use the vendor resp.
521+ # product name as the vendor/product ID for systems too.
522+ return self.getVendorOrProduct(type_)
523+ else:
524+ return self.getProperty('%s.%s_id' % (bus, type_))
525+
526+ @property
527+ def vendor_id(self):
528+ """See `BaseDevice`."""
529+ return self.getVendorOrProductID('vendor')
530+
531+ @property
532+ def product_id(self):
533+ """See `BaseDevice`."""
534+ return self.getVendorOrProductID('product')
535+
536+
537 class ProcessingLoop(object):
538 """An `ITunableLoop` for processing HWDB submissions."""
539
540
541=== modified file 'lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py'
542--- lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py 2009-10-01 11:14:19 +0000
543+++ lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py 2009-10-02 15:43:14 +0000
544@@ -462,6 +462,60 @@
545 'Unexpected value of HALDevice.usb_product_id for Non-USB '
546 'device.')
547
548+ def testHALDeviceScsiVendor(self):
549+ """Test of HALDevice.scsi_vendor."""
550+ properties = {
551+ 'scsi.vendor': ('SEAGATE', 'string'),
552+ }
553+ parser = SubmissionParser(self.log)
554+ device = HALDevice(1, '/some/udi/path', properties, parser)
555+ self.assertEqual(
556+ 'SEAGATE', device.scsi_vendor,
557+ 'Unexpected value of HALDevice.scsi_vendor.')
558+
559+ properties = {}
560+ parser = SubmissionParser(self.log)
561+ device = HALDevice(1, '/some/udi/path', properties, parser)
562+ self.assertEqual(
563+ None, device.scsi_vendor,
564+ 'Unexpected value of HALDevice.scsi_vendor for Non-SCSI device.')
565+
566+ def testHALDeviceScsiModel(self):
567+ """Test of HALDevice.scsi_model."""
568+ properties = {
569+ 'scsi.model': ('ST1234567', 'string'),
570+ }
571+ parser = SubmissionParser(self.log)
572+ device = HALDevice(1, '/some/udi/path', properties, parser)
573+ self.assertEqual(
574+ 'ST1234567', device.scsi_model,
575+ 'Unexpected value of HALDevice.scsi_model.')
576+
577+ properties = {}
578+ parser = SubmissionParser(self.log)
579+ device = HALDevice(1, '/some/udi/path', properties, parser)
580+ self.assertEqual(
581+ None, device.scsi_model,
582+ 'Unexpected value of HALDevice.scsi_model for Non-SCSI device.')
583+
584+ def testHALDeviceDriverName(self):
585+ """Test of HALDevice.driver_name."""
586+ properties = {
587+ 'info.linux.driver': ('ahci', 'string'),
588+ }
589+ parser = SubmissionParser(self.log)
590+ device = HALDevice(1, '/some/udi/path', properties, parser)
591+ self.assertEqual(
592+ 'ahci', device.driver_name,
593+ 'Unexpected value of HALDevice.driver_name.')
594+
595+ properties = {}
596+ parser = SubmissionParser(self.log)
597+ device = HALDevice(1, '/some/udi/path', properties, parser)
598+ self.assertEqual(
599+ None, device.driver_name,
600+ 'Unexpected value of HALDevice.driver_name for Non-SCSI device.')
601+
602 def testHalDeviceRawBus(self):
603 """test of HALDevice.raw_bus."""
604 properties = {