Compare commits
36 Commits
Author | SHA1 | Date |
---|---|---|
Lucidiot | 1f7cac1d8c | |
Lucidiot | ac6a1b3103 | |
~lucidiot | 04e3354394 | |
~lucidiot | 91918e8d41 | |
~lucidiot | e3a89613e1 | |
~lucidiot | 4438f17e94 | |
~lucidiot | 70b155cf04 | |
~lucidiot | d6becffcc0 | |
~lucidiot | 27cc5de1b9 | |
~lucidiot | 1936c58dec | |
~lucidiot | 544edd0b3d | |
~lucidiot | da39b09ef5 | |
~lucidiot | 149b3bde99 | |
~lucidiot | ad52e4c823 | |
~lucidiot | 30e1ed5ec4 | |
~lucidiot | 7cebd6202d | |
~lucidiot | 57d96ab345 | |
~lucidiot | 386b7fc326 | |
~lucidiot | 587a55af73 | |
~lucidiot | 96cbe0e72c | |
~lucidiot | 09256806f1 | |
Lucidiot | 27695dd747 | |
Lucidiot | 0b00a2e586 | |
Lucidiot | 06906411a1 | |
Jan Lützler | 9f90cf8519 | |
Lucidiot | 7778e1a48a | |
Lucidiot | 51aac8692d | |
Lucidiot | 11c57ae0f1 | |
Lucidiot | 7da6c0756c | |
Lucidiot | 45dfdd7277 | |
Lucidiot | a34c08c9ce | |
Lucidiot | ce1abd76d0 | |
Lucidiot | 0b06017ab7 | |
Lucidiot | 92417f1b6a | |
Lucidiot | 65053cfdba | |
Lucidiot | 3b93a30fb4 |
|
@ -0,0 +1,120 @@
|
|||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: default
|
||||
|
||||
steps:
|
||||
- name: pre-commit
|
||||
image: python:3-alpine
|
||||
commands:
|
||||
- apk add --no-cache git gcc musl-dev
|
||||
- pip install .[dev]
|
||||
- pre-commit run -a
|
||||
|
||||
- name: test-py36
|
||||
image: python:3.6-alpine
|
||||
commands:
|
||||
- pip install .[dev]
|
||||
- coverage run setup.py test
|
||||
- coverage report
|
||||
|
||||
- name: test-py37
|
||||
image: python:3.7-alpine
|
||||
commands:
|
||||
- pip install .[dev]
|
||||
- coverage run setup.py test
|
||||
- coverage report
|
||||
|
||||
- name: test-py38
|
||||
image: python:3.8-alpine
|
||||
commands:
|
||||
- pip install .[dev]
|
||||
- coverage run setup.py test
|
||||
- coverage report
|
||||
|
||||
- name: test-py39
|
||||
image: python:3.9-alpine
|
||||
commands:
|
||||
- pip install .[dev]
|
||||
- coverage run setup.py test
|
||||
- coverage report
|
||||
|
||||
- name: test-py310
|
||||
image: python:3.10-alpine
|
||||
commands:
|
||||
- pip install .[dev]
|
||||
- coverage run setup.py test
|
||||
- coverage report
|
||||
|
||||
- name: test-py311
|
||||
image: python:3.11-alpine
|
||||
commands:
|
||||
- pip install .[dev]
|
||||
- coverage run setup.py test
|
||||
- coverage report
|
||||
|
||||
- name: testpypi
|
||||
image: python:3.11-alpine
|
||||
commands:
|
||||
- pip install .[dev] twine setuptools wheel
|
||||
- |
|
||||
echo "[distutils]
|
||||
index-servers = testpypi
|
||||
[testpypi]
|
||||
repository=https://test.pypi.org/legacy/
|
||||
username=$$TESTPYPI_DEPLOY_USERNAME
|
||||
password=$$TESTPYPI_DEPLOY_PASSWORD" > ~/.pypirc
|
||||
- python setup.py sdist bdist_wheel
|
||||
- twine upload dist/* -r testpypi
|
||||
|
||||
when:
|
||||
event:
|
||||
- promote
|
||||
repo:
|
||||
- lucidiot/pylspci
|
||||
|
||||
depends_on:
|
||||
- pre-commit
|
||||
- test-py36
|
||||
- test-py37
|
||||
- test-py38
|
||||
- test-py39
|
||||
- test-py310
|
||||
- test-py311
|
||||
|
||||
environment:
|
||||
TESTPYPI_DEPLOY_USERNAME:
|
||||
from_secret: testpypi_username
|
||||
TESTPYPI_DEPLOY_PASSWORD:
|
||||
from_secret: testpypi_password
|
||||
|
||||
- name: pypi
|
||||
image: python:3.11-alpine
|
||||
commands:
|
||||
- pip install .[dev] twine setuptools wheel
|
||||
- |
|
||||
echo "[distutils]
|
||||
index-servers = pypi
|
||||
[pypi]
|
||||
repository=https://upload.pypi.org/legacy/
|
||||
username=$$PYPI_DEPLOY_USERNAME
|
||||
password=$$PYPI_DEPLOY_PASSWORD" > ~/.pypirc
|
||||
- python setup.py sdist bdist_wheel
|
||||
- twine upload dist/* -r pypi
|
||||
|
||||
when:
|
||||
event:
|
||||
- promote
|
||||
repo:
|
||||
- lucidiot/pylspci
|
||||
branch:
|
||||
- master
|
||||
|
||||
depends_on:
|
||||
- testpypi
|
||||
|
||||
environment:
|
||||
PYPI_DEPLOY_USERNAME:
|
||||
from_secret: pypi_username
|
||||
PYPI_DEPLOY_PASSWORD:
|
||||
from_secret: pypi_password
|
|
@ -1,97 +0,0 @@
|
|||
image: python:3.7
|
||||
stages:
|
||||
- test
|
||||
- deploy
|
||||
|
||||
variables:
|
||||
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
|
||||
|
||||
cache:
|
||||
paths:
|
||||
- .cache/pip
|
||||
- venv/
|
||||
|
||||
before_script:
|
||||
- pip install virtualenv
|
||||
- virtualenv venv
|
||||
- source venv/bin/activate
|
||||
- pip install .[dev]
|
||||
|
||||
tests:
|
||||
stage: test
|
||||
coverage: '/TOTAL[\s\d]+\s(\d+%)/'
|
||||
script:
|
||||
- coverage run setup.py test
|
||||
- coverage report
|
||||
- codecov
|
||||
|
||||
flake8:
|
||||
stage: test
|
||||
script:
|
||||
- flake8
|
||||
|
||||
mypy:
|
||||
stage: test
|
||||
script:
|
||||
- mypy .
|
||||
|
||||
doc8:
|
||||
stage: test
|
||||
script:
|
||||
- doc8
|
||||
|
||||
deploy-pypi:
|
||||
stage: deploy
|
||||
when: manual
|
||||
only:
|
||||
- master@Lucidiot/pylspci
|
||||
environment:
|
||||
name: pypi
|
||||
url: https://pypi.org/project/pylspci
|
||||
|
||||
script:
|
||||
- pip install twine setuptools wheel
|
||||
- echo "[distutils]" > ~/.pypirc
|
||||
- echo "index-servers =" >> ~/.pypirc
|
||||
- echo " pypi" >> ~/.pypirc
|
||||
- echo "[pypi]" >> ~/.pypirc
|
||||
- echo "repository=https://upload.pypi.org/legacy/" >> ~/.pypirc
|
||||
- echo "username=$PYPI_DEPLOY_USERNAME" >> ~/.pypirc
|
||||
- echo "password=$PYPI_DEPLOY_PASSWORD" >> ~/.pypirc
|
||||
- python setup.py sdist bdist_wheel
|
||||
- twine upload dist/* -r pypi
|
||||
|
||||
deploy-testpypi:
|
||||
stage: deploy
|
||||
when: manual
|
||||
only:
|
||||
- branches@Lucidiot/pylspci
|
||||
environment:
|
||||
name: testpypi
|
||||
url: https://test.pypi.org/project/pylspci
|
||||
|
||||
script:
|
||||
- pip install twine setuptools wheel
|
||||
- echo "[distutils]" > ~/.pypirc
|
||||
- echo "index-servers =" >> ~/.pypirc
|
||||
- echo " testpypi" >> ~/.pypirc
|
||||
- echo "[testpypi]" >> ~/.pypirc
|
||||
- echo "repository=https://test.pypi.org/legacy/" >> ~/.pypirc
|
||||
- echo "username=$PYPI_DEPLOY_USERNAME" >> ~/.pypirc
|
||||
- echo "password=$PYPI_DEPLOY_PASSWORD" >> ~/.pypirc
|
||||
- python setup.py sdist bdist_wheel
|
||||
- twine upload dist/* -r testpypi
|
||||
|
||||
pages:
|
||||
stage: deploy
|
||||
when: manual
|
||||
only:
|
||||
- master@Lucidiot/pylspci
|
||||
artifacts:
|
||||
paths:
|
||||
- public
|
||||
|
||||
script:
|
||||
- cd docs
|
||||
- make html
|
||||
- mv _build/html ../public
|
|
@ -0,0 +1,35 @@
|
|||
# See https://pre-commit.com for more information
|
||||
# See https://pre-commit.com/hooks.html for more hooks
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.4.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: check-yaml
|
||||
- id: check-added-large-files
|
||||
- id: check-merge-conflict
|
||||
- id: check-executables-have-shebangs
|
||||
- id: check-symlinks
|
||||
- repo: https://github.com/PyCQA/flake8
|
||||
rev: 6.0.0
|
||||
hooks:
|
||||
- id: flake8
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: v1.2.0
|
||||
hooks:
|
||||
- id: mypy
|
||||
args:
|
||||
- --ignore-missing-imports
|
||||
- --disallow-incomplete-defs
|
||||
- --disallow-untyped-defs
|
||||
- --check-untyped-defs
|
||||
- --no-implicit-optional
|
||||
- repo: https://github.com/PyCQA/doc8
|
||||
rev: v1.1.1
|
||||
hooks:
|
||||
- id: doc8
|
||||
- repo: https://github.com/PyCQA/isort
|
||||
rev: 5.12.0
|
||||
hooks:
|
||||
- id: isort
|
|
@ -0,0 +1,6 @@
|
|||
include requirements.txt
|
||||
include requirements-dev.txt
|
||||
include VERSION
|
||||
include LICENSE
|
||||
include README.rst
|
||||
include pylspci/py.typed
|
|
@ -5,4 +5,4 @@ A Python parser for the ``lspci`` command from the pciutils_ package.
|
|||
`Browse documentation`_
|
||||
|
||||
.. _pciutils: http://mj.ucw.cz/sw/pciutils/
|
||||
.. _Browse documentation: https://lucidiot.gitlab.io/pylspci/
|
||||
.. _Browse documentation: https://lucidiot.tildepages.org/pylspci/
|
||||
|
|
|
@ -16,4 +16,4 @@ help:
|
|||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
|
|
@ -130,4 +130,3 @@ PCI access
|
|||
``-H2``
|
||||
Access hardware using Intel configuration mechanism 2.
|
||||
Alias to ``-A intel-conf2``.
|
||||
|
||||
|
|
39
docs/conf.py
39
docs/conf.py
|
@ -14,13 +14,14 @@
|
|||
#
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.abspath('..'))
|
||||
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = 'pylspci'
|
||||
copyright = '2019, Lucidiot and contributors'
|
||||
copyright = '2022, Lucidiot and contributors'
|
||||
author = 'Lucidiot and contributors'
|
||||
|
||||
# The short X.Y version
|
||||
|
@ -61,7 +62,7 @@ master_doc = 'index'
|
|||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = None
|
||||
language = 'en'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
|
@ -112,23 +113,23 @@ htmlhelp_basename = 'pylspcidoc'
|
|||
|
||||
# -- Options for LaTeX output ------------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#
|
||||
# 'preamble': '',
|
||||
|
||||
# Latex figure (float) alignment
|
||||
#
|
||||
# 'figure_align': 'htbp',
|
||||
}
|
||||
# latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#
|
||||
# 'papersize': 'letterpaper',
|
||||
#
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#
|
||||
# 'pointsize': '10pt',
|
||||
#
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#
|
||||
# 'preamble': '',
|
||||
#
|
||||
# Latex figure (float) alignment
|
||||
#
|
||||
# 'figure_align': 'htbp',
|
||||
# }
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
|
|
|
@ -6,8 +6,8 @@ Contributions to the project are greatly appreciated.
|
|||
Bugs and suggestions
|
||||
--------------------
|
||||
|
||||
You may `submit an issue`_ to GitLab to warn of any bugs, ask for new features,
|
||||
or ask any questions that are not answered in this documentation.
|
||||
You may `submit an issue`_ to the Gitea repository to warn of any bugs, ask for
|
||||
new features, or ask any questions that are not answered in this documentation.
|
||||
|
||||
When reporting a bug, do not forget to put in your version of Python and your
|
||||
version of *pylspci*. This will greatly help when troubleshooting, as most
|
||||
|
@ -22,7 +22,7 @@ Setup
|
|||
You will need a virtual envionment to work properly. `virtualenvwrapper`_ is
|
||||
recommended::
|
||||
|
||||
git clone https://gitlab.com/Lucidiot/pylspci
|
||||
git clone https://tildegit.org/lucidiot/pylspci.git
|
||||
cd pylspci
|
||||
mkvirtualenv -a . pylspci
|
||||
pip install -e .[dev]
|
||||
|
@ -44,8 +44,8 @@ Tests coverage
|
|||
|
||||
I aim for 100% coverage on all of my Python packages whenever I add unit
|
||||
tests to them; this package is no exception. CI checks use the `coverage`_
|
||||
Python package and `codecov`_ to check for test coverage. To get test coverage
|
||||
data locally, run::
|
||||
Python package to get coverage statistics.
|
||||
To get test coverage data locally, run::
|
||||
|
||||
coverage run setup.py test
|
||||
|
||||
|
@ -60,7 +60,7 @@ offline using your favorite web browser and shows line by line coverage::
|
|||
|
||||
If you are having issues reaching 100% coverage, try to still add some tests,
|
||||
and mention your issues when creating a pull request to the
|
||||
`GitLab repository`_.
|
||||
`Gitea repository`_.
|
||||
|
||||
Linting
|
||||
^^^^^^^
|
||||
|
@ -69,19 +69,25 @@ The source code follows the PEP 8 code style and performs CI checks using the
|
|||
``flake8`` tool. To perform the same checks locally, run ``flake8`` on the root
|
||||
directory of this repository.
|
||||
|
||||
Type checking
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
The source code uses PEP 484 type hints and type checking is performed in CI
|
||||
using ``mypy``. To run those checks locally, run ``mypy .`` on the root
|
||||
directory of this repository.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
The documentation you are reading is generated by the `Sphinx`_ tool.
|
||||
The text files that hold the documentation's contents are written in
|
||||
`reStructuredText`_ and are available under the ``/docs`` folder of the
|
||||
`GitLab repository`_.
|
||||
`Gitea repository`_.
|
||||
They are also subject to linting using the ``doc8`` tool.
|
||||
|
||||
.. _submit an issue: https://gitlab.com/Lucidiot/pylspci/issues/new
|
||||
.. _submit an issue: https://tildegit.org/lucidiot/pylspci/issues/new
|
||||
.. _virtualenvwrapper: https://virtualenvwrapper.readthedocs.io
|
||||
.. _coverage: https://coverage.readthedocs.io/
|
||||
.. _codecov: https://codecov.io/gl/Lucidiot/pylspci
|
||||
.. _GitLab repository: https://gitlab.com/Lucidiot/pylspci
|
||||
.. _Gitea repository: https://tildegit.org/lucidiot/pylspci
|
||||
.. _Sphinx: http://www.sphinx-doc.org/
|
||||
.. _reStructuredText: http://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html
|
||||
|
|
|
@ -18,20 +18,11 @@ Python lspci parser
|
|||
.. image:: https://img.shields.io/pypi/status/pylspci.svg
|
||||
:target: https://pypi.org/project/pylspci
|
||||
|
||||
.. image:: https://gitlab.com/Lucidiot/pylspci/badges/master/pipeline.svg
|
||||
:target: https://gitlab.com/Lucidiot/pylspci/pipelines
|
||||
.. image:: https://drone.tildegit.org/api/badges/lucidiot/pylspci/status.svg
|
||||
:target: https://drone.tildegit.org/api/badges/lucidiot/pylspci/status.svg
|
||||
|
||||
.. image:: https://codecov.io/gl/Lucidiot/pylspci/branch/master/graph/badge.svg
|
||||
:target: https://codecov.io/gl/Lucidiot/pylspci
|
||||
|
||||
.. image:: https://requires.io/github/Lucidiot/pylspci/requirements.svg?branch=master
|
||||
:target: https://requires.io/github/Lucidiot/pylspci/requirements/?branch=master
|
||||
|
||||
.. image:: https://img.shields.io/github/last-commit/Lucidiot/pylspci.svg
|
||||
:target: https://gitlab.com/Lucidiot/pylspci/commits
|
||||
|
||||
.. image:: https://img.shields.io/badge/badge%20count-10-brightgreen.svg
|
||||
:target: https://gitlab.com/Lucidiot/pylspci
|
||||
.. image:: https://img.shields.io/badge/badge%20count-7-brightgreen.svg
|
||||
:target: https://tildegit.org/lucidiot/pylspci
|
||||
|
||||
A Python parser for the ``lspci`` command from the pciutils_ package.
|
||||
|
||||
|
@ -63,7 +54,7 @@ To parse ``lspci -nnmmvvvk``, use the
|
|||
.. code:: python
|
||||
|
||||
>>> from pylspci.parsers import SimpleParser
|
||||
>>> SimpleParser.run()
|
||||
>>> SimpleParser().run()
|
||||
[Device(slot=Slot('0000:00:01.3'), name=NameWithID('Name A [2420]'), ...),
|
||||
Device(slot=Slot('0000:00:01.4'), name=NameWithID('Name B [0e54]'), ...)]
|
||||
|
||||
|
@ -73,8 +64,8 @@ Custom arguments
|
|||
.. code:: python
|
||||
|
||||
>>> from pylspci.command import IDResolveOption
|
||||
>>> from pylspci import parser
|
||||
>>> parser.run(
|
||||
>>> from pylspci.parsers import VerboseParser
|
||||
>>> VerboseParser().run(
|
||||
... hide_single_domain=False,
|
||||
... id_resolve_option=IDResolveOption.NameOnly,
|
||||
... )
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
#!/usr/bin/env python3
|
||||
from pathlib import Path
|
||||
from typing import Optional, Dict, Any
|
||||
from pylspci.command import CommandBuilder, IDResolveOption
|
||||
from pylspci.filters import SlotFilter, DeviceFilter
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from pylspci.command import CommandBuilder, IDResolveOption
|
||||
from pylspci.filters import DeviceFilter, SlotFilter
|
||||
|
||||
|
||||
def get_parser() -> argparse.ArgumentParser:
|
||||
|
@ -160,8 +161,7 @@ def main() -> None:
|
|||
parser: argparse.ArgumentParser = get_parser()
|
||||
args: Dict[str, Any] = vars(parser.parse_args())
|
||||
|
||||
# Specific parsing required
|
||||
use_parser: bool = args.pop('json', True)
|
||||
json_output: bool = args.pop('json', True)
|
||||
kernel_modules: bool = args.pop('kernel_modules', False)
|
||||
access_method: Optional[str] = args.pop('access_method', None)
|
||||
pcilib_params = args.pop('pcilib_params', []) or []
|
||||
|
@ -178,7 +178,7 @@ def main() -> None:
|
|||
|
||||
for param in pcilib_params:
|
||||
if param.strip().lower() == 'help':
|
||||
builder = builder.list_pcilib_params(raw=not use_parser)
|
||||
builder = builder.list_pcilib_params(raw=not json_output)
|
||||
break
|
||||
if '=' not in param:
|
||||
parser.error(
|
||||
|
@ -186,24 +186,19 @@ def main() -> None:
|
|||
key, value = map(str.strip, param.split('=', 2))
|
||||
builder = builder.with_pcilib_params(**{key: value})
|
||||
|
||||
if use_parser:
|
||||
if json_output:
|
||||
builder = builder.with_default_parser()
|
||||
|
||||
result = list(builder)
|
||||
if not use_parser: # Raw mode
|
||||
if not json_output: # Raw mode
|
||||
for item in result:
|
||||
print(item)
|
||||
return
|
||||
|
||||
def _item_handler(item: Any) -> Any:
|
||||
if hasattr(item, '_asdict'):
|
||||
return item._asdict()
|
||||
return item
|
||||
|
||||
print(json.dumps(
|
||||
list(map(_item_handler, result)),
|
||||
default=vars,
|
||||
))
|
||||
print(json.dumps([
|
||||
item if isinstance(item, str) else item.as_dict()
|
||||
for item in result
|
||||
]))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import subprocess
|
||||
from enum import Enum
|
||||
from typing import \
|
||||
Optional, Union, List, Mapping, MutableMapping, Any, Iterator
|
||||
from pathlib import Path
|
||||
from typing import (
|
||||
Any, Iterator, List, Mapping, MutableMapping, Optional, Union
|
||||
)
|
||||
|
||||
from pylspci.device import Device
|
||||
from pylspci.fields import PCIAccessParameter
|
||||
from pylspci.filters import SlotFilter, DeviceFilter
|
||||
from pylspci.filters import DeviceFilter, SlotFilter
|
||||
from pylspci.parsers.base import Parser
|
||||
import subprocess
|
||||
|
||||
OptionalPath = Optional[Union[str, Path]]
|
||||
|
||||
|
@ -207,7 +209,7 @@ class CommandBuilder(object):
|
|||
_params: MutableMapping[str, Any] = {}
|
||||
_parser: Optional[Parser] = None
|
||||
|
||||
def __init__(self, **kwargs: Mapping[str, Any]):
|
||||
def __init__(self, **kwargs: Any):
|
||||
self._params = kwargs
|
||||
|
||||
def __iter__(self) -> Iterator[Union[str, Device, PCIAccessParameter]]:
|
||||
|
|
|
@ -1,5 +1,15 @@
|
|||
from typing import NamedTuple, Optional, List
|
||||
from pylspci.fields import Slot, NameWithID
|
||||
from typing import Dict, List, NamedTuple, Optional, Union
|
||||
|
||||
from pylspci.fields import NameWithID, NameWithIDDict, Slot, SlotDict
|
||||
|
||||
DeviceDict = Dict[str, Union[
|
||||
int,
|
||||
str,
|
||||
SlotDict,
|
||||
NameWithIDDict,
|
||||
List[str],
|
||||
None,
|
||||
]]
|
||||
|
||||
|
||||
class Device(NamedTuple):
|
||||
|
@ -10,90 +20,92 @@ class Device(NamedTuple):
|
|||
slot: Slot
|
||||
"""
|
||||
The device's slot (domain, bus, number and function).
|
||||
|
||||
:type: Slot
|
||||
"""
|
||||
|
||||
cls: NameWithID
|
||||
"""
|
||||
The device's class, with a name and/or an ID.
|
||||
|
||||
:type: NameWithID
|
||||
"""
|
||||
|
||||
vendor: NameWithID
|
||||
"""
|
||||
The device's vendor, with a name and/or an ID.
|
||||
|
||||
:type: NameWithID
|
||||
"""
|
||||
|
||||
device: NameWithID
|
||||
"""
|
||||
The device's name and/or ID.
|
||||
|
||||
:type: NameWithID
|
||||
"""
|
||||
|
||||
subsystem_vendor: Optional[NameWithID] = None
|
||||
"""
|
||||
The device's subsystem vendor, if found, with a name and/or an ID.
|
||||
|
||||
:type: NameWithID or None
|
||||
"""
|
||||
|
||||
subsystem_device: Optional[NameWithID] = None
|
||||
"""
|
||||
The device's subsystem name and/or ID, if found.
|
||||
|
||||
:type: NameWithID or None
|
||||
"""
|
||||
|
||||
revision: Optional[int] = None
|
||||
"""
|
||||
The device's revision number.
|
||||
|
||||
:type: int or None
|
||||
"""
|
||||
|
||||
progif: Optional[int] = None
|
||||
"""
|
||||
The device's programming interface number.
|
||||
|
||||
:type: int or None
|
||||
"""
|
||||
|
||||
driver: Optional[str] = None
|
||||
"""
|
||||
The device's driver (Linux only).
|
||||
|
||||
:type: str or None
|
||||
"""
|
||||
|
||||
kernel_modules: List[str] = []
|
||||
"""
|
||||
One or more kernel modules that can handle this device (Linux only).
|
||||
|
||||
:type: List[str] or None
|
||||
"""
|
||||
|
||||
numa_node: Optional[int] = None
|
||||
"""
|
||||
NUMA node this device is connected to (Linux only).
|
||||
|
||||
:type: int or None
|
||||
"""
|
||||
|
||||
iommu_group: Optional[int] = None
|
||||
"""
|
||||
IOMMU group that this device is part of (optional, Linux only).
|
||||
|
||||
:type: int or None
|
||||
"""
|
||||
|
||||
physical_slot: Optional[int] = None
|
||||
physical_slot: Optional[str] = None
|
||||
"""
|
||||
The device's physical slot number (Linux only).
|
||||
|
||||
:type: int or None
|
||||
"""
|
||||
|
||||
def as_dict(self) -> DeviceDict:
|
||||
"""
|
||||
Serialize this device as a JSON-serializable `dict`.
|
||||
"""
|
||||
return {
|
||||
"slot": self.slot.as_dict(),
|
||||
"cls": self.cls.as_dict(),
|
||||
"vendor": self.vendor.as_dict(),
|
||||
"device": self.device.as_dict(),
|
||||
"subsystem_vendor": (
|
||||
self.subsystem_vendor.as_dict()
|
||||
if self.subsystem_vendor
|
||||
else None
|
||||
),
|
||||
"subsystem_device": (
|
||||
self.subsystem_device.as_dict()
|
||||
if self.subsystem_device
|
||||
else None
|
||||
),
|
||||
"revision": self.revision,
|
||||
"progif": self.progif,
|
||||
"driver": self.driver,
|
||||
"kernel_modules": self.kernel_modules,
|
||||
"numa_node": self.numa_node,
|
||||
"iommu_group": self.iommu_group,
|
||||
"physical_slot": self.physical_slot,
|
||||
}
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
from functools import partial
|
||||
from typing import Optional, Any
|
||||
import re
|
||||
from functools import partial
|
||||
from typing import Any, Dict, Optional, Union
|
||||
|
||||
# mypy does not support recursive type definitions
|
||||
# SlotDict = Dict[str, Union[int, 'SlotDict', None]]
|
||||
SlotDict = Dict[str, Union[int, Dict[str, Any], None]]
|
||||
NameWithIDDict = Dict[str, Union[int, str, None]]
|
||||
|
||||
hexstring = partial(int, base=16)
|
||||
|
||||
|
@ -18,36 +22,26 @@ class Slot(object):
|
|||
"""
|
||||
The slot's domain, as a four-digit hexadecimal number.
|
||||
When omitted, defaults to ``0x0000``.
|
||||
|
||||
:type: int
|
||||
"""
|
||||
|
||||
bus: int
|
||||
"""
|
||||
The slot's bus, as a two-digit hexadecimal number.
|
||||
|
||||
:type: int
|
||||
"""
|
||||
|
||||
device: int
|
||||
"""
|
||||
The slot's device, as a two-digit hexadecimal number, up to `0x1f`.
|
||||
|
||||
:type: int
|
||||
"""
|
||||
|
||||
function: int
|
||||
"""
|
||||
The slot's function, as a single octal digit.
|
||||
|
||||
:type: int
|
||||
"""
|
||||
|
||||
parent: Optional["Slot"] = None
|
||||
"""
|
||||
The slot's parent bridge, if present.
|
||||
|
||||
:type: Slot or None
|
||||
"""
|
||||
|
||||
def __init__(self, value: str) -> None:
|
||||
|
@ -76,6 +70,18 @@ class Slot(object):
|
|||
def __repr__(self) -> str:
|
||||
return '{}({!r})'.format(self.__class__.__name__, str(self))
|
||||
|
||||
def as_dict(self) -> SlotDict:
|
||||
"""
|
||||
Serialize this slot as a JSON-serializable `dict`.
|
||||
"""
|
||||
return {
|
||||
"domain": self.domain,
|
||||
"bus": self.bus,
|
||||
"device": self.device,
|
||||
"function": self.function,
|
||||
"parent": self.parent.as_dict() if self.parent else None,
|
||||
}
|
||||
|
||||
|
||||
class NameWithID(object):
|
||||
"""
|
||||
|
@ -86,15 +92,11 @@ class NameWithID(object):
|
|||
id: Optional[int]
|
||||
"""
|
||||
The PCI ID as a four-digit hexadecimal number.
|
||||
|
||||
:type: int or None
|
||||
"""
|
||||
|
||||
name: Optional[str]
|
||||
"""
|
||||
The human-readable name associated with this ID.
|
||||
|
||||
:type: str or None
|
||||
"""
|
||||
|
||||
_NAME_ID_REGEX = re.compile(r'^(?P<name>.+)\s\[(?P<id>[0-9a-fA-F]{4})\]$')
|
||||
|
@ -132,6 +134,15 @@ class NameWithID(object):
|
|||
def __repr__(self) -> str:
|
||||
return '{}({!r})'.format(self.__class__.__name__, str(self))
|
||||
|
||||
def as_dict(self) -> NameWithIDDict:
|
||||
"""
|
||||
Serialize this name and ID as a JSON-serializable `dict`.
|
||||
"""
|
||||
return {
|
||||
"id": self.id,
|
||||
"name": self.name,
|
||||
}
|
||||
|
||||
|
||||
class PCIAccessParameter(object):
|
||||
"""
|
||||
|
@ -143,22 +154,16 @@ class PCIAccessParameter(object):
|
|||
name: str
|
||||
"""
|
||||
The parameter's name.
|
||||
|
||||
:type: str
|
||||
"""
|
||||
|
||||
description: str
|
||||
"""
|
||||
A short description of the parameter's use.
|
||||
|
||||
:type: str
|
||||
"""
|
||||
|
||||
default: Optional[str]
|
||||
"""
|
||||
An optional default value for the parameter.
|
||||
|
||||
:type: str or None
|
||||
"""
|
||||
|
||||
_PARAM_REGEX = re.compile(
|
||||
|
@ -184,3 +189,13 @@ class PCIAccessParameter(object):
|
|||
return isinstance(other, PCIAccessParameter) and \
|
||||
(self.name, self.description, self.default) \
|
||||
== (other.name, other.description, other.default)
|
||||
|
||||
def as_dict(self) -> Dict[str, Optional[str]]:
|
||||
"""
|
||||
Serialize this PCI access parameter as a JSON-serializable `dict`.
|
||||
"""
|
||||
return {
|
||||
"name": self.name,
|
||||
"description": self.description,
|
||||
"default": self.default,
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from typing import Optional, Pattern, ClassVar, Dict, Type, TypeVar, Any
|
||||
from pylspci.fields import hexstring
|
||||
import re
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any, ClassVar, Dict, Optional, Pattern, Type, TypeVar
|
||||
|
||||
from pylspci.fields import hexstring
|
||||
|
||||
T = TypeVar('T', bound='Filter')
|
||||
|
||||
|
@ -42,29 +43,21 @@ class SlotFilter(Filter):
|
|||
domain: Optional[int] = None
|
||||
"""
|
||||
Device domain, as a four-digit hexadecimal number.
|
||||
|
||||
:type: int or None
|
||||
"""
|
||||
|
||||
bus: Optional[int] = None
|
||||
"""
|
||||
Device bus, as a two-digit hexadecimal number.
|
||||
|
||||
:type: int or None
|
||||
"""
|
||||
|
||||
device: Optional[int] = None
|
||||
"""
|
||||
Device number, as a two-digit hexadecimal number, up to `0x1f`.
|
||||
|
||||
:type: int or None
|
||||
"""
|
||||
|
||||
function: Optional[int] = None
|
||||
"""
|
||||
The slot's function, as a single octal digit.
|
||||
|
||||
:type: int or None
|
||||
"""
|
||||
|
||||
# [[domain:]bus:][device][.function]
|
||||
|
@ -115,22 +108,16 @@ class DeviceFilter(Filter):
|
|||
cls: Optional[int] = None
|
||||
"""
|
||||
Device class ID, as a four-digit hexadecimal number.
|
||||
|
||||
:type: int or None
|
||||
"""
|
||||
|
||||
vendor: Optional[int] = None
|
||||
"""
|
||||
Device vendor ID, as a four-digit hexadecimal number.
|
||||
|
||||
:type: int or None
|
||||
"""
|
||||
|
||||
device: Optional[int] = None
|
||||
"""
|
||||
Device ID, as a four-digit hexadecimal number.
|
||||
|
||||
:type: int or None
|
||||
"""
|
||||
|
||||
# [vendor]:[device][:class]
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from typing import Union, Mapping, Iterable, List, Dict, Any
|
||||
from typing import Any, Dict, Iterable, List, Union
|
||||
|
||||
from pylspci.device import Device
|
||||
|
||||
|
||||
|
@ -30,7 +31,7 @@ class Parser(ABC):
|
|||
:rtype: List[Device]
|
||||
"""
|
||||
|
||||
def run(self, **kwargs: Mapping[str, Any]) -> List[Device]:
|
||||
def run(self, **kwargs: Any) -> List[Device]:
|
||||
"""
|
||||
Run the lspci command with the given arguments, defaulting to the
|
||||
parser's default arguments, and parse the result.
|
||||
|
@ -38,7 +39,7 @@ class Parser(ABC):
|
|||
:param \\**kwargs: Optional arguments to override the parser's default
|
||||
arguments. See :func:`lspci`'s documentation for a list of
|
||||
available arguments.
|
||||
:type \\**kwargs: Mapping[str, Any]
|
||||
:type \\**kwargs: Any
|
||||
:returns: A list of parsed devices.
|
||||
:rtype: List[Device]
|
||||
"""
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
from typing import Union, List, Iterable
|
||||
from cached_property import cached_property
|
||||
from pylspci.parsers.base import Parser
|
||||
from pylspci.fields import hexstring, Slot, NameWithID
|
||||
from pylspci.device import Device
|
||||
import argparse
|
||||
import shlex
|
||||
from typing import Any, Iterable, List, Union
|
||||
|
||||
from cached_property import cached_property
|
||||
|
||||
from pylspci.device import Device
|
||||
from pylspci.fields import NameWithID, Slot, hexstring
|
||||
from pylspci.parsers.base import Parser
|
||||
|
||||
|
||||
class SimpleParser(Parser):
|
||||
|
@ -86,3 +88,11 @@ class SimpleParser(Parser):
|
|||
if isinstance(args, str):
|
||||
args = shlex.split(args)
|
||||
return Device(**vars(self._parser.parse_args(args)))
|
||||
|
||||
def run(self, **kwargs: Any) -> List[Device]:
|
||||
if kwargs.get('verbose'):
|
||||
raise ValueError(
|
||||
'Verbose output is unsupported from the SimpleParser. '
|
||||
'Please use the pylspci.parsers.VerboseParser instead.'
|
||||
)
|
||||
return super().run(**kwargs)
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
from typing import Union, List, Dict, Iterable, NamedTuple, Callable, Any
|
||||
from pylspci.parsers.base import Parser
|
||||
from pylspci.device import Device
|
||||
from pylspci.fields import hexstring, Slot, NameWithID
|
||||
import warnings
|
||||
from typing import Any, Callable, Dict, Iterable, List, NamedTuple, Union
|
||||
|
||||
from pylspci.device import Device
|
||||
from pylspci.fields import NameWithID, Slot, hexstring
|
||||
from pylspci.parsers.base import Parser
|
||||
|
||||
UNKNOWN_FIELD_WARNING = (
|
||||
'Unsupported device field {!r} with value {!r}\n'
|
||||
'Please report this, along with the output of `lspci -mmnnvvvk`, at '
|
||||
'https://gitlab.com/Lucidiot/pylspci/issues'
|
||||
'https://tildegit.org/lucidiot/pylspci/issues/new'
|
||||
)
|
||||
|
||||
|
||||
|
@ -20,23 +21,17 @@ class FieldMapping(NamedTuple):
|
|||
field_name: str
|
||||
"""
|
||||
Field name on the :class:`Device` named tuple.
|
||||
|
||||
:type: str
|
||||
"""
|
||||
|
||||
field_type: Callable[[str], Any]
|
||||
"""
|
||||
Field type; a callable to use to parse the string value.
|
||||
|
||||
:type: Callable[[str], Any]
|
||||
"""
|
||||
|
||||
many: bool = False
|
||||
"""
|
||||
Whether or not to use a List, if this field can be repeated multiple times
|
||||
in the lspci output.
|
||||
|
||||
:type: bool
|
||||
"""
|
||||
|
||||
|
||||
|
@ -74,7 +69,7 @@ class VerboseParser(Parser):
|
|||
),
|
||||
'NUMANode': FieldMapping(field_name='numa_node', field_type=int),
|
||||
'IOMMUGroup': FieldMapping(field_name='iommu_group', field_type=int),
|
||||
'PhySlot': FieldMapping(field_name='physical_slot', field_type=int),
|
||||
'PhySlot': FieldMapping(field_name='physical_slot', field_type=str),
|
||||
}
|
||||
|
||||
def _parse_device(self, device_data: Union[str, Iterable[str]]) -> Device:
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from unittest import TestCase
|
||||
from unittest.mock import patch, call, MagicMock
|
||||
from pathlib import Path
|
||||
from unittest import TestCase
|
||||
from unittest.mock import MagicMock, call, patch
|
||||
|
||||
from pylspci.command import CommandBuilder, IDResolveOption
|
||||
from pylspci.parsers import SimpleParser, VerboseParser
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
from unittest import TestCase
|
||||
from unittest.mock import patch, call, MagicMock
|
||||
from unittest.mock import MagicMock, call, patch
|
||||
|
||||
from pylspci.command import (
|
||||
IDResolveOption, list_access_methods, list_pcilib_params, lspci
|
||||
)
|
||||
from pylspci.fields import PCIAccessParameter
|
||||
from pylspci.command import \
|
||||
lspci, list_access_methods, list_pcilib_params, IDResolveOption
|
||||
|
||||
|
||||
class TestCommand(TestCase):
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from unittest import TestCase
|
||||
from unittest.mock import patch, call, MagicMock
|
||||
from typing import List
|
||||
from unittest import TestCase
|
||||
from unittest.mock import MagicMock, call, patch
|
||||
|
||||
from pylspci.device import Device
|
||||
from pylspci.parsers import SimpleParser
|
||||
|
||||
|
@ -98,3 +99,12 @@ class TestSimpleParser(TestCase):
|
|||
|
||||
self.assertEqual(cmd_mock.call_count, 1)
|
||||
self.assertEqual(cmd_mock.call_args, call())
|
||||
|
||||
def test_verbose_error(self) -> None:
|
||||
with self.assertRaises(ValueError) as ctx:
|
||||
self.parser.run(verbose=True)
|
||||
self.assertEqual(
|
||||
ctx.exception.args[0],
|
||||
'Verbose output is unsupported from the SimpleParser. '
|
||||
'Please use the pylspci.parsers.VerboseParser instead.'
|
||||
)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from unittest import TestCase
|
||||
from unittest.mock import patch, call, MagicMock
|
||||
from typing import List
|
||||
from unittest import TestCase
|
||||
from unittest.mock import MagicMock, call, patch
|
||||
|
||||
from pylspci.device import Device
|
||||
from pylspci.parsers import VerboseParser
|
||||
|
||||
|
@ -55,7 +56,7 @@ class TestVerboseParser(TestCase):
|
|||
self.assertListEqual(dev.kernel_modules, ['nouveau', 'nvidia'])
|
||||
self.assertEqual(dev.numa_node, 0)
|
||||
self.assertEqual(dev.iommu_group, 1)
|
||||
self.assertEqual(dev.physical_slot, 4)
|
||||
self.assertEqual(dev.physical_slot, '4')
|
||||
|
||||
def test_parse_str(self) -> None:
|
||||
devices: List[Device] = self.parser.parse(SAMPLE_DEVICE)
|
||||
|
@ -86,7 +87,7 @@ class TestVerboseParser(TestCase):
|
|||
msg="Unsupported device field 'NewField' with value 'Value'\n"
|
||||
"Please report this, along with the output of"
|
||||
"`lspci -mmnnvvvk`, at "
|
||||
"https://gitlab.com/Lucidiot/pylspci/issues"):
|
||||
"https://tildegit.org/lucidiot/pylspci/issues/new"):
|
||||
devices: List[Device] = \
|
||||
self.parser.parse(SAMPLE_DEVICE + 'NewField\tValue')
|
||||
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
from unittest import TestCase
|
||||
|
||||
from pylspci.device import Device
|
||||
from pylspci.fields import NameWithID, Slot
|
||||
|
||||
|
||||
class TestDevice(TestCase):
|
||||
|
||||
def test_as_dict(self) -> None:
|
||||
d = Device(
|
||||
slot=Slot('cafe:13:07.2'),
|
||||
cls=NameWithID('Something [caf3]'),
|
||||
vendor=NameWithID('Something [caf3]'),
|
||||
device=NameWithID('Something [caf3]'),
|
||||
subsystem_vendor=NameWithID('Something [caf3]'),
|
||||
subsystem_device=NameWithID('Something [caf3]'),
|
||||
revision=20,
|
||||
progif=1,
|
||||
driver='self_driving',
|
||||
kernel_modules=['snd-pcsp'],
|
||||
numa_node=0,
|
||||
iommu_group=1,
|
||||
physical_slot='4-2',
|
||||
)
|
||||
self.assertDictEqual(d.as_dict(), {
|
||||
'slot': {
|
||||
'bus': 0x13,
|
||||
'device': 0x07,
|
||||
'domain': 0xcafe,
|
||||
'function': 0x2,
|
||||
'parent': None
|
||||
},
|
||||
'cls': {
|
||||
'id': 0xcaf3,
|
||||
'name': 'Something'
|
||||
},
|
||||
'vendor': {
|
||||
'id': 0xcaf3,
|
||||
'name': 'Something'
|
||||
},
|
||||
'device': {
|
||||
'id': 0xcaf3,
|
||||
'name': 'Something'
|
||||
},
|
||||
'subsystem_vendor': {
|
||||
'id': 0xcaf3,
|
||||
'name': 'Something'
|
||||
},
|
||||
'subsystem_device': {
|
||||
'id': 0xcaf3,
|
||||
'name': 'Something'
|
||||
},
|
||||
'revision': 20,
|
||||
'progif': 1,
|
||||
'driver': 'self_driving',
|
||||
'kernel_modules': ['snd-pcsp'],
|
||||
'numa_node': 0,
|
||||
'iommu_group': 1,
|
||||
'physical_slot': '4-2',
|
||||
})
|
|
@ -1,5 +1,6 @@
|
|||
from unittest import TestCase
|
||||
from pylspci.fields import Slot, NameWithID, PCIAccessParameter
|
||||
|
||||
from pylspci.fields import NameWithID, PCIAccessParameter, Slot
|
||||
|
||||
|
||||
class TestSlot(TestCase):
|
||||
|
@ -72,6 +73,16 @@ class TestSlot(TestCase):
|
|||
self.assertEqual(s.parent.parent.device, 0x07)
|
||||
self.assertEqual(s.parent.parent.function, 0x2)
|
||||
|
||||
def test_as_dict(self) -> None:
|
||||
s = Slot('cafe:13:07.2')
|
||||
self.assertDictEqual(s.as_dict(), {
|
||||
"domain": 0xcafe,
|
||||
"bus": 0x13,
|
||||
"device": 0x07,
|
||||
"function": 0x2,
|
||||
"parent": None,
|
||||
})
|
||||
|
||||
|
||||
class TestNameWithID(TestCase):
|
||||
|
||||
|
@ -127,6 +138,13 @@ class TestNameWithID(TestCase):
|
|||
self.assertIsNone(n.id)
|
||||
self.assertEqual(n.name, 'Something [hexa]')
|
||||
|
||||
def test_as_dict(self) -> None:
|
||||
n = NameWithID('Something [caf3]')
|
||||
self.assertDictEqual(n.as_dict(), {
|
||||
"id": 0xcaf3,
|
||||
"name": "Something",
|
||||
})
|
||||
|
||||
|
||||
class TestPCIAccessParameter(TestCase):
|
||||
|
||||
|
@ -158,3 +176,11 @@ class TestPCIAccessParameter(TestCase):
|
|||
p2 = PCIAccessParameter('param.name Some description ()')
|
||||
self.assertEqual(p1, p1)
|
||||
self.assertNotEqual(p1, p2)
|
||||
|
||||
def test_as_dict(self) -> None:
|
||||
p = PCIAccessParameter('param.name Some description (default value)')
|
||||
self.assertDictEqual(p.as_dict(), {
|
||||
"name": "param.name",
|
||||
"description": "Some description",
|
||||
"default": "default value",
|
||||
})
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from unittest import TestCase
|
||||
from pylspci.filters import SlotFilter, DeviceFilter
|
||||
|
||||
from pylspci.filters import DeviceFilter, SlotFilter
|
||||
|
||||
|
||||
class TestSlotFilter(TestCase):
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
flake8>=3.5
|
||||
doc8>=0.8.0
|
||||
Sphinx>=1.8.1
|
||||
coverage>=4.5
|
||||
codecov>=2.0
|
||||
mypy>=0.720
|
||||
pre-commit>=2.9.2
|
||||
|
|
|
@ -1 +1 @@
|
|||
cached-property>=1.5.1
|
||||
cached-property>=1.5.1
|
||||
|
|
|
@ -10,3 +10,6 @@ disallow_incomplete_defs=True
|
|||
disallow_untyped_defs=True
|
||||
check_untyped_defs=True
|
||||
no_implicit_optional=True
|
||||
|
||||
[isort]
|
||||
multi_line_output=5
|
||||
|
|
27
setup.py
27
setup.py
|
@ -1,7 +1,8 @@
|
|||
#!/usr/bin/env python3
|
||||
from setuptools import setup, find_packages
|
||||
from typing import List
|
||||
|
||||
from setuptools import find_packages, setup
|
||||
|
||||
|
||||
def read_requirements(filename: str) -> List[str]:
|
||||
return [req.strip() for req in open(filename)]
|
||||
|
@ -21,22 +22,27 @@ setup(
|
|||
'console_scripts': ['pylspci=pylspci.__main__:main'],
|
||||
},
|
||||
package_data={
|
||||
'': ['VERSION', 'LICENSE', 'README.rst'],
|
||||
'': [
|
||||
'VERSION',
|
||||
'LICENSE',
|
||||
'README.rst',
|
||||
'requirements.txt',
|
||||
'requirements-dev.txt',
|
||||
],
|
||||
'pylspci': ['py.typed'],
|
||||
},
|
||||
python_requires='>=3.5',
|
||||
python_requires='>=3.6',
|
||||
install_requires=requirements,
|
||||
extras_require={
|
||||
'dev': dev_requirements,
|
||||
},
|
||||
tests_require=dev_requirements,
|
||||
test_suite='pylspci.tests',
|
||||
license='GNU General Public License 3',
|
||||
description="Simple parser for lspci -mmnn.",
|
||||
long_description=open('README.rst').read(),
|
||||
long_description_content_type='text/x-rst',
|
||||
keywords="lspci parser",
|
||||
url="https://gitlab.com/Lucidiot/pylspci",
|
||||
url="https://tildegit.org/lucidiot/pylspci",
|
||||
classifiers=[
|
||||
"Development Status :: 4 - Beta",
|
||||
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
||||
|
@ -44,16 +50,21 @@ setup(
|
|||
"Intended Audience :: Developers",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3 :: Only",
|
||||
"Programming Language :: Python :: 3.5",
|
||||
"Programming Language :: Python :: 3.6",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Topic :: Software Development :: Libraries",
|
||||
"Topic :: System :: Hardware",
|
||||
"Topic :: Utilities",
|
||||
"Typing :: Typed",
|
||||
],
|
||||
project_urls={
|
||||
"Source Code": "https://gitlab.com/Lucidiot/pylspci",
|
||||
"GitHub Mirror": "https://github.com/Lucidiot/pylspci",
|
||||
"Homepage": "https://tildegit.org/lucidiot/pylspci",
|
||||
"Changelog": "https://tildegit.org/lucidiot/pylspci/releases",
|
||||
"Documentation": "https://lucidiot.tildepages.org/pylspci/",
|
||||
"Issue tracker": "https://tildegit.org/lucidiot/pylspci/issues",
|
||||
}
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue