Compare commits

...

2 Commits

Author SHA1 Message Date
Mario Fetka 3f4ba32dbb Imported Upstream version 3.1.0 2023-07-01 11:55:30 +02:00
Mario Fetka 8a6603ece7 Imported Upstream version 3.1.0 2023-07-01 11:54:39 +02:00
18 changed files with 142 additions and 111 deletions

57
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,57 @@
name: CI
on:
pull_request: {}
push: {}
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python 3
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Install dependencies
run: |
python3 -m pip install --upgrade pip
python3 -m pip install tox tox-gh-actions
- run: tox -e lint
check-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python 3
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Install dependencies
run: |
python3 -m pip install --upgrade pip
python3 -m pip install tox tox-gh-actions
- run: tox -e check-docs
test:
runs-on: ubuntu-latest
continue-on-error: ${{ matrix.experimental }}
strategy:
fail-fast: false
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
experimental: [false]
include:
- python-version: "3.12-dev"
experimental: true
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python3 -m pip install --upgrade pip setuptools
python3 -m pip install tox tox-gh-actions
- run: tox

View File

@ -1,37 +0,0 @@
language: python
dist: xenial
python:
- "3.6"
- "3.7"
- "3.8"
# TODO add 3.9
- "3.9-dev"
install: pip install tox-travis tox tox-venv
# Used by the `test` stage.
script: tox
stages:
- test
- lint
jobs:
allow_failures:
- python: "3.9-dev"
include:
# The `test` stage using the `python` matrix defined above
# is included implicitly.
- stage: lint
name: "Code Linting"
python: "3.7"
script: TOXENV=lint tox
- stage: check-docs
name: "Docs check"
python: "3.7"
script: TOXENV=check-docs tox

View File

@ -9,7 +9,7 @@ lint:
.PHONY: test .PHONY: test
test: test:
coverage run -m py.test coverage run -m pytest
coverage report coverage report
.PHONY: clean .PHONY: clean
@ -49,15 +49,11 @@ check-docs:
deb-local: clean sdist deb-local: clean sdist
docker build -t afancontrol-debuild -f ./Dockerfile.debian . docker build -t afancontrol-debuild -f ./Dockerfile.debian .
docker run -it --rm \ docker run -it --rm \
-e DEBFULLNAME="`git config --global user.name`" \
-e DEBEMAIL="`git config --global user.email`" \
-v `pwd`/dist:/afancontrol/dist \ -v `pwd`/dist:/afancontrol/dist \
-v `pwd`/debian:/afancontrol/debian \ -v `pwd`/debian:/afancontrol/debian \
afancontrol-debuild sh -ex -c '\ afancontrol-debuild sh -ex -c '\
tar xaf /afancontrol/dist/afancontrol-*.tar.gz --strip 1; \ tar xaf /afancontrol/dist/afancontrol-*.tar.gz --strip 1; \
dch -v `python3 setup.py --version` -b --distribution=unstable; \
debuild -us -uc -b; \ debuild -us -uc -b; \
cp debian/changelog /afancontrol/debian/; \
cd ../; \ cd ../; \
ls -alh; \ ls -alh; \
mkdir /afancontrol/dist/debian; \ mkdir /afancontrol/dist/debian; \

View File

@ -5,8 +5,8 @@ afancontrol
:target: https://pypi.python.org/pypi/afancontrol/ :target: https://pypi.python.org/pypi/afancontrol/
:alt: Latest Version :alt: Latest Version
.. image:: https://img.shields.io/travis/KostyaEsmukov/afancontrol.svg?style=flat-square .. image:: https://img.shields.io/github/workflow/status/KostyaEsmukov/afancontrol/CI?style=flat-square
:target: https://travis-ci.org/KostyaEsmukov/afancontrol :target: https://github.com/KostyaEsmukov/afancontrol/actions
:alt: Build Status :alt: Build Status
.. image:: https://img.shields.io/github/license/KostyaEsmukov/afancontrol.svg?style=flat-square .. image:: https://img.shields.io/github/license/KostyaEsmukov/afancontrol.svg?style=flat-square

16
debian/changelog vendored
View File

@ -1,3 +1,19 @@
afancontrol (3.1.0-1) unstable; urgency=medium
[ Juha Yrjölä ]
* Support glob expansion with fans (#9)
[ Kostya Esmukov ]
* Drop Python 3.6 support, add 3.11
-- Kostya Esmukov <kostya@esmukov.ru> Mon, 28 Nov 2022 00:11:43 +0200
afancontrol (3.0.0-2) unstable; urgency=medium
* Bump debhelper compat to 13
-- Kostya Esmukov <kostya@esmukov.ru> Mon, 02 Aug 2021 19:19:34 +0000
afancontrol (3.0.0-1) unstable; urgency=medium afancontrol (3.0.0-1) unstable; urgency=medium
* Drop support for prometheus-client < 0.1.0 (debian stretch) * Drop support for prometheus-client < 0.1.0 (debian stretch)

1
debian/compat vendored
View File

@ -1 +0,0 @@
9

30
debian/control vendored
View File

@ -2,37 +2,29 @@ Source: afancontrol
Section: utils Section: utils
Priority: optional Priority: optional
Maintainer: Kostya Esmukov <kostya@esmukov.ru> Maintainer: Kostya Esmukov <kostya@esmukov.ru>
Build-Depends: debhelper (>= 9), Build-Depends: debhelper-compat (= 13),
dh-python, dh-python,
dh-systemd,
python3-all, python3-all,
python3-click,
python3-prometheus-client (>= 0.1.0),
python3-pytest,
python3-requests,
python3-serial,
python3-setuptools python3-setuptools
Build-Depends-Indep: python3-pytest,
python3-requests,
python3-click,
python3-prometheus-client (>= 0.1.0),
python3-serial
Standards-Version: 3.9.8 Standards-Version: 3.9.8
Homepage: https://github.com/KostyaEsmukov/afancontrol Homepage: https://github.com/KostyaEsmukov/afancontrol
X-Python3-Version: >= 3.5
#Vcs-Git: https://anonscm.debian.org/git/python-modules/packages/python3-afancontrol.git
#Vcs-Browser: https://anonscm.debian.org/cgit/python-modules/packages/python3-afancontrol.git/
#Testsuite: autopkgtest-pkg-python
Package: afancontrol Package: afancontrol
Architecture: all Architecture: all
Depends: ${python3:Depends}, Depends: hddtemp,
${misc:Depends},
hddtemp,
lm-sensors, lm-sensors,
python3-click, python3-click,
python3-pkg-resources, python3-pkg-resources,
python3-prometheus-client (>= 0.1.0), python3-prometheus-client (>= 0.1.0),
python3-serial python3-serial,
Suggests: freeipmi-tools, ${misc:Depends},
${python3:Depends}
Suggests: freeipmi-tools
Description: Advanced Fan Control program (Python 3) Description: Advanced Fan Control program (Python 3)
afancontrol is an Advanced Fan Control program, which controls PWM afancontrol is an Advanced Fan Control program, which controls PWM
fans according to the current temperatures of the system components. fans according to the current temperatures of the system components.
.
This package installs the library for Python 3.

2
debian/rules vendored Normal file → Executable file
View File

@ -9,4 +9,4 @@ export PYBUILD_TEST_PYTEST=1
export PYBUILD_TEST_ARGS={dir}/tests/ export PYBUILD_TEST_ARGS={dir}/tests/
%: %:
dh $@ --with systemd,python3 --buildsystem=pybuild dh $@ --with python3 --buildsystem=pybuild

View File

@ -25,13 +25,8 @@ per-file-ignores =
src/afancontrol/config.py:C901 src/afancontrol/config.py:C901
[isort] [isort]
; https://github.com/timothycrosley/isort#multi-line-output-modes
multi_line_output = 3 multi_line_output = 3
; https://github.com/ambv/black#how-black-wraps-lines profile = black
include_trailing_comma = True
force_grid_wrap = 0
combine_as_imports = True
line_length = 88
[metadata] [metadata]
author = Kostya Esmukov author = Kostya Esmukov
@ -45,10 +40,6 @@ classifier =
Programming Language :: Python Programming Language :: Python
Programming Language :: Python :: 3 Programming Language :: Python :: 3
Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Topic :: System :: Hardware Topic :: System :: Hardware
Topic :: System :: Monitoring Topic :: System :: Monitoring
Topic :: System :: Systems Administration Topic :: System :: Systems Administration
@ -76,7 +67,7 @@ install_requires =
package_dir = package_dir =
= src = src
packages = find: packages = find:
python_requires = >=3.6 python_requires = >=3.7
[options.entry_points] [options.entry_points]
console_scripts = console_scripts =
@ -88,15 +79,16 @@ arduino =
metrics = metrics =
prometheus-client>=0.1.0 prometheus-client>=0.1.0
dev = dev =
black==20.8b1 black==22.10.0
coverage==5.3 coverage==6.5.0
flake8==3.8.4 flake8==5.0.4
isort==5.5.4 isort==5.10.1
mypy==0.782 mypy==0.990
pytest==6.1.0 pytest==7.2.0
sphinx==4.3.2
vcrpy==4.2.1
requests requests
sphinx==3.2.1 types-requests
wheel
[options.packages.find] [options.packages.find]
where = src where = src

View File

@ -1 +1 @@
__version__ = "3.0.0" __version__ = "3.1.0"

View File

@ -1,4 +1,5 @@
import configparser import configparser
import glob
from typing import Any, Generic, Iterator, Optional, Type, TypeVar, Union, overload from typing import Any, Generic, Iterator, Optional, Type, TypeVar, Union, overload
T = TypeVar("T", bound=str) T = TypeVar("T", bound=str)
@ -127,3 +128,12 @@ class ConfigParserSection(Generic[T]):
"[%s] %r option is expected to be set" % (self.__section.name, option) "[%s] %r option is expected to be set" % (self.__section.name, option)
) )
return res return res
def expand_glob(path: str):
matches = glob.glob(path)
if not matches:
return path # a FileNotFoundError will be raised on a first read attempt
if len(matches) == 1:
return matches[0]
raise ValueError("Expected glob to expand to a single path, got %r" % (matches,))

View File

@ -187,7 +187,8 @@ def fantest(
elif fan_type == "arduino": elif fan_type == "arduino":
if not arduino_serial_url: if not arduino_serial_url:
arduino_serial_url = click.prompt( arduino_serial_url = click.prompt(
"\n%s\nArduino Serial url" % HELP_ARDUINO_SERIAL_URL, type=str "\n%s\nArduino Serial url" % HELP_ARDUINO_SERIAL_URL,
type=str, # type: ignore
) )
# typeshed currently specifies `Optional[str]` for `default`, # typeshed currently specifies `Optional[str]` for `default`,
@ -197,17 +198,19 @@ def fantest(
# Hence the `type: ignore`. # Hence the `type: ignore`.
arduino_baudrate = click.prompt( # type: ignore arduino_baudrate = click.prompt( # type: ignore
"\n%s\nBaudrate" % HELP_ARDUINO_BAUDRATE, "\n%s\nBaudrate" % HELP_ARDUINO_BAUDRATE,
type=int, type=int, # type: ignore
default=str(arduino_baudrate), default=str(arduino_baudrate),
show_default=True, show_default=True,
) )
if not arduino_pwm_pin and arduino_pwm_pin != 0: if not arduino_pwm_pin and arduino_pwm_pin != 0:
arduino_pwm_pin = click.prompt( arduino_pwm_pin = click.prompt(
"\n%s\nArduino PWM pin" % HELP_ARDUINO_PWM_PIN, type=int "\n%s\nArduino PWM pin" % HELP_ARDUINO_PWM_PIN,
type=int, # type: ignore
) )
if not arduino_tacho_pin and arduino_tacho_pin != 0: if not arduino_tacho_pin and arduino_tacho_pin != 0:
arduino_tacho_pin = click.prompt( arduino_tacho_pin = click.prompt(
"\n%s\nArduino Tachometer pin" % HELP_ARDUINO_TACHO_PIN, type=int "\n%s\nArduino Tachometer pin" % HELP_ARDUINO_TACHO_PIN,
type=int, # type: ignore
) )
assert arduino_serial_url is not None assert arduino_serial_url is not None

View File

@ -1,11 +1,13 @@
import abc import abc
from typing import NewType, Type from typing import NewType, Tuple, Type
PWMValue = NewType("PWMValue", int) # [0..255] PWMValue = NewType("PWMValue", int) # [0..255]
FanValue = NewType("FanValue", int) FanValue = NewType("FanValue", int)
class _SlotsReprMixin: class _SlotsReprMixin:
__slots__: Tuple[str, ...]
def __eq__(self, other): def __eq__(self, other):
if isinstance(other, type(self)): if isinstance(other, type(self)):
for attr in self.__slots__: for attr in self.__slots__:

View File

@ -1,7 +1,7 @@
from pathlib import Path from pathlib import Path
from typing import NewType from typing import NewType
from afancontrol.configparser import ConfigParserSection from afancontrol.configparser import ConfigParserSection, expand_glob
from afancontrol.pwmfan.base import ( from afancontrol.pwmfan.base import (
BaseFanPWMRead, BaseFanPWMRead,
BaseFanPWMWrite, BaseFanPWMWrite,
@ -18,7 +18,7 @@ class LinuxFanSpeed(BaseFanSpeed):
__slots__ = ("_fan_input",) __slots__ = ("_fan_input",)
def __init__(self, fan_input: FanInputDevice) -> None: def __init__(self, fan_input: FanInputDevice) -> None:
self._fan_input = Path(fan_input) self._fan_input = Path(expand_glob(fan_input))
@classmethod @classmethod
def from_configparser(cls, section: ConfigParserSection) -> BaseFanSpeed: def from_configparser(cls, section: ConfigParserSection) -> BaseFanSpeed:
@ -35,7 +35,7 @@ class LinuxFanPWMRead(BaseFanPWMRead):
min_pwm = PWMValue(0) min_pwm = PWMValue(0)
def __init__(self, pwm: PWMDevice) -> None: def __init__(self, pwm: PWMDevice) -> None:
self._pwm = Path(pwm) self._pwm = Path(expand_glob(pwm))
@classmethod @classmethod
def from_configparser(cls, section: ConfigParserSection) -> BaseFanPWMRead: def from_configparser(cls, section: ConfigParserSection) -> BaseFanPWMRead:
@ -51,8 +51,9 @@ class LinuxFanPWMWrite(BaseFanPWMWrite):
read_cls = LinuxFanPWMRead read_cls = LinuxFanPWMRead
def __init__(self, pwm: PWMDevice) -> None: def __init__(self, pwm: PWMDevice) -> None:
self._pwm = Path(pwm) base = expand_glob(pwm)
self._pwm_enable = Path(pwm + "_enable") self._pwm = Path(base)
self._pwm_enable = Path(base + "_enable")
@classmethod @classmethod
def from_configparser(cls, section: ConfigParserSection) -> BaseFanPWMWrite: def from_configparser(cls, section: ConfigParserSection) -> BaseFanPWMWrite:

View File

@ -1,21 +1,11 @@
import glob
import re import re
from pathlib import Path from pathlib import Path
from typing import Optional, Tuple from typing import Optional, Tuple
from afancontrol.configparser import ConfigParserSection from afancontrol.configparser import ConfigParserSection, expand_glob
from afancontrol.temp.base import Temp, TempCelsius from afancontrol.temp.base import Temp, TempCelsius
def _expand_glob(path: str):
matches = glob.glob(path)
if not matches:
return path # a FileNotFoundError will be raised on a first read attempt
if len(matches) == 1:
return matches[0]
raise ValueError("Expected glob to expand to a single path, got %r" % (matches,))
class FileTemp(Temp): class FileTemp(Temp):
def __init__( def __init__(
self, self,
@ -33,7 +23,7 @@ class FileTemp(Temp):
# /sys/devices/pci0000:00/0000:00:01.3/[...]/hwmon/hwmon*/temp1_input # /sys/devices/pci0000:00/0000:00:01.3/[...]/hwmon/hwmon*/temp1_input
# The `hwmon*` might change after reboot, but it is always a single # The `hwmon*` might change after reboot, but it is always a single
# directory within the device. # directory within the device.
temp_path = _expand_glob(temp_path + "_input") temp_path = expand_glob(temp_path + "_input")
temp_path = re.sub(r"_input$", "", temp_path) temp_path = re.sub(r"_input$", "", temp_path)
self._temp_input = Path(temp_path + "_input") self._temp_input = Path(temp_path + "_input")

View File

@ -6,7 +6,8 @@ import pytest
from click.testing import CliRunner from click.testing import CliRunner
from afancontrol import daemon from afancontrol import daemon
from afancontrol.daemon import PidFile, Signals, daemon as main from afancontrol.daemon import PidFile, Signals
from afancontrol.daemon import daemon as main
def test_main_smoke(temp_path): def test_main_smoke(temp_path):

View File

@ -10,9 +10,9 @@ from afancontrol.fantest import (
CSVMeasurementsOutput, CSVMeasurementsOutput,
HumanMeasurementsOutput, HumanMeasurementsOutput,
MeasurementsOutput, MeasurementsOutput,
fantest as main,
run_fantest,
) )
from afancontrol.fantest import fantest as main
from afancontrol.fantest import run_fantest
from afancontrol.pwmfan import ( from afancontrol.pwmfan import (
BaseFanPWMRead, BaseFanPWMRead,
BaseFanPWMWrite, BaseFanPWMWrite,

11
tox.ini
View File

@ -1,5 +1,5 @@
[tox] [tox]
envlist=py{36,37,38,39,310}{,-arduino,-metrics},lint,check-docs envlist=py{37,38,39,310,311,312}{,-arduino,-metrics},lint,check-docs
[testenv] [testenv]
extras = extras =
@ -12,6 +12,15 @@ commands = make test
; sources to the working dir by default. ; sources to the working dir by default.
usedevelop = True usedevelop = True
[gh-actions]
python =
3.7: py37
3.8: py38
3.9: py39
3.10: py310
3.11: py311
3.12: py312
[testenv:lint] [testenv:lint]
extras = extras =
arduino arduino