)]}'
{"/COMMIT_MSG":[{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"84c3d70e37e88a5846400175adffc431a6274d0b","unresolved":true,"context_lines":[{"line_number":24,"context_line":""},{"line_number":25,"context_line":"This patch only adds the shared helpers and their unit tests; migrating"},{"line_number":26,"context_line":"the individual drivers to use them will follow in later patches."},{"line_number":27,"context_line":""},{"line_number":28,"context_line":"Partial-Bug: #2152545"},{"line_number":29,"context_line":"Generated-By: Claude Code (claude-opus-4-8)"},{"line_number":30,"context_line":"Change-Id: I810742dfa79a369f441b6f6cf7234ae88330afe4"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":2,"id":"e765cb68_d7a7d420","line":27,"updated":"2026-07-27 16:08:13.000000000","message":"thanks for starting on this.\nnext cycle we are considerign a more invasieve rewite of the pci enumartion and discoveray functionatliy but paying down this initall technial debt will make that converstaion eaiser to progress.","commit_id":"8afcdd8772353241802cdae89bb10eb750a4a5d1"},{"author":{"_account_id":39344,"name":"Gihong Lee","display_name":"gamio","email":"gh9231@gmail.com","username":"gamio"},"change_message_id":"61bf3035197b515b5b38dc2f88acfffae31f790d","unresolved":false,"context_lines":[{"line_number":24,"context_line":""},{"line_number":25,"context_line":"This patch only adds the shared helpers and their unit tests; migrating"},{"line_number":26,"context_line":"the individual drivers to use them will follow in later patches."},{"line_number":27,"context_line":""},{"line_number":28,"context_line":"Partial-Bug: #2152545"},{"line_number":29,"context_line":"Generated-By: Claude Code (claude-opus-4-8)"},{"line_number":30,"context_line":"Change-Id: I810742dfa79a369f441b6f6cf7234ae88330afe4"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":2,"id":"abcceb95_fead39f1","line":27,"in_reply_to":"e765cb68_d7a7d420","updated":"2026-07-28 12:20:50.000000000","message":"Thanks, happy to help pay this down.","commit_id":"8afcdd8772353241802cdae89bb10eb750a4a5d1"},{"author":{"_account_id":34452,"name":"Joan Gilabert","display_name":"jgilaber","email":"jgilaber@redhat.com","username":"jgilaber"},"change_message_id":"121ff942c00f05f28cd1fe861009599ea7b9cd1e","unresolved":true,"context_lines":[{"line_number":13,"context_line":""},{"line_number":14,"context_line":"Introduce shared implementations in the accelerator common utils:"},{"line_number":15,"context_line":""},{"line_number":16,"context_line":"* lspci_privileged(cmd\u003d(\u0027lspci\u0027, \u0027-nnn\u0027, \u0027-D\u0027)) wraps the privileged"},{"line_number":17,"context_line":"  lspci call and takes an optional command, so callers that need a"},{"line_number":18,"context_line":"  different invocation (e.g. xilinx\u0027s per-device \u0027lspci -k -s\u0027) can"},{"line_number":19,"context_line":"  reuse it."}],"source_content_type":"text/x-gerrit-commit-message","patch_set":3,"id":"a7f1144c_ba53ab4d","line":16,"updated":"2026-07-28 13:45:40.000000000","message":"nit: this has been changed in code to `-nn`, but not updated here. And the same is true about the parameters for the `get_pci_devices`","commit_id":"901bbfa50717e63ae50c11ff43d7111069c1b5e7"},{"author":{"_account_id":39344,"name":"Gihong Lee","display_name":"gamio","email":"gh9231@gmail.com","username":"gamio"},"change_message_id":"24ac1064f69c44748983c5bcb16877fa40712a4b","unresolved":false,"context_lines":[{"line_number":13,"context_line":""},{"line_number":14,"context_line":"Introduce shared implementations in the accelerator common utils:"},{"line_number":15,"context_line":""},{"line_number":16,"context_line":"* lspci_privileged(cmd\u003d(\u0027lspci\u0027, \u0027-nnn\u0027, \u0027-D\u0027)) wraps the privileged"},{"line_number":17,"context_line":"  lspci call and takes an optional command, so callers that need a"},{"line_number":18,"context_line":"  different invocation (e.g. xilinx\u0027s per-device \u0027lspci -k -s\u0027) can"},{"line_number":19,"context_line":"  reuse it."}],"source_content_type":"text/x-gerrit-commit-message","patch_set":3,"id":"a8040e96_44a1338c","line":16,"in_reply_to":"a7f1144c_ba53ab4d","updated":"2026-07-28 14:11:16.000000000","message":"Done, updated the commit messages to match the predicate-based design.","commit_id":"901bbfa50717e63ae50c11ff43d7111069c1b5e7"}],"/PATCHSET_LEVEL":[{"author":{"_account_id":34452,"name":"Joan Gilabert","display_name":"jgilaber","email":"jgilaber@redhat.com","username":"jgilaber"},"change_message_id":"121ff942c00f05f28cd1fe861009599ea7b9cd1e","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":3,"id":"76aab6e1_d35bdabb","updated":"2026-07-28 13:45:40.000000000","message":"lgtm","commit_id":"901bbfa50717e63ae50c11ff43d7111069c1b5e7"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"7b6885ce597409e041a2071ac2d979442874c8ff","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":5,"id":"c6c0dd1d_24ad2f22","updated":"2026-07-28 15:28:15.000000000","message":"ill leave this up for a few days for others to review but this looks reasonable to me. i also want to review the next patch as well but i think this will result in quite a nice cleanup.","commit_id":"40fedd89604bc6e31c4d5c615278eea6e2fd2aa0"},{"author":{"_account_id":39344,"name":"Gihong Lee","display_name":"gamio","email":"gh9231@gmail.com","username":"gamio"},"change_message_id":"364a73e43499a1ad5d3a91f302a6c327dcd31ba1","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":5,"id":"31f7ca98_af2479d9","in_reply_to":"c6c0dd1d_24ad2f22","updated":"2026-07-29 14:41:19.000000000","message":"Thanks. Fixed the privsep contract in this revision. Ready for another look whenever you have time.","commit_id":"40fedd89604bc6e31c4d5c615278eea6e2fd2aa0"},{"author":{"_account_id":4690,"name":"melanie witt","display_name":"melwitt","email":"melwittt@gmail.com","username":"melwitt"},"change_message_id":"865ff74cb9023946354823659e3a7522e0a0651a","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":7,"id":"3af613e0_6bb4eb6e","updated":"2026-08-04 16:49:34.000000000","message":"This looks much better, thank you","commit_id":"1ac482dc87c463752626a18f927b996a2da48acd"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"b0f2d7a8c04141f596cca42931d0b5ca0102e975","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":7,"id":"cdbbfd88_3cd1789c","updated":"2026-08-04 15:13:14.000000000","message":"i think this looks good but lets see what other think","commit_id":"1ac482dc87c463752626a18f927b996a2da48acd"}],"cyborg/accelerator/common/utils.py":[{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"4e9b71dd708ae1e9836601c8556ae55ae87166c1","unresolved":false,"context_lines":[{"line_number":152,"context_line":"    return processutils.execute(*cmd)[0]"},{"line_number":153,"context_line":""},{"line_number":154,"context_line":""},{"line_number":155,"context_line":"def get_pci_devices(pci_flags, vendor_id\u003dNone, match\u003dany):"},{"line_number":156,"context_line":"    device_for_vendor_out \u003d []"},{"line_number":157,"context_line":"    all_device_out \u003d []"},{"line_number":158,"context_line":"    lspci_out \u003d lspci_privileged().split(\u0027\\n\u0027)"}],"source_content_type":"text/x-python","patch_set":2,"id":"2a14bf0d_9159c1de","line":155,"updated":"2026-07-26 14:35:43.000000000","message":"The shared get_pci_devices() always calls lspci_privileged() with no arguments, so it permanently uses the default (\u0027lspci\u0027, \u0027-nnn\u0027, \u0027-D\u0027). The SSD driver\u0027s existing lspci_privileged() uses \u0027lspci -nn -D\u0027 (two n\u0027s, not three). Since get_pci_devices does not expose a cmd parameter, migrating the S...\n\n**Severity**: SUGGESTION | **Confidence**: 0.8\n\n**Benefit**: When the SSD driver is migrated to use the shared get_pci_devices (as planned in the commit message), the lspci output will switch from -nn to -nnn format. This changes the verbosity of PCI class/device names in the output, which could affect downstream parsing of SSD device info. The SSD driver...\n\n**Recommendation**:\nAdd an optional cmd parameter to get_pci_devices (e.g. def get_pci_devices(pci_flags, vendor_id\u003dNone, match\u003dany, cmd\u003dNone)) and pass it through to lspci_privileged when provided, so all drivers including SSD can reuse the shared function while preserving their current lspci invocation. Alternatively, document this limitation explicitly so the SSD migration patch addresses it.","commit_id":"8afcdd8772353241802cdae89bb10eb750a4a5d1"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"84c3d70e37e88a5846400175adffc431a6274d0b","unresolved":true,"context_lines":[{"line_number":152,"context_line":"    return processutils.execute(*cmd)[0]"},{"line_number":153,"context_line":""},{"line_number":154,"context_line":""},{"line_number":155,"context_line":"def get_pci_devices(pci_flags, vendor_id\u003dNone, match\u003dany):"},{"line_number":156,"context_line":"    device_for_vendor_out \u003d []"},{"line_number":157,"context_line":"    all_device_out \u003d []"},{"line_number":158,"context_line":"    lspci_out \u003d lspci_privileged().split(\u0027\\n\u0027)"}],"source_content_type":"text/x-python","patch_set":2,"id":"7561371f_22c24d10","line":155,"range":{"start_line":155,"start_character":47,"end_line":155,"end_character":52},"updated":"2026-07-27 16:08:13.000000000","message":"match is a keyword in python 3.10 and later\n\n\n\n```suggestion\ndef get_pci_devices(pci_flags, vendor_id\u003dNone, predicate\u003dany):\n```","commit_id":"8afcdd8772353241802cdae89bb10eb750a4a5d1"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"84c3d70e37e88a5846400175adffc431a6274d0b","unresolved":true,"context_lines":[{"line_number":152,"context_line":"    return processutils.execute(*cmd)[0]"},{"line_number":153,"context_line":""},{"line_number":154,"context_line":""},{"line_number":155,"context_line":"def get_pci_devices(pci_flags, vendor_id\u003dNone, match\u003dany):"},{"line_number":156,"context_line":"    device_for_vendor_out \u003d []"},{"line_number":157,"context_line":"    all_device_out \u003d []"},{"line_number":158,"context_line":"    lspci_out \u003d lspci_privileged().split(\u0027\\n\u0027)"}],"source_content_type":"text/x-python","patch_set":2,"id":"399b9f3e_b779f648","line":155,"in_reply_to":"2a14bf0d_9159c1de","updated":"2026-07-27 16:08:13.000000000","message":"so we do not want this to be configurable in general, however -nnn is not a valid parmater\n\nthere is -n and -nn\n\ni think -nnn woudl be parsed as both `lspci -n -nn -D`\nor internally if its usign getopt it woudl coudn the ns and set the levle ot 3 but that is not something lspci actully implent.\n\nwe shodul jsut use `-nn` since tha tis the behvior we actully want and what is documented by lspci\n\n\nhttps://man7.org/linux/man-pages/man8/lspci.8.html","commit_id":"8afcdd8772353241802cdae89bb10eb750a4a5d1"},{"author":{"_account_id":39344,"name":"Gihong Lee","display_name":"gamio","email":"gh9231@gmail.com","username":"gamio"},"change_message_id":"61bf3035197b515b5b38dc2f88acfffae31f790d","unresolved":false,"context_lines":[{"line_number":152,"context_line":"    return processutils.execute(*cmd)[0]"},{"line_number":153,"context_line":""},{"line_number":154,"context_line":""},{"line_number":155,"context_line":"def get_pci_devices(pci_flags, vendor_id\u003dNone, match\u003dany):"},{"line_number":156,"context_line":"    device_for_vendor_out \u003d []"},{"line_number":157,"context_line":"    all_device_out \u003d []"},{"line_number":158,"context_line":"    lspci_out \u003d lspci_privileged().split(\u0027\\n\u0027)"}],"source_content_type":"text/x-python","patch_set":2,"id":"affdf696_0d2c95f2","line":155,"in_reply_to":"399b9f3e_b779f648","updated":"2026-07-28 12:20:50.000000000","message":"I changed the default to `-nn`.","commit_id":"8afcdd8772353241802cdae89bb10eb750a4a5d1"},{"author":{"_account_id":39344,"name":"Gihong Lee","display_name":"gamio","email":"gh9231@gmail.com","username":"gamio"},"change_message_id":"61bf3035197b515b5b38dc2f88acfffae31f790d","unresolved":false,"context_lines":[{"line_number":152,"context_line":"    return processutils.execute(*cmd)[0]"},{"line_number":153,"context_line":""},{"line_number":154,"context_line":""},{"line_number":155,"context_line":"def get_pci_devices(pci_flags, vendor_id\u003dNone, match\u003dany):"},{"line_number":156,"context_line":"    device_for_vendor_out \u003d []"},{"line_number":157,"context_line":"    all_device_out \u003d []"},{"line_number":158,"context_line":"    lspci_out \u003d lspci_privileged().split(\u0027\\n\u0027)"}],"source_content_type":"text/x-python","patch_set":2,"id":"2aad5965_6d1bf2c5","line":155,"range":{"start_line":155,"start_character":47,"end_line":155,"end_character":52},"in_reply_to":"7561371f_22c24d10","updated":"2026-07-28 12:20:50.000000000","message":"Done. Renamed to a predicate based API as you suggested.","commit_id":"8afcdd8772353241802cdae89bb10eb750a4a5d1"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"84c3d70e37e88a5846400175adffc431a6274d0b","unresolved":true,"context_lines":[{"line_number":153,"context_line":""},{"line_number":154,"context_line":""},{"line_number":155,"context_line":"def get_pci_devices(pci_flags, vendor_id\u003dNone, match\u003dany):"},{"line_number":156,"context_line":"    device_for_vendor_out \u003d []"},{"line_number":157,"context_line":"    all_device_out \u003d []"},{"line_number":158,"context_line":"    lspci_out \u003d lspci_privileged().split(\u0027\\n\u0027)"},{"line_number":159,"context_line":"    for pci in lspci_out:"},{"line_number":160,"context_line":"        if match(x in pci for x in pci_flags):"},{"line_number":161,"context_line":"            all_device_out.append(pci)"},{"line_number":162,"context_line":"            if vendor_id and vendor_id in pci:"},{"line_number":163,"context_line":"                device_for_vendor_out.append(pci)"},{"line_number":164,"context_line":"    return device_for_vendor_out if vendor_id else all_device_out"}],"source_content_type":"text/x-python","patch_set":2,"id":"c1012a41_d87d6d4c","line":164,"range":{"start_line":156,"start_character":0,"end_line":164,"end_character":65},"updated":"2026-07-27 16:08:13.000000000","message":"i think in general this could be expess more compoabley wiht a cleaner interface if we model this as a generator expression that thats as set of composable predicates\n\nsomething like this\n\n```\nfrom collections import abc\nfrom oslo_concurrency import processutils\n\nimport cyborg.privsep\n\n\n@cyborg.privsep.sys_admin_pctxt.entrypoint\ndef lspci_privileged(cmd\u003d(\u0027lspci\u0027, \u0027-nn\u0027, \u0027-D\u0027)):\n    return processutils.execute(*cmd)[0]\n\n\ndef has_flags(\n    flags: abc.Iterable[str],\n    reducer: abc.Callable[[abc.Iterable[bool]], bool] \u003d any,\n) -\u003e abc.Callable[[str], bool]:\n    \"\"\"Return a predicate checking if a line matches flags using a reducer (any/all).\"\"\"\n    return lambda line: reducer(flag in line for flag in flags)\n\n\ndef has_vendor(vendor_id: str) -\u003e abc.Callable[[str], bool]:\n    \"\"\"Return a predicate checking if a vendor ID exists in a line.\"\"\"\n    return lambda line: vendor_id in line\n\n\ndef get_pci_devices(\n    *predicates: abc.Callable[[str], bool]\n) -\u003e abc.Iterator[str]:\n    \"\"\"Yield PCI lines that satisfy all provided predicate functions.\n\n    Example:\n        \u003e\u003e\u003e # Stream Intel (8086) devices matching all specified flags (AND reducer)\n        \u003e\u003e\u003e intel_network_devices \u003d get_pci_devices(\n        ...     has_vendor(\"8086\"),\n        ...     has_flags([\"Ethernet\", \"Controller\"], reducer\u003dall),\n        ... )\n        \u003e\u003e\u003e for device in intel_network_devices:\n        ...     print(device)\n\n        \u003e\u003e\u003e # Stream devices that match either NVIDIA or 3D controller flags (OR reducer)\n        \u003e\u003e\u003e nvidia_or_gpus \u003d get_pci_devices(\n        ...     has_flags([\"NVIDIA\", \"3D controller\"], reducer\u003dany),\n        ... )\n\n        \u003e\u003e\u003e # Stream all PCI devices unconditionally\n        \u003e\u003e\u003e all_devices \u003d get_pci_devices()\n    \"\"\"\n    for pci in lspci_privileged().splitlines():\n        if all(predicate(pci) for predicate in predicates):\n            yield pci\n```","commit_id":"8afcdd8772353241802cdae89bb10eb750a4a5d1"},{"author":{"_account_id":39344,"name":"Gihong Lee","display_name":"gamio","email":"gh9231@gmail.com","username":"gamio"},"change_message_id":"61bf3035197b515b5b38dc2f88acfffae31f790d","unresolved":false,"context_lines":[{"line_number":153,"context_line":""},{"line_number":154,"context_line":""},{"line_number":155,"context_line":"def get_pci_devices(pci_flags, vendor_id\u003dNone, match\u003dany):"},{"line_number":156,"context_line":"    device_for_vendor_out \u003d []"},{"line_number":157,"context_line":"    all_device_out \u003d []"},{"line_number":158,"context_line":"    lspci_out \u003d lspci_privileged().split(\u0027\\n\u0027)"},{"line_number":159,"context_line":"    for pci in lspci_out:"},{"line_number":160,"context_line":"        if match(x in pci for x in pci_flags):"},{"line_number":161,"context_line":"            all_device_out.append(pci)"},{"line_number":162,"context_line":"            if vendor_id and vendor_id in pci:"},{"line_number":163,"context_line":"                device_for_vendor_out.append(pci)"},{"line_number":164,"context_line":"    return device_for_vendor_out if vendor_id else all_device_out"}],"source_content_type":"text/x-python","patch_set":2,"id":"abfa8593_b4127d87","line":164,"range":{"start_line":156,"start_character":0,"end_line":164,"end_character":65},"in_reply_to":"c1012a41_d87d6d4c","updated":"2026-07-28 12:20:50.000000000","message":"Adopted your suggestion. Added `has_flags` and `has_vendor` predicates, and `get_pci_devices` now takes `*predicates` and yields.","commit_id":"8afcdd8772353241802cdae89bb10eb750a4a5d1"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"35cd2c447b7edb60ac75bfcdc3f4b7848b4421d3","unresolved":true,"context_lines":[{"line_number":148,"context_line":""},{"line_number":149,"context_line":""},{"line_number":150,"context_line":"@cyborg.privsep.sys_admin_pctxt.entrypoint"},{"line_number":151,"context_line":"def lspci_privileged(cmd\u003d(\u0027lspci\u0027, \u0027-nn\u0027, \u0027-D\u0027)):"},{"line_number":152,"context_line":"    return processutils.execute(*cmd)[0]"},{"line_number":153,"context_line":""},{"line_number":154,"context_line":""}],"source_content_type":"text/x-python","patch_set":5,"id":"64444a90_fa84f750","line":151,"updated":"2026-07-28 16:53:15.000000000","message":"sorry missed this we do not allow privdage function with wid contract liek this\n\ni.e. where you are basiclly just passing any comands to processutils.execute(*cmd)","commit_id":"40fedd89604bc6e31c4d5c615278eea6e2fd2aa0"},{"author":{"_account_id":39344,"name":"Gihong Lee","display_name":"gamio","email":"gh9231@gmail.com","username":"gamio"},"change_message_id":"364a73e43499a1ad5d3a91f302a6c327dcd31ba1","unresolved":false,"context_lines":[{"line_number":148,"context_line":""},{"line_number":149,"context_line":""},{"line_number":150,"context_line":"@cyborg.privsep.sys_admin_pctxt.entrypoint"},{"line_number":151,"context_line":"def lspci_privileged(cmd\u003d(\u0027lspci\u0027, \u0027-nn\u0027, \u0027-D\u0027)):"},{"line_number":152,"context_line":"    return processutils.execute(*cmd)[0]"},{"line_number":153,"context_line":""},{"line_number":154,"context_line":""}],"source_content_type":"text/x-python","patch_set":5,"id":"d62d1912_9a921938","line":151,"in_reply_to":"64444a90_fa84f750","updated":"2026-07-29 14:41:19.000000000","message":"`lspci_privileged()` now runs a fixed \u0027lspci -nn -D\u0027 with no command parameter, so nothing arbitrary is passed to execute.","commit_id":"40fedd89604bc6e31c4d5c615278eea6e2fd2aa0"},{"author":{"_account_id":4690,"name":"melanie witt","display_name":"melwitt","email":"melwittt@gmail.com","username":"melwitt"},"change_message_id":"11a2dd4675b8490598f69d0846cc1b73116e39f0","unresolved":true,"context_lines":[{"line_number":152,"context_line":"    return processutils.execute(\u0027lspci\u0027, \u0027-nn\u0027, \u0027-D\u0027)[0]"},{"line_number":153,"context_line":""},{"line_number":154,"context_line":""},{"line_number":155,"context_line":"def has_flags("},{"line_number":156,"context_line":"    flags: collections.abc.Iterable[str],"},{"line_number":157,"context_line":"    reducer: collections.abc.Callable["},{"line_number":158,"context_line":"        [collections.abc.Iterable[bool]], bool"},{"line_number":159,"context_line":"    ] \u003d any,"},{"line_number":160,"context_line":") -\u003e collections.abc.Callable[[str], bool]:"},{"line_number":161,"context_line":"    \"\"\"Return a predicate checking if a line matches flags using a reducer.\"\"\""},{"line_number":162,"context_line":"    return lambda line: reducer(flag in line for flag in flags)"},{"line_number":163,"context_line":""},{"line_number":164,"context_line":""},{"line_number":165,"context_line":"def has_vendor(vendor_id: str) -\u003e collections.abc.Callable[[str], bool]:"},{"line_number":166,"context_line":"    \"\"\"Return a predicate checking if a vendor ID exists in a line.\"\"\""},{"line_number":167,"context_line":"    return lambda line: vendor_id in line"},{"line_number":168,"context_line":""},{"line_number":169,"context_line":""},{"line_number":170,"context_line":"def get_pci_devices("},{"line_number":171,"context_line":"    *predicates: collections.abc.Callable[[str], bool],"},{"line_number":172,"context_line":") -\u003e collections.abc.Iterator[str]:"},{"line_number":173,"context_line":"    \"\"\"Yield PCI lines that satisfy all provided predicate functions.\"\"\""},{"line_number":174,"context_line":"    for pci in lspci_privileged().splitlines():"},{"line_number":175,"context_line":"        if all(predicate(pci) for predicate in predicates):"},{"line_number":176,"context_line":"            yield pci"}],"source_content_type":"text/x-python","patch_set":6,"id":"1d904641_7a757f70","line":176,"range":{"start_line":155,"start_character":0,"end_line":176,"end_character":21},"updated":"2026-07-30 22:50:19.000000000","message":"This seems to work but ... do we really need all this complication? Each caller just needs flags, vendor_id and whether to match all vs any.\n\nThere isn\u0027t a need for callers to provide custom predicates and even if there were, this makes the contract really opaque IMHO.\n\nI thought PS2 was much better than this, is there any objection to going back to that?","commit_id":"d9327e31f15f9c3acf1f7b26f78fb06e2f1b38b6"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"f1ddc57c5f6bf01e6aa7bff25fa6441b46e51ae3","unresolved":true,"context_lines":[{"line_number":152,"context_line":"    return processutils.execute(\u0027lspci\u0027, \u0027-nn\u0027, \u0027-D\u0027)[0]"},{"line_number":153,"context_line":""},{"line_number":154,"context_line":""},{"line_number":155,"context_line":"def has_flags("},{"line_number":156,"context_line":"    flags: collections.abc.Iterable[str],"},{"line_number":157,"context_line":"    reducer: collections.abc.Callable["},{"line_number":158,"context_line":"        [collections.abc.Iterable[bool]], bool"},{"line_number":159,"context_line":"    ] \u003d any,"},{"line_number":160,"context_line":") -\u003e collections.abc.Callable[[str], bool]:"},{"line_number":161,"context_line":"    \"\"\"Return a predicate checking if a line matches flags using a reducer.\"\"\""},{"line_number":162,"context_line":"    return lambda line: reducer(flag in line for flag in flags)"},{"line_number":163,"context_line":""},{"line_number":164,"context_line":""},{"line_number":165,"context_line":"def has_vendor(vendor_id: str) -\u003e collections.abc.Callable[[str], bool]:"},{"line_number":166,"context_line":"    \"\"\"Return a predicate checking if a vendor ID exists in a line.\"\"\""},{"line_number":167,"context_line":"    return lambda line: vendor_id in line"},{"line_number":168,"context_line":""},{"line_number":169,"context_line":""},{"line_number":170,"context_line":"def get_pci_devices("},{"line_number":171,"context_line":"    *predicates: collections.abc.Callable[[str], bool],"},{"line_number":172,"context_line":") -\u003e collections.abc.Iterator[str]:"},{"line_number":173,"context_line":"    \"\"\"Yield PCI lines that satisfy all provided predicate functions.\"\"\""},{"line_number":174,"context_line":"    for pci in lspci_privileged().splitlines():"},{"line_number":175,"context_line":"        if all(predicate(pci) for predicate in predicates):"},{"line_number":176,"context_line":"            yield pci"}],"source_content_type":"text/x-python","patch_set":6,"id":"85705c18_d53ec7f1","line":176,"range":{"start_line":155,"start_character":0,"end_line":176,"end_character":21},"in_reply_to":"1d904641_7a757f70","updated":"2026-07-31 10:14:10.000000000","message":"i think this is much much cleaner and maintainable long term.\n\ni explcitly asked for this desgin supporting custom predifcates and i would prefer not to go back to v2\n\nbasic vendor id matach and lags is enough for driver other then the generic pci driver but the pci driver need much more advanced match based ont eh whitelist\n\nthe tow mough common wasy to use the generi drver woudl  be either to pass the vendor id and product id pair\nor the adress using one on the supprot format which include regex and blob matches\n\n\nhttps://github.com/openstack/cyborg/blob/master/cyborg/accelerator/drivers/pci/pci/sysinfo.py#L139-L167\n\nhttps://github.com/openstack/cyborg/blob/master/cyborg/accelerator/drivers/pci/devspec.py#L33-L251\n\nif we use the predicate approch we can rewirte \n\nhttps://github.com/openstack/cyborg/blob/master/cyborg/accelerator/drivers/pci/pci/sysinfo.py#L139-L167\n\nso that we parase the whitllist and reusie its device_assignable function directly as the predicate.\n\n```\ndef _discover_pcis():\n    cyborg.conf.devices.register_dynamic_opts(CONF)\n    dev_filter \u003d whitelist.Whitelist(CONF.pci.passthrough_whitelist)\n\n    # 1. Define a predicate closure to check if a PCI line passes whitelist filtering\n    def is_assignable(line: str) -\u003e bool:\n        m \u003d LSPCI_PATTERN.match(line)\n        if not m:\n            return False\n        pci_dict \u003d m.groupdict()\n        dev_info \u003d {\n            \u0027vendor_id\u0027: pci_dict[\u0027vendor_id\u0027],\n            \u0027product_id\u0027: pci_dict[\u0027product_id\u0027],\n            \u0027address\u0027: pci_dict[\u0027devices\u0027],\n            \u0027parent_addr\u0027: None,\n        }\n        return dev_filter.device_assignable(dev_info)\n\n    # 2. Delegate filtering to get_pci_devices using short-circuiting predicates\n    pci_lines \u003d pci_utils.get_pci_devices(\n        LSPCI_PATTERN.search,  # Fast check: ensures valid lspci line structure\n        is_assignable,         # Whitelist check: validates assignability\n    )\n\n    # 3. Build driver objects from the pre-filtered generator\n    pci_list \u003d []\n    for line in pci_lines:\n        # Guaranteed to match here since it passed predicates\n        pci_dict \u003d LSPCI_PATTERN.match(line).groupdict()\n        pci_dict[\u0027hostname\u0027] \u003d CONF.host\n        pci_dict[\"rc\"] \u003d constants.RESOURCES[\"PCI\"]\n        traits \u003d _get_traits(pci_dict[\"vendor_id\"], pci_dict[\"product_id\"])\n        pci_dict.update(traits)\n        pci_list.append(_generate_driver_device(pci_dict))\n\n    LOG.info(\u0027pci_list: %s\u0027, pci_list)\n    return pci_list\n```\n\neveutally i want to move to a model where driver will not use thise fucntion directly at all and this will be handel by a PciBusManager mdoel where it handles all enumeration, filtering and construction of PciDevice object form the underly stirng data. \n\nso in the PciBusManager version  we woudl be returnign something like this\n\n```\n@dataclass(slots\u003dTrue)\nclass PciDevice:\n    slot: str          # e.g., \"0000:0c:00.0\"\n    vendor_id: str     # e.g., \"19e5\"\n    product_id: str    # e.g., \"d100\"\n    class_name: str    # e.g., \"Processing accelerators\"\n    device_name: str   # e.g., \"Device\"\n    revision: Optional[str] \u003d None\n    raw_line: str \u003d \"\"\n\n    # --- Annotations \u0026 Metadata ---\n    traits: Set[str] \u003d field(default_factory\u003dset)\n    matched_specs: List[Any] \u003d field(default_factory\u003dlist)\n    tags: Dict[str, Any] \u003d field(default_factory\u003ddict)\n\n    def to_dict(self) -\u003e Dict[str, Any]:\n        return {\n            \u0027address\u0027: self.slot,\n            \u0027vendor_id\u0027: self.vendor_id,\n            \u0027product_id\u0027: self.product_id,\n            \u0027class\u0027: self.class_name,\n            \u0027device\u0027: self.device_name,\n            \u0027revision\u0027: self.revision,\n        }\n```\n\nwhich allow the whitelist to evolve to somethign liek this\n\n```\nclass Whitelist:\n    \"\"\"White list class to represent assignable PCI devices.\"\"\"\n\n    def __init__(self, whitelist_spec\u003dNone):\n        if whitelist_spec:\n            self.specs \u003d self._parse_white_list_from_config(whitelist_spec)\n        else:\n            self.specs \u003d []\n\n    def __call__(self, dev: PciDevice) -\u003e bool:\n        \"\"\"Predicate interface for get_pci_devices.\n\n        Filters device assignability and annotates matched device with\n        traits and spec references from config.\n        \"\"\"\n        matched \u003d False\n        for spec in self.specs:\n            if spec.match_pci_device(dev):\n                # Annotate the device with spec reference and traits from config\n                dev.matched_specs.append(spec)\n                dev.traits.update(spec.traits)\n                matched \u003d True\n\n        return matched\n```\n\nand \n\nthe generic pci driver can tehn basiclly be somethign like htis\n\n```\nclass PciDriver:\n    def __init__(self, bus_manager: Optional[PciBusManager] \u003d None):\n        self.bus_manager \u003d bus_manager or PciBusManager()\n\n    def discover(self):\n        \"\"\"Discover assignable PCI devices on the system.\n\n        Delegates system scanning to PciBusManager while using the\n        Whitelist predicate to filter devices and annotate traits.\n        \"\"\"\n        # 1. Initialize whitelist predicate from pre-registered config\n        dev_filter \u003d whitelist.Whitelist(CONF.pci.passthrough_whitelist)\n\n        # 2. Delegate discovery to PciBusManager passing Whitelist as predicate\n        devices: List[PciDevice] \u003d self.bus_manager.get_pci_devices(dev_filter)\n\n        # 3. Build driver objects from pre-filtered and pre-annotated device objects\n        pci_list \u003d []\n        for dev in devices:\n            pci_dict \u003d dev.to_dict()\n            pci_dict[\u0027hostname\u0027] \u003d CONF.host\n            pci_dict[\u0027rc\u0027] \u003d constants.RESOURCES[\"PCI\"]\n            pci_dict[\u0027traits\u0027] \u003d list(dev.traits)\n\n            driver_dev \u003d self._generate_driver_device(pci_dict)\n            pci_list.append(driver_dev)\n\n        LOG.info(\u0027Discovered pci_list: %s\u0027, pci_list)\n        return pci_list\n```\n\nthe direver undersnad hwo to parse there respecitve config section and pass in a predicate for the device they should be able to mange and those stateful predicate can also annote the device objects.\n\nthat means thet driver never need to know about lspci and only work with the typed objects and the PciBusManager never need to know how devices are matched.\n\n\nthis is what i had in mind orginally when i ased for this design as it enables us to prgressively work towards https://etherpad.opendev.org/p/cyborg-2026.2-ptg#L249","commit_id":"d9327e31f15f9c3acf1f7b26f78fb06e2f1b38b6"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"4c2284923ee5742583b2abe7ea4dbba0bd93f978","unresolved":true,"context_lines":[{"line_number":152,"context_line":"    return processutils.execute(\u0027lspci\u0027, \u0027-nn\u0027, \u0027-D\u0027)[0]"},{"line_number":153,"context_line":""},{"line_number":154,"context_line":""},{"line_number":155,"context_line":"def has_flags("},{"line_number":156,"context_line":"    flags: collections.abc.Iterable[str],"},{"line_number":157,"context_line":"    reducer: collections.abc.Callable["},{"line_number":158,"context_line":"        [collections.abc.Iterable[bool]], bool"},{"line_number":159,"context_line":"    ] \u003d any,"},{"line_number":160,"context_line":") -\u003e collections.abc.Callable[[str], bool]:"},{"line_number":161,"context_line":"    \"\"\"Return a predicate checking if a line matches flags using a reducer.\"\"\""},{"line_number":162,"context_line":"    return lambda line: reducer(flag in line for flag in flags)"},{"line_number":163,"context_line":""},{"line_number":164,"context_line":""},{"line_number":165,"context_line":"def has_vendor(vendor_id: str) -\u003e collections.abc.Callable[[str], bool]:"},{"line_number":166,"context_line":"    \"\"\"Return a predicate checking if a vendor ID exists in a line.\"\"\""},{"line_number":167,"context_line":"    return lambda line: vendor_id in line"},{"line_number":168,"context_line":""},{"line_number":169,"context_line":""},{"line_number":170,"context_line":"def get_pci_devices("},{"line_number":171,"context_line":"    *predicates: collections.abc.Callable[[str], bool],"},{"line_number":172,"context_line":") -\u003e collections.abc.Iterator[str]:"},{"line_number":173,"context_line":"    \"\"\"Yield PCI lines that satisfy all provided predicate functions.\"\"\""},{"line_number":174,"context_line":"    for pci in lspci_privileged().splitlines():"},{"line_number":175,"context_line":"        if all(predicate(pci) for predicate in predicates):"},{"line_number":176,"context_line":"            yield pci"}],"source_content_type":"text/x-python","patch_set":6,"id":"aa69a491_7bcbae5b","line":176,"range":{"start_line":155,"start_character":0,"end_line":176,"end_character":21},"in_reply_to":"5d2be88c_a30799da","updated":"2026-07-31 18:43:43.000000000","message":"it wont be contract less but without PciDevice calss it sort of is in this inital verison as we are leaking the raw lspci output.\n\nthe predicate contact would be defined in terms of PciDevice since once that is intoduce there would be no access to the underlying raw data only the parased objects. the example i gave above are psudo code an not quite the final contract i envison which is not very clear given i didnt remove the \"raw string\"\n\n\neven with the second patch  https://review.opendev.org/c/openstack/cyborg/+/998817/4\n\nthe current series doe not actully close the bug reprot\n\nit removign the duplication and divernce in lspci invocations but the main value wont come form that until we introduce common parsing and standardised PciDevice object.\n\n\nthis patch only does part of the proposed solution in https://bugs.launchpad.net/openstack-cyborg/+bug/2152545 by the way \n\nthe seocnd half of part 1 is\n\n```\n    # Common regex for parsing lspci -nnn -D output\n    PCI_DEVICE_PATTERN \u003d re.compile(\n        r\"(?P\u003caddress\u003e[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:\"\n        r\"[0-9a-fA-F]{2}\\.[0-9a-fA-F]) \"\n        r\"(?P\u003cclass_name\u003e.*?) \\[(?P\u003cclass_id\u003e[0-9a-fA-F]{4})\\]: \"\n        r\"(?P\u003cdevice_name\u003e.*?) \"\n        r\"\\[(?P\u003cvendor_id\u003e[0-9a-fA-F]{4}):(?P\u003cdevice_id\u003e[0-9a-fA-F]{4})\\]\"\n        r\"(?:.*?\\(rev (?P\u003crevision\u003e[0-9a-fA-F]{2})\\))?\"\n    )\n\n    def parse_lspci_line(line):\n        \"\"\"Parse a single line of lspci -nnn -D output.\n\n        :param line: Single line from lspci output\n        :return: Dict with parsed fields or None if parse fails\n        \"\"\"\n        match \u003d PCI_DEVICE_PATTERN.match(line)\n        if match:\n            return match.groupdict()\n        return None\n```\n\nwhich provcdie a singel regex to parse the output of lspci and transform it into a semi standardised dict of key value paris\n\nthat is what would give the majority of the code considtation win\n\nwithout that consoldiation i woudl not condiser https://bugs.launchpad.net/openstack-cyborg/+bug/2152545 closed.\n\nif this change intoduces the following\n\n```\nPCI_DEVICE_PATTERN \u003d re.compile(\n    r\"(?P\u003caddress\u003e[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:\"\n    r\"[0-9a-fA-F]{2}\\.[0-9a-fA-F]) \"\n    r\"(?P\u003cclass_name\u003e.*?) \\[(?P\u003cclass_id\u003e[0-9a-fA-F]{4})\\]: \"\n    r\"(?P\u003cdevice_name\u003e.*?) \"\n    r\"\\[(?P\u003cvendor_id\u003e[0-9a-fA-F]{4}):(?P\u003cdevice_id\u003e[0-9a-fA-F]{4})\\]\"\n    r\"(?:.*?\\(rev (?P\u003crevision\u003e[0-9a-fA-F]{2})\\))?\"\n)\n\n\ndef parse_lspci_line(line: str) -\u003e dict | None:\n    \"\"\"Parse a line of lspci output and normalize hex IDs to lowercase.\"\"\"\n    match \u003d PCI_DEVICE_PATTERN.match(line)\n    if not match:\n        return None\n\n    data \u003d match.groupdict()\n\n    # Normalize hex fields at the parser boundary\n    for key in (\"vendor_id\", \"device_id\", \"class_id\", \"revision\"):\n        if data.get(key):\n            data[key] \u003d data[key].lower()\n\n    return data\n\n\ndef get_pci_devices() -\u003e collections.abc.Iterator[dict]:\n    \"\"\"Yield parsed PCI device dictionaries from lspci output.\"\"\"\n    out \u003d lspci_privileged()[0]\n    for line in out.splitlines():\n        dev \u003d parse_lspci_line(line)\n        if dev:\n            yield dev\n```\n\nthen usign this here \n\nhttps://review.opendev.org/c/openstack/cyborg/+/998817/4/cyborg/accelerator/drivers/fpga/inspur/sysinfo.py#66\n\nwoudl looks like \n\n```\ndef fpga_tree():\n    fpga_list \u003d []\n    \n    # Ensure constant comparison is also normalized once\n    target_vendor \u003d VENDOR_ID.lower()\n\n    fpgas \u003d (\n        dev for dev in get_pci_devices()\n        if dev[\"vendor_id\"] \u003d\u003d target_vendor\n        and any(flag in dev[\"device_name\"] for flag in INSPUR_FPGA_FLAGS)\n    )\n\n    for fpga_dict in fpgas:\n        traits \u003d get_traits(fpga_dict[\"vendor_id\"], fpga_dict[\"device_id\"])\n        fpga_dict[\"rc\"] \u003d constants.RESOURCES[\"FPGA\"]\n        fpga_dict.update(traits)\n        fpga_list.append(_generate_driver_device(fpga_dict))\n\n    return fpga_list\n```\n\nthat allows use to defer much of the disign decisn till later while still consolidating on a comm set of function and normalised dicts instead of raw srings or stdout\n\nits less stict then i woudl eventually like but its much beter then we have today\n\nevenutlly i woudl like to move away form driver knowing baout the regexes or the fact that lspci is used entrily and this will get us part of the way there.","commit_id":"d9327e31f15f9c3acf1f7b26f78fb06e2f1b38b6"},{"author":{"_account_id":4690,"name":"melanie witt","display_name":"melwitt","email":"melwittt@gmail.com","username":"melwitt"},"change_message_id":"a4a607ee76d6b43f162245b7ec34d7fa4cda9d43","unresolved":true,"context_lines":[{"line_number":152,"context_line":"    return processutils.execute(\u0027lspci\u0027, \u0027-nn\u0027, \u0027-D\u0027)[0]"},{"line_number":153,"context_line":""},{"line_number":154,"context_line":""},{"line_number":155,"context_line":"def has_flags("},{"line_number":156,"context_line":"    flags: collections.abc.Iterable[str],"},{"line_number":157,"context_line":"    reducer: collections.abc.Callable["},{"line_number":158,"context_line":"        [collections.abc.Iterable[bool]], bool"},{"line_number":159,"context_line":"    ] \u003d any,"},{"line_number":160,"context_line":") -\u003e collections.abc.Callable[[str], bool]:"},{"line_number":161,"context_line":"    \"\"\"Return a predicate checking if a line matches flags using a reducer.\"\"\""},{"line_number":162,"context_line":"    return lambda line: reducer(flag in line for flag in flags)"},{"line_number":163,"context_line":""},{"line_number":164,"context_line":""},{"line_number":165,"context_line":"def has_vendor(vendor_id: str) -\u003e collections.abc.Callable[[str], bool]:"},{"line_number":166,"context_line":"    \"\"\"Return a predicate checking if a vendor ID exists in a line.\"\"\""},{"line_number":167,"context_line":"    return lambda line: vendor_id in line"},{"line_number":168,"context_line":""},{"line_number":169,"context_line":""},{"line_number":170,"context_line":"def get_pci_devices("},{"line_number":171,"context_line":"    *predicates: collections.abc.Callable[[str], bool],"},{"line_number":172,"context_line":") -\u003e collections.abc.Iterator[str]:"},{"line_number":173,"context_line":"    \"\"\"Yield PCI lines that satisfy all provided predicate functions.\"\"\""},{"line_number":174,"context_line":"    for pci in lspci_privileged().splitlines():"},{"line_number":175,"context_line":"        if all(predicate(pci) for predicate in predicates):"},{"line_number":176,"context_line":"            yield pci"}],"source_content_type":"text/x-python","patch_set":6,"id":"5d2be88c_a30799da","line":176,"range":{"start_line":155,"start_character":0,"end_line":176,"end_character":21},"in_reply_to":"6d9ee37e_37f7125f","updated":"2026-07-31 16:44:46.000000000","message":"I dunno ... I think we will have to agree to disagree on this one.\n\nTo me, this gives the impression of a \"plugin\" or \"hook\" type of design where you can insert a contract-less free for all logic into these predicate points. And at the same time the logic is a black box that is hard to read or reason about.\n\nIt\u0027s not my intention to block this but I can\u0027t give it an honest +1 from my side.","commit_id":"d9327e31f15f9c3acf1f7b26f78fb06e2f1b38b6"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"8b44dc4bb20eb7ac3f695c1513c9e57173f4a080","unresolved":true,"context_lines":[{"line_number":152,"context_line":"    return processutils.execute(\u0027lspci\u0027, \u0027-nn\u0027, \u0027-D\u0027)[0]"},{"line_number":153,"context_line":""},{"line_number":154,"context_line":""},{"line_number":155,"context_line":"def has_flags("},{"line_number":156,"context_line":"    flags: collections.abc.Iterable[str],"},{"line_number":157,"context_line":"    reducer: collections.abc.Callable["},{"line_number":158,"context_line":"        [collections.abc.Iterable[bool]], bool"},{"line_number":159,"context_line":"    ] \u003d any,"},{"line_number":160,"context_line":") -\u003e collections.abc.Callable[[str], bool]:"},{"line_number":161,"context_line":"    \"\"\"Return a predicate checking if a line matches flags using a reducer.\"\"\""},{"line_number":162,"context_line":"    return lambda line: reducer(flag in line for flag in flags)"},{"line_number":163,"context_line":""},{"line_number":164,"context_line":""},{"line_number":165,"context_line":"def has_vendor(vendor_id: str) -\u003e collections.abc.Callable[[str], bool]:"},{"line_number":166,"context_line":"    \"\"\"Return a predicate checking if a vendor ID exists in a line.\"\"\""},{"line_number":167,"context_line":"    return lambda line: vendor_id in line"},{"line_number":168,"context_line":""},{"line_number":169,"context_line":""},{"line_number":170,"context_line":"def get_pci_devices("},{"line_number":171,"context_line":"    *predicates: collections.abc.Callable[[str], bool],"},{"line_number":172,"context_line":") -\u003e collections.abc.Iterator[str]:"},{"line_number":173,"context_line":"    \"\"\"Yield PCI lines that satisfy all provided predicate functions.\"\"\""},{"line_number":174,"context_line":"    for pci in lspci_privileged().splitlines():"},{"line_number":175,"context_line":"        if all(predicate(pci) for predicate in predicates):"},{"line_number":176,"context_line":"            yield pci"}],"source_content_type":"text/x-python","patch_set":6,"id":"6d9ee37e_37f7125f","line":176,"range":{"start_line":155,"start_character":0,"end_line":176,"end_character":21},"in_reply_to":"85705c18_d53ec7f1","updated":"2026-07-31 15:29:31.000000000","message":"just ot be clear im not sugggesting we do any of this large  refacto to type in this bugfix series  but it is my intent to propsoe a spec for the pciBusManager work next cycle and my hope is when we do that change the driver will noloner operat on raw sting but the types\n\nto the predecates im expectign to have at that point will operated on instance or the PciDevice class\n\nlong term we may even remove the useage of lspci entrily adn just use /sys directly but the reason for haveing the PciBusManager is to encapulte the logic for how we enumerate and interact with pci device on the system while the driver can focus on higher level object of which device shoudl we orshtrea  and how they can be used.\n\ni.e. has this device been allow via config or the api to be managed by the driver is a driver policy decsion, doe a device exist is a PciBusManager reposniblity.\n\nfor the scope of the wishlist bug we jsut want to deduplate the code as much as posible to reduce the maintance cost but i would like to do so while reducing the amount of rework we will need to do in the future.\n\n\nif we really want to defer the more general predicate inteface to the PCIBusManger feature design we could but to me this is a pretty clean approch tough it will need to be reworked in the futre regardess of the approch we take.","commit_id":"d9327e31f15f9c3acf1f7b26f78fb06e2f1b38b6"},{"author":{"_account_id":39344,"name":"Gihong Lee","display_name":"gamio","email":"gh9231@gmail.com","username":"gamio"},"change_message_id":"ddf1b68d195e7742418949f1b6da76d45bf77969","unresolved":true,"context_lines":[{"line_number":152,"context_line":"    return processutils.execute(\u0027lspci\u0027, \u0027-nn\u0027, \u0027-D\u0027)[0]"},{"line_number":153,"context_line":""},{"line_number":154,"context_line":""},{"line_number":155,"context_line":"def has_flags("},{"line_number":156,"context_line":"    flags: collections.abc.Iterable[str],"},{"line_number":157,"context_line":"    reducer: collections.abc.Callable["},{"line_number":158,"context_line":"        [collections.abc.Iterable[bool]], bool"},{"line_number":159,"context_line":"    ] \u003d any,"},{"line_number":160,"context_line":") -\u003e collections.abc.Callable[[str], bool]:"},{"line_number":161,"context_line":"    \"\"\"Return a predicate checking if a line matches flags using a reducer.\"\"\""},{"line_number":162,"context_line":"    return lambda line: reducer(flag in line for flag in flags)"},{"line_number":163,"context_line":""},{"line_number":164,"context_line":""},{"line_number":165,"context_line":"def has_vendor(vendor_id: str) -\u003e collections.abc.Callable[[str], bool]:"},{"line_number":166,"context_line":"    \"\"\"Return a predicate checking if a vendor ID exists in a line.\"\"\""},{"line_number":167,"context_line":"    return lambda line: vendor_id in line"},{"line_number":168,"context_line":""},{"line_number":169,"context_line":""},{"line_number":170,"context_line":"def get_pci_devices("},{"line_number":171,"context_line":"    *predicates: collections.abc.Callable[[str], bool],"},{"line_number":172,"context_line":") -\u003e collections.abc.Iterator[str]:"},{"line_number":173,"context_line":"    \"\"\"Yield PCI lines that satisfy all provided predicate functions.\"\"\""},{"line_number":174,"context_line":"    for pci in lspci_privileged().splitlines():"},{"line_number":175,"context_line":"        if all(predicate(pci) for predicate in predicates):"},{"line_number":176,"context_line":"            yield pci"}],"source_content_type":"text/x-python","patch_set":6,"id":"5d0dc7e1_6610c7fd","line":176,"range":{"start_line":155,"start_character":0,"end_line":176,"end_character":21},"in_reply_to":"90b2c42b_99f92b93","updated":"2026-08-01 07:48:24.000000000","message":"Reworked the series so drivers no longer re-parse lspci. `get_pci_devices()` now yields normalized dicts from a single `parse_lspci_line()`, and each driver filters those dicts and drops its own regex. This removes the divergent parsing and could close the dedup goal in 2152545. Happy to hear other opinions before this goes further.","commit_id":"d9327e31f15f9c3acf1f7b26f78fb06e2f1b38b6"},{"author":{"_account_id":39344,"name":"Gihong Lee","display_name":"gamio","email":"gh9231@gmail.com","username":"gamio"},"change_message_id":"af50a606a53513e95187a7c043195537bfb164d9","unresolved":true,"context_lines":[{"line_number":152,"context_line":"    return processutils.execute(\u0027lspci\u0027, \u0027-nn\u0027, \u0027-D\u0027)[0]"},{"line_number":153,"context_line":""},{"line_number":154,"context_line":""},{"line_number":155,"context_line":"def has_flags("},{"line_number":156,"context_line":"    flags: collections.abc.Iterable[str],"},{"line_number":157,"context_line":"    reducer: collections.abc.Callable["},{"line_number":158,"context_line":"        [collections.abc.Iterable[bool]], bool"},{"line_number":159,"context_line":"    ] \u003d any,"},{"line_number":160,"context_line":") -\u003e collections.abc.Callable[[str], bool]:"},{"line_number":161,"context_line":"    \"\"\"Return a predicate checking if a line matches flags using a reducer.\"\"\""},{"line_number":162,"context_line":"    return lambda line: reducer(flag in line for flag in flags)"},{"line_number":163,"context_line":""},{"line_number":164,"context_line":""},{"line_number":165,"context_line":"def has_vendor(vendor_id: str) -\u003e collections.abc.Callable[[str], bool]:"},{"line_number":166,"context_line":"    \"\"\"Return a predicate checking if a vendor ID exists in a line.\"\"\""},{"line_number":167,"context_line":"    return lambda line: vendor_id in line"},{"line_number":168,"context_line":""},{"line_number":169,"context_line":""},{"line_number":170,"context_line":"def get_pci_devices("},{"line_number":171,"context_line":"    *predicates: collections.abc.Callable[[str], bool],"},{"line_number":172,"context_line":") -\u003e collections.abc.Iterator[str]:"},{"line_number":173,"context_line":"    \"\"\"Yield PCI lines that satisfy all provided predicate functions.\"\"\""},{"line_number":174,"context_line":"    for pci in lspci_privileged().splitlines():"},{"line_number":175,"context_line":"        if all(predicate(pci) for predicate in predicates):"},{"line_number":176,"context_line":"            yield pci"}],"source_content_type":"text/x-python","patch_set":6,"id":"90b2c42b_99f92b93","line":176,"range":{"start_line":155,"start_character":0,"end_line":176,"end_character":21},"in_reply_to":"aa69a491_7bcbae5b","updated":"2026-08-01 06:29:15.000000000","message":"Agree the real dedup is the shared parsing, not just the lspci call. I\u0027ll add `PCI_DEVICE_PATTERN` and `parse_lspci_line()` so `get_pci_devices()` yields normalized dicts, then drop the predicate helpers and migrate each driver to filter the dicts and remove its own regex. That delivers the consolidation that actually closes 2152545 and removes the opaque predicate contract. I\u0027ll keep the typed `PciDevice` and `PciBusManager` work for the spec next cycle.","commit_id":"d9327e31f15f9c3acf1f7b26f78fb06e2f1b38b6"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"b0f2d7a8c04141f596cca42931d0b5ca0102e975","unresolved":false,"context_lines":[{"line_number":170,"context_line":"    if not match:"},{"line_number":171,"context_line":"        return None"},{"line_number":172,"context_line":"    data \u003d match.groupdict()"},{"line_number":173,"context_line":"    data[\"raw_line\"] \u003d line"},{"line_number":174,"context_line":"    for key in (\"vendor_id\", \"device_id\", \"class_id\", \"revision\"):"},{"line_number":175,"context_line":"        if data.get(key):"},{"line_number":176,"context_line":"            data[key] \u003d data[key].lower()"}],"source_content_type":"text/x-python","patch_set":7,"id":"37bdf063_0d964d8b","line":173,"range":{"start_line":173,"start_character":4,"end_line":173,"end_character":27},"updated":"2026-08-04 15:13:14.000000000","message":"eventually i think we will want to drop this but for right now it gives us an escape hatch if we need it so im fine with this for now.","commit_id":"1ac482dc87c463752626a18f927b996a2da48acd"}]}
