Options for lspci

This commit is contained in:
Lucidiot 2019-07-06 16:57:25 +00:00
parent 7bda6b9ac7
commit 7a4e85830c
4 changed files with 255 additions and 10 deletions

65
pylspci/command.py Normal file
View File

@ -0,0 +1,65 @@
from enum import Enum
from typing import Optional, Union, List, Mapping, Any
from pathlib import Path
import subprocess
OptionalPath = Optional[Union[str, Path]]
class IDResolveOption(Enum):
NameOnly = ''
IDOnly = '-n'
Both = '-nn'
def lspci(
pciids: OptionalPath = None,
pcimap: OptionalPath = None,
access_method: Optional[str] = None,
pcilib_params: Mapping[str, Any] = {},
file: OptionalPath = None,
verbose: bool = False,
kernel_drivers: bool = False,
hide_single_domain: bool = True,
id_resolve_option: IDResolveOption = IDResolveOption.Both,
) -> str:
args: List[str] = ['lspci', '-mm']
if verbose:
args.append('-vvv')
if kernel_drivers:
args.append('-k')
if not hide_single_domain:
args.append('-D')
if access_method:
args.append('-A{}'.format(access_method))
if id_resolve_option != IDResolveOption.NameOnly:
args.append(id_resolve_option.value)
if pciids:
args.append('-i')
if not isinstance(pciids, Path):
pciids = Path(pciids)
assert pciids.is_file(), 'ID database file not found'
args.append(str(pciids.absolute()))
if pcimap:
args.append('-p')
if not isinstance(pcimap, Path):
pcimap = Path(pcimap)
assert pcimap.is_file(), 'Kernel module mapping file not found'
args.append(str(pcimap.absolute()))
if file:
args.append('-F')
if not isinstance(file, Path):
file = Path(file)
assert file.is_file(), 'Hex dump file not found'
args.append(str(file.absolute()))
for key, value in pcilib_params.items():
args.append('-O{}={}'.format(key, value))
return subprocess.check_output(
args,
universal_newlines=True,
)

View File

@ -1,10 +1,10 @@
from typing import Union, List
from cached_property import cached_property
from pylspci.command import lspci
from pylspci.fields import hexstring, Slot, NameWithID
from pylspci.device import Device
import argparse
import shlex
import subprocess
class SimpleFormatParser(object):
@ -58,8 +58,5 @@ class SimpleFormatParser(object):
def from_lspci(self) -> List[Device]:
return list(map(
self.parse,
subprocess.check_output(
['lspci', '-nnmm'],
universal_newlines=True,
).splitlines(),
lspci().splitlines(),
))

View File

