public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: "Max R. Carrara" <m.carrara@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [pve-devel] [PATCH v1 pve-esxi-import-tools 1/5] listvms: respect new type hints of pyVmomi package
Date: Fri, 18 Jul 2025 18:19:01 +0200	[thread overview]
Message-ID: <20250718161905.461482-2-m.carrara@proxmox.com> (raw)
In-Reply-To: <20250718161905.461482-1-m.carrara@proxmox.com>

This makes mypy on trixie happy again.

Make the type hints explicit; even though this is a little more
verbose, it should make any typing-related changes more visible in the
future. Also return (hopefully) sane defaults wherever possible.

Note that Python doesn't have something like "null-aware member
access" like JS does, so there isn't really any prettier version of
doing this.

(Besides, I have *never* seen as many `Optional`s in any Python
codebase as in the pyVmomi package's type stubs, let alone their
"on-demand loading" of types.)

Signed-off-by: Max R. Carrara <m.carrara@proxmox.com>
---
 listvms.py | 95 ++++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 81 insertions(+), 14 deletions(-)

diff --git a/listvms.py b/listvms.py
index 44f207c..221d562 100755
--- a/listvms.py
+++ b/listvms.py
@@ -162,14 +162,29 @@ def get_datacenter_of_vm(vm: vim.VirtualMachine) -> vim.Datacenter | None:
 
 def list_vms(service_instance: vim.ServiceInstance) -> list[vim.VirtualMachine]:
     """List all VMs on the ESXi/vCenter server."""
-    content = service_instance.content
-    vm_view: Any = content.viewManager.CreateContainerView(
+
+    content: vim.ServiceInstanceContent | None = service_instance.content
+
+    if content is None:
+        return []
+
+    view_manager: vim.view.ViewManager | None = content.viewManager
+
+    if view_manager is None:
+        return []
+
+    vm_view = view_manager.CreateContainerView(
         content.rootFolder,
         [vim.VirtualMachine],
         True,
     )
+
+    if vm_view is None:
+        return []
+
     vms = vm_view.view
     vm_view.Destroy()
+
     return vms
 
 
@@ -180,23 +195,42 @@ def parse_file_path(path) -> tuple[str, str]:
     return (datastore_name, relative_path)
 
 
-def get_vm_vmx_info(vm: vim.VirtualMachine) -> VmVmxInfo:
+def get_vm_vmx_info(vm: vim.VirtualMachine) -> VmVmxInfo | None:
     """Extract VMX file path and checksum from a VM object."""
-    datastore_name, relative_vmx_path = parse_file_path(
-        vm.config.files.vmPathName
+
+    config: vim.vm.ConfigInfo | None = vm.config
+
+    if config is None:
+        return None
+
+    files: vim.vm.FileInfo | None = config.files
+
+    if files is None:
+        return None
+
+    vm_path_name: str | None = files.vmPathName
+
+    if vm_path_name is None:
+        return None
+
+    datastore_name, relative_vmx_path = parse_file_path(vm_path_name)
+    checksum = (
+        config.vmxConfigChecksum.hex() if config.vmxConfigChecksum else "N/A"
     )
 
     return VmVmxInfo(
         datastore=datastore_name,
         path=relative_vmx_path,
-        checksum=vm.config.vmxConfigChecksum.hex()
-        if vm.config.vmxConfigChecksum
-        else "N/A",
+        checksum=checksum,
     )
 
 
 def get_vm_disk_info(vm: vim.VirtualMachine) -> list[VmDiskInfo]:
-    disks = []
+    disks: list[VmDiskInfo] = []
+
+    if vm.config is None:
+        return disks
+
     for device in vm.config.hardware.device:
         if isinstance(device, vim.vm.device.VirtualDisk):
             try:
@@ -217,12 +251,27 @@ def get_all_datacenters(
     service_instance: vim.ServiceInstance,
 ) -> list[vim.Datacenter]:
     """Retrieve all datacenters from the ESXi/vCenter server."""
-    content = service_instance.content
-    dc_view: Any = content.viewManager.CreateContainerView(
+
+    content: vim.ServiceInstanceContent | None = service_instance.content
+
+    if content is None:
+        return []
+
+    view_manager: vim.view.ViewManager | None = content.viewManager
+
+    if view_manager is None:
+        return []
+
+    dc_view = view_manager.CreateContainerView(
         content.rootFolder, [vim.Datacenter], True
     )
+
+    if dc_view is None:
+        return []
+
     datacenters = dc_view.view
     dc_view.Destroy()
+
     return datacenters
 
 
@@ -242,13 +291,28 @@ def fetch_and_update_vm_data(vm: vim.VirtualMachine, data: dict[Any, Any]):
     vms = data[datacenter.name].setdefault("vms", {})
     datastores = data[datacenter.name].setdefault("datastores", {})
 
+    config: vim.vm.ConfigInfo | None = vm.config
+
+    if config is None:
+        return
+
+    vm_vmx_info: VmVmxInfo | None = get_vm_vmx_info(vm)
+
+    if vm_vmx_info is None:
+        return
+
+    runtime: vim.vm.RuntimeInfo | None = vm.runtime
+
+    if runtime is None:
+        return
+
     vms[vm.name] = VmInfo(
-        config=get_vm_vmx_info(vm),
+        config=vm_vmx_info,
         disks=get_vm_disk_info(vm),
-        power=str(vm.runtime.powerState),
+        power=str(runtime.powerState),
     )
 
-    datastores.update({ds.name: ds.url for ds in vm.config.datastoreUrl})
+    datastores.update({ds.name: ds.url for ds in config.datastoreUrl})
 
 
 def is_vcls_agent_vm(vm: vim.VirtualMachine) -> bool:
@@ -261,6 +325,9 @@ def is_vcls_agent_vm(vm: vim.VirtualMachine) -> bool:
                for cfg in vm.config.extraConfig)
 
 def is_diskless_vm(vm: vim.VirtualMachine) -> bool:
+    if vm.config is None or vm.config.files is None:
+        return True
+
     datastore_name, _ = parse_file_path(vm.config.files.vmPathName)
 
     return not datastore_name
-- 
2.39.5



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


  reply	other threads:[~2025-07-18 16:18 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-07-18 16:19 [pve-devel] [PATCH v1 pve-esxi-import-tools 0/5] Type Checking Improvements Max R. Carrara
2025-07-18 16:19 ` Max R. Carrara [this message]
2025-07-18 16:19 ` [pve-devel] [PATCH v1 pve-esxi-import-tools 2/5] listvms: s/EsxiConnectonArgs/EsxiConnectionArgs Max R. Carrara
2025-07-18 16:19 ` [pve-devel] [PATCH v1 pve-esxi-import-tools 3/5] listvms: run formatter Max R. Carrara
2025-07-18 16:19 ` [pve-devel] [PATCH v1 pve-esxi-import-tools 4/5] d/control: add python3-pyvmomi (>= 8) as build dependency Max R. Carrara
2025-07-18 16:19 ` [pve-devel] [PATCH v1 pve-esxi-import-tools 5/5] .gitignore: ignore .lint-incremental Max R. Carrara
2025-07-22 20:14 ` [pve-devel] [PATCH v1 pve-esxi-import-tools 0/5] Type Checking Improvements Thomas Lamprecht

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250718161905.461482-2-m.carrara@proxmox.com \
    --to=m.carrara@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal