@@ -205,14 +205,14 @@ def parse_basic_device_properties(
205
205
properties ['port' ] = expected
206
206
207
207
@staticmethod
208
- def serialize_str (value : str ):
208
+ def serialize_str (value : str ) -> str :
209
209
"""
210
210
Serialize python string to ensure consistency.
211
211
"""
212
212
return "'" + str (value ).replace ("'" , r"\'" ) + "'"
213
213
214
214
@staticmethod
215
- def deserialize_str (value : str ):
215
+ def deserialize_str (value : str ) -> str :
216
216
"""
217
217
Deserialize python string to ensure consistency.
218
218
"""
@@ -255,7 +255,11 @@ class Port:
255
255
port_id (str): A unique (in backend domain) identifier for the port.
256
256
devclass (str): The class of the port (e.g., 'usb', 'pci').
257
257
"""
258
- def __init__ (self , backend_domain , port_id , devclass ):
258
+ def __init__ (self ,
259
+ backend_domain : Optional [QubesVM ],
260
+ port_id : str ,
261
+ devclass : str
262
+ ):
259
263
self .__backend_domain = backend_domain
260
264
self .__port_id = port_id
261
265
self .__devclass = devclass
@@ -288,7 +292,7 @@ def __str__(self):
288
292
@property
289
293
def backend_name (self ) -> str :
290
294
# pylint: disable=missing-function-docstring
291
- if self .backend_domain not in ( None , "*" ) :
295
+ if self .backend_domain is not None :
292
296
return self .backend_domain .name
293
297
return "*"
294
298
@@ -359,12 +363,22 @@ def devclass(self) -> str:
359
363
return self .__devclass
360
364
return "peripheral"
361
365
362
-
363
366
@property
364
367
def has_devclass (self ):
365
368
return self .__devclass is not None
366
369
367
370
371
+ class AnyPort (Port ):
372
+ def __init__ (self , devclass : str ):
373
+ super ().__init__ (None , "*" , devclass )
374
+
375
+ def __repr__ (self ):
376
+ return "*"
377
+
378
+ def __str__ (self ):
379
+ return "*"
380
+
381
+
368
382
class VirtualDevice :
369
383
"""
370
384
Class of a device connected to *port*.
@@ -378,7 +392,7 @@ def __init__(
378
392
port : Optional [Port ] = None ,
379
393
device_id : Optional [str ] = None ,
380
394
):
381
- assert port is not None or device_id is not None
395
+ assert not isinstance ( port , AnyPort ) or device_id is not None
382
396
self .port : Optional [Port ] = port
383
397
self ._device_id = device_id
384
398
@@ -394,19 +408,26 @@ def clone(self, **kwargs) -> 'VirtualDevice':
394
408
return VirtualDevice (** attr )
395
409
396
410
@property
397
- def port (self ) -> Union [ Port , str ] :
411
+ def port (self ) -> Port :
398
412
# pylint: disable=missing-function-docstring
399
413
return self ._port
400
414
401
415
@port .setter
402
416
def port (self , value : Union [Port , str , None ]):
403
417
# pylint: disable=missing-function-docstring
404
- self ._port = value if value is not None else '*'
418
+ if isinstance (value , Port ):
419
+ self ._port = value
420
+ return
421
+ if isinstance (value , str ) and value != '*' :
422
+ raise ValueError ("Unsupported value for port" )
423
+ if self .device_id == '*' :
424
+ raise ValueError ("Cannot set port to '*' if device_is is '*'" )
425
+ self ._port = AnyPort (self .devclass )
405
426
406
427
@property
407
428
def device_id (self ) -> str :
408
429
# pylint: disable=missing-function-docstring
409
- if self ._device_id is not None :
430
+ if self .is_device_id_set :
410
431
return self ._device_id
411
432
return '*'
412
433
@@ -418,34 +439,26 @@ def is_device_id_set(self) -> bool:
418
439
return self ._device_id is not None
419
440
420
441
@property
421
- def backend_domain (self ) -> Union [QubesVM , str ]:
442
+ def backend_domain (self ) -> Optional [QubesVM ]:
422
443
# pylint: disable=missing-function-docstring
423
- if self .port != '*' and self .port .backend_domain is not None :
424
- return self .port .backend_domain
425
- return '*'
444
+ return self .port .backend_domain
426
445
427
446
@property
428
447
def backend_name (self ) -> str :
429
448
"""
430
449
Return backend domain name if any or `*`.
431
450
"""
432
- if self .port != '*' :
433
- return self .port .backend_name
434
- return '*'
451
+ return self .port .backend_name
435
452
436
453
@property
437
454
def port_id (self ) -> str :
438
455
# pylint: disable=missing-function-docstring
439
- if self .port != '*' and self .port .port_id is not None :
440
- return self .port .port_id
441
- return '*'
456
+ return self .port .port_id
442
457
443
458
@property
444
459
def devclass (self ) -> str :
445
460
# pylint: disable=missing-function-docstring
446
- if self .port != '*' and self .port .devclass is not None :
447
- return self .port .devclass
448
- return '*'
461
+ return self .port .devclass
449
462
450
463
@property
451
464
def description (self ) -> str :
@@ -483,9 +496,11 @@ def __lt__(self, other):
483
496
4. *:*
484
497
"""
485
498
if isinstance (other , (VirtualDevice , DeviceAssignment )):
486
- if self .port == '*' and other .port != '*' :
499
+ if (isinstance (self .port , AnyPort )
500
+ and not isinstance (other .port , AnyPort )):
487
501
return True
488
- if self .port != '*' and other .port == '*' :
502
+ if (not isinstance (self .port , AnyPort )
503
+ and isinstance (other .port , AnyPort )):
489
504
return False
490
505
reprs = {self : [self .port ], other : [other .port ]}
491
506
for obj , obj_repr in reprs .items ():
@@ -509,10 +524,10 @@ def __str__(self):
509
524
def from_qarg (
510
525
cls ,
511
526
representation : str ,
512
- devclass ,
527
+ devclass : Optional [ str ] ,
513
528
domains ,
514
- blind = False ,
515
- backend = None ,
529
+ blind : bool = False ,
530
+ backend : Optional [ QubesVM ] = None ,
516
531
) -> 'VirtualDevice' :
517
532
"""
518
533
Parse qrexec argument <back_vm>+<port_id>:<device_id> to get device info
@@ -528,8 +543,12 @@ def from_qarg(
528
543
529
544
@classmethod
530
545
def from_str (
531
- cls , representation : str , devclass : Optional [str ], domains ,
532
- blind = False , backend = None
546
+ cls ,
547
+ representation : str ,
548
+ devclass : Optional [str ],
549
+ domains ,
550
+ blind : bool = False ,
551
+ backend : Optional [QubesVM ] = None ,
533
552
) -> 'VirtualDevice' :
534
553
"""
535
554
Parse string <back_vm>+<port_id>:<device_id> to get device info
@@ -549,7 +568,7 @@ def _parse(
549
568
representation : str ,
550
569
devclass : Optional [str ],
551
570
get_domain : Callable ,
552
- backend ,
571
+ backend : Optional [ QubesVM ] ,
553
572
sep : str
554
573
) -> 'VirtualDevice' :
555
574
"""
@@ -942,7 +961,8 @@ def subdevices(self) -> List[VirtualDevice]:
942
961
If the device has subdevices (e.g., partitions of a USB stick),
943
962
the subdevices id should be here.
944
963
"""
945
- return [dev for dev in self .backend_domain .devices [self .devclass ]
964
+ return [dev for devclass in self .backend_domain .devices .keys ()
965
+ for dev in self .backend_domain .devices [devclass ]
946
966
if dev .parent_device .port .port_id == self .port_id ]
947
967
948
968
@property
@@ -956,7 +976,7 @@ def serialize(self) -> bytes:
956
976
"""
957
977
Serialize an object to be transmitted via Qubes API.
958
978
"""
959
- properties = VirtualDevice .serialize (self )
979
+ properties = super () .serialize ()
960
980
# 'attachment', 'interfaces', 'data', 'parent_device'
961
981
# are not string, so they need special treatment
962
982
default = DeviceInfo (self .port )
0 commit comments