@ -0,0 +1,186 @@
from unittest import TestCase
from unittest.mock import patch, call, MagicMock
from pylspci.command import lspci, IDResolveOption
class TestCommand(TestCase):
@patch('pylspci.command.subprocess.check_output')
def test_default(self, cmd_mock: MagicMock) -> None:
cmd_mock.return_value = 'something'
self.assertEqual(lspci(), 'something')
self.assertEqual(cmd_mock.call_count, 1)
self.assertEqual(cmd_mock.call_args, call(
['lspci', '-mm', '-nn'], universal_newlines=True,
))
@patch('pylspci.command.Path.is_file')
@patch('pylspci.command.subprocess.check_output')
def test_pciids(self, cmd_mock: MagicMock, is_file_mock: MagicMock):
cmd_mock.return_value = 'something'
is_file_mock.return_value = True
self.assertEqual(lspci(pciids='/somewhere'), 'something')
self.assertEqual(cmd_mock.call_count, 1)
self.assertEqual(cmd_mock.call_args, call(
['lspci', '-mm', '-nn', '-i', '/somewhere'],
universal_newlines=True,
))
self.assertEqual(is_file_mock.call_count, 1)
@patch('pylspci.command.Path.is_file')
def test_pciids_missing(self, is_file_mock: MagicMock):
is_file_mock.return_value = False
with self.assertRaises(AssertionError):
lspci(pciids='/nowhere')
self.assertEqual(is_file_mock.call_count, 1)
@patch('pylspci.command.Path.is_file')
@patch('pylspci.command.subprocess.check_output')
def test_pcimap(self, cmd_mock: MagicMock, is_file_mock: MagicMock):
cmd_mock.return_value = 'something'
is_file_mock.return_value = True
self.assertEqual(lspci(pcimap='/somewhere'), 'something')
self.assertEqual(cmd_mock.call_count, 1)
self.assertEqual(cmd_mock.call_args, call(
['lspci', '-mm', '-nn', '-p', '/somewhere'],
universal_newlines=True,
))
self.assertEqual(is_file_mock.call_count, 1)
@patch('pylspci.command.Path.is_file')
def test_pcimap_missing(self, is_file_mock: MagicMock):
is_file_mock.return_value = False
with self.assertRaises(AssertionError):
lspci(pcimap='/nowhere')
self.assertEqual(is_file_mock.call_count, 1)
@patch('pylspci.command.subprocess.check_output')
def test_access_method(self, cmd_mock: MagicMock) -> None:
cmd_mock.return_value = 'something'
self.assertEqual(lspci(access_method='somemethod'), 'something')
self.assertEqual(cmd_mock.call_count, 1)
self.assertEqual(cmd_mock.call_args, call(
['lspci', '-mm', '-Asomemethod', '-nn'], universal_newlines=True,
))
@patch('pylspci.command.subprocess.check_output')
def test_pcilib_params(self, cmd_mock: MagicMock) -> None:
cmd_mock.return_value = 'something'
self.assertEqual(lspci(pcilib_params={'a': 'b', 'c': 2}), 'something')
self.assertEqual(cmd_mock.call_count, 1)
self.assertEqual(cmd_mock.call_args, call(
['lspci', '-mm', '-nn', '-Oa=b', '-Oc=2'],
universal_newlines=True,
))
@patch('pylspci.command.Path.is_file')
@patch('pylspci.command.subprocess.check_output')
def test_file(self, cmd_mock: MagicMock, is_file_mock: MagicMock):
cmd_mock.return_value = 'something'
is_file_mock.return_value = True
self.assertEqual(lspci(file='/somewhere'), 'something')
self.assertEqual(cmd_mock.call_count, 1)
self.assertEqual(cmd_mock.call_args, call(
['lspci', '-mm', '-nn', '-F', '/somewhere'],
universal_newlines=True,
))
self.assertEqual(is_file_mock.call_count, 1)
@patch('pylspci.command.Path.is_file')
def test_file_missing(self, is_file_mock: MagicMock):
is_file_mock.return_value = False
with self.assertRaises(AssertionError):
lspci(file='/nowhere')
self.assertEqual(is_file_mock.call_count, 1)
@patch('pylspci.command.subprocess.check_output')
def test_verbose(self, cmd_mock: MagicMock) -> None:
cmd_mock.return_value = 'something'
self.assertEqual(lspci(verbose=True), 'something')
self.assertEqual(cmd_mock.call_count, 1)
self.assertEqual(cmd_mock.call_args, call(
['lspci', '-mm', '-vvv', '-nn'],
universal_newlines=True,
))
@patch('pylspci.command.subprocess.check_output')
def test_kernel_drivers(self, cmd_mock: MagicMock) -> None:
cmd_mock.return_value = 'something'
self.assertEqual(lspci(kernel_drivers=True), 'something')
self.assertEqual(cmd_mock.call_count, 1)
self.assertEqual(cmd_mock.call_args, call(
['lspci', '-mm', '-k', '-nn'],
universal_newlines=True,
))
@patch('pylspci.command.subprocess.check_output')
def test_hide_single_domain(self, cmd_mock: MagicMock) -> None:
cmd_mock.return_value = 'something'
self.assertEqual(lspci(hide_single_domain=False), 'something')
self.assertEqual(cmd_mock.call_count, 1)
self.assertEqual(cmd_mock.call_args, call(
['lspci', '-mm', '-D', '-nn'],
universal_newlines=True,
))
@patch('pylspci.command.subprocess.check_output')
def test_id_resolve_option_id_only(self, cmd_mock: MagicMock) -> None:
cmd_mock.return_value = 'something'
self.assertEqual(
lspci(id_resolve_option=IDResolveOption.IDOnly),
'something',
)
self.assertEqual(cmd_mock.call_count, 1)
self.assertEqual(cmd_mock.call_args, call(
['lspci', '-mm', '-n'],
universal_newlines=True,
))
@patch('pylspci.command.subprocess.check_output')
def test_id_resolve_option_name_only(self, cmd_mock: MagicMock) -> None:
cmd_mock.return_value = 'something'
self.assertEqual(
lspci(id_resolve_option=IDResolveOption.NameOnly),
'something',
)
self.assertEqual(cmd_mock.call_count, 1)
self.assertEqual(cmd_mock.call_args, call(
['lspci', '-mm'],
universal_newlines=True,
))
@patch('pylspci.command.Path.is_file')
@patch('pylspci.command.subprocess.check_output')
def test_everything(self, cmd_mock: MagicMock, is_file_mock: MagicMock):
cmd_mock.return_value = 'something'
is_file_mock.return_value = True
self.assertEqual(lspci(
pciids='/pciids',
pcimap='/pcimap',
access_method='somemethod',
pcilib_params={'a': 'b', 'c': 42},
file='/file',
verbose=True,
kernel_drivers=True,
hide_single_domain=False,
id_resolve_option=IDResolveOption.IDOnly,
), 'something')
self.assertEqual(cmd_mock.call_count, 1)
self.assertEqual(cmd_mock.call_args, call(
['lspci',
'-mm',
'-vvv',
'-k',
'-D',
'-Asomemethod',
'-n',
'-i', '/pciids',
'-p', '/pcimap',
'-F', '/file',
'-Oa=b',
'-Oc=42'],
universal_newlines=True,
))
self.assertEqual(is_file_mock.call_count, 3)

View File

@ -74,7 +74,7 @@ class TestSimpleFormatParser(TestCase):
self.assertIsNone(dev.revision)
self.assertIsNone(dev.progif)
@patch('pylspci.simple_parser.subprocess.check_output')
@patch('pylspci.simple_parser.lspci')
def test_command(self, cmd_mock: MagicMock) -> None:
cmd_mock.return_value = \
'00:1c.3 "PCI bridge [0604]" "Intel Corporation [8086]" ' \
@ -87,7 +87,4 @@ class TestSimpleFormatParser(TestCase):
self._check_device(devices[1])
self.assertEqual(cmd_mock.call_count, 1)
self.assertEqual(cmd_mock.call_args, call(
['lspci', '-nnmm'],
universal_newlines=True,
))
self.assertEqual(cmd_mock.call_args, call())