35
35
import sys
36
36
from enum import Enum
37
37
from typing import Optional , Dict , Any , List , Union , Tuple , Callable
38
+ from typing import TYPE_CHECKING
38
39
39
40
import qubes .utils
40
-
41
41
from qubes .exc import ProtocolError
42
42
43
- QubesVM = "qubes.vm.BaseVM"
43
+ if TYPE_CHECKING :
44
+ from qubes .vm .qubesvm import QubesVM
45
+ else :
46
+ QubesVM = "qubes.vm.qubesvm.QubesVM"
44
47
45
48
46
49
class UnexpectedDeviceProperty (qubes .exc .QubesException , ValueError ):
@@ -82,8 +85,8 @@ def unpack_properties(
82
85
"ascii" , errors = "strict"
83
86
).strip ()
84
87
85
- properties = {}
86
- options = {}
88
+ properties : Dict [ str , str ] = {}
89
+ options : Dict [ str , str ] = {}
87
90
88
91
if not ut_decoded :
89
92
return properties , options
@@ -221,7 +224,7 @@ def deserialize_str(value: str) -> str:
221
224
def sanitize_str (
222
225
untrusted_value : str ,
223
226
allowed_chars : set ,
224
- replace_char : str = None ,
227
+ replace_char : Optional [ str ] = None ,
225
228
error_message : str = "" ,
226
229
) -> str :
227
230
"""
@@ -256,7 +259,10 @@ class Port:
256
259
"""
257
260
258
261
def __init__ (
259
- self , backend_domain : Optional [QubesVM ], port_id : str , devclass : str
262
+ self ,
263
+ backend_domain : Optional [QubesVM ],
264
+ port_id : str ,
265
+ devclass : Optional [str ],
260
266
):
261
267
self .__backend_domain = backend_domain
262
268
self .__port_id = port_id
@@ -370,6 +376,7 @@ def has_devclass(self):
370
376
371
377
class AnyPort (Port ):
372
378
"""Represents any port in virtual devices ("*")"""
379
+
373
380
def __init__ (self , devclass : str ):
374
381
super ().__init__ (None , "*" , devclass )
375
382
@@ -395,14 +402,14 @@ def __init__(
395
402
device_id : Optional [str ] = None ,
396
403
):
397
404
assert not isinstance (port , AnyPort ) or device_id is not None
398
- self .port : Optional [Port ] = port
405
+ self .port : Optional [Port ] = port # type: ignore
399
406
self ._device_id = device_id
400
407
401
408
def clone (self , ** kwargs ) -> "VirtualDevice" :
402
409
"""
403
410
Clone object and substitute attributes with explicitly given.
404
411
"""
405
- attr = {
412
+ attr : Dict [ str , Any ] = {
406
413
"port" : self .port ,
407
414
"device_id" : self .device_id ,
408
415
}
@@ -429,7 +436,7 @@ def port(self, value: Union[Port, str, None]):
429
436
@property
430
437
def device_id (self ) -> str :
431
438
# pylint: disable=missing-function-docstring
432
- if self .is_device_id_set :
439
+ if self ._device_id is not None and self . is_device_id_set :
433
440
return self ._device_id
434
441
return "*"
435
442
@@ -467,7 +474,7 @@ def description(self) -> str:
467
474
"""
468
475
Return human-readable description of the device identity.
469
476
"""
470
- if self .device_id == "*" :
477
+ if not self . device_id or self .device_id == "*" :
471
478
return "any device"
472
479
return self .device_id
473
480
@@ -581,12 +588,11 @@ def _parse(
581
588
backend = get_domain (backend_name )
582
589
else :
583
590
identity = representation
591
+
584
592
port_id , _ , devid = identity .partition (":" )
585
- if devid == "" :
586
- devid = None
587
593
return cls (
588
594
Port (backend_domain = backend , port_id = port_id , devclass = devclass ),
589
- device_id = devid ,
595
+ device_id = devid or None ,
590
596
)
591
597
592
598
def serialize (self ) -> bytes :
@@ -849,7 +855,7 @@ def __init__(
849
855
name : Optional [str ] = None ,
850
856
serial : Optional [str ] = None ,
851
857
interfaces : Optional [List [DeviceInterface ]] = None ,
852
- parent : Optional [Port ] = None ,
858
+ parent : Optional ["DeviceInfo" ] = None ,
853
859
attachment : Optional [QubesVM ] = None ,
854
860
device_id : Optional [str ] = None ,
855
861
** kwargs ,
@@ -1002,6 +1008,8 @@ def subdevices(self) -> List[VirtualDevice]:
1002
1008
If the device has subdevices (e.g., partitions of a USB stick),
1003
1009
the subdevices id should be here.
1004
1010
"""
1011
+ if not self .backend_domain :
1012
+ return []
1005
1013
return [
1006
1014
dev
1007
1015
for devclass in self .backend_domain .devices .keys ()
@@ -1103,7 +1111,7 @@ def _deserialize(
1103
1111
1104
1112
if "attachment" not in properties or not properties ["attachment" ]:
1105
1113
properties ["attachment" ] = None
1106
- else :
1114
+ elif expected_device . backend_domain :
1107
1115
app = expected_device .backend_domain .app
1108
1116
properties ["attachment" ] = app .domains .get_blind (
1109
1117
properties ["attachment" ]
@@ -1260,7 +1268,7 @@ def __lt__(self, other):
1260
1268
)
1261
1269
1262
1270
@property
1263
- def backend_domain (self ) -> QubesVM :
1271
+ def backend_domain (self ) -> Optional [ QubesVM ] :
1264
1272
# pylint: disable=missing-function-docstring
1265
1273
return self .virtual_device .backend_domain
1266
1274
@@ -1287,14 +1295,16 @@ def device_id(self) -> str:
1287
1295
@property
1288
1296
def devices (self ) -> List [DeviceInfo ]:
1289
1297
"""Get DeviceInfo objects corresponding to this DeviceAssignment"""
1298
+ result : List [DeviceInfo ] = []
1299
+ if not self .backend_domain :
1300
+ return result
1290
1301
if self .port_id != "*" :
1291
1302
dev = self .backend_domain .devices [self .devclass ][self .port_id ]
1292
1303
if (
1293
1304
isinstance (dev , UnknownDevice )
1294
1305
or self .device_id in (dev .device_id , "*" )
1295
1306
):
1296
1307
return [dev ]
1297
- result = []
1298
1308
if self .device_id == "0000:0000::?******" :
1299
1309
return result
1300
1310
for dev in self .backend_domain .devices [self .devclass ]:
@@ -1334,8 +1344,13 @@ def frontend_domain(self) -> Optional[QubesVM]:
1334
1344
def frontend_domain (self , frontend_domain : Optional [Union [str , QubesVM ]]):
1335
1345
"""Which domain the device is attached/assigned to."""
1336
1346
if isinstance (frontend_domain , str ):
1337
- frontend_domain = self .backend_domain .app .domains [frontend_domain ]
1338
- self .__frontend_domain = frontend_domain
1347
+ if not self .backend_domain :
1348
+ raise ProtocolError ("Cannot determine backend domain" )
1349
+ self .__frontend_domain : Optional [QubesVM ] = (
1350
+ self .backend_domain .app .domains [frontend_domain ]
1351
+ )
1352
+ else :
1353
+ self .__frontend_domain = frontend_domain
1339
1354
1340
1355
@property
1341
1356
def attached (self ) -> bool :
0 commit comments