Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:

strategy:
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13"]
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]

steps:
- name: Checkout
Expand Down Expand Up @@ -44,7 +44,7 @@ jobs:
- name: Install dependencies
if: steps.cache.outputs.cache-hit != 'true'
run: |
pip install --disable-pip-version-check -e . --group=dev
pip install --disable-pip-version-check -e . --group=dev_no_qt
- name: Linting
run: |
ruff check --diff --output-format=github linuxpy tests examples
Expand All @@ -56,7 +56,7 @@ jobs:
- name: Tests
id: tests
run: |
pytest
pytest -k "not qt"

- name: Upload coverage data
uses: actions/upload-artifact@v4
Expand Down
2 changes: 1 addition & 1 deletion linuxpy/codegen/video.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ class StandardID(enum.IntFlag):
CEnum("TimeCodeType", "V4L2_TC_TYPE_"),
CEnum("TimeCodeFlag", "V4L2_TC_FLAG_", "IntFlag"),
CEnum("EventSubscriptionFlag", "V4L2_EVENT_SUB_FL_", "IntFlag"),
CEnum("EventControlChange", "V4L2_EVENT_CTRL_CH_"),
CEnum("EventControlChange", "V4L2_EVENT_CTRL_CH_", "IntFlag"),
CEnum("EventType", "V4L2_EVENT_"),
CEnum("MbusFrameFormatFlag", "V4L2_MBUS_FRAMEFMT_", "IntFlag"),
# It is very dificult to match just only these two values using prefix, so put whole name there
Expand Down
19 changes: 14 additions & 5 deletions linuxpy/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,11 @@ class BaseDevice(ReentrantOpen):

PREFIX = None

def __init__(self, name_or_file, read_write=True, io=IO):
def __init__(self, name_or_file, read_write=True, io=IO, blocking=False):
super().__init__()
self.io = io
if isinstance(name_or_file, (str, pathlib.Path)):
self.blocking = blocking
if isinstance(name_or_file, str | pathlib.Path):
filename = pathlib.Path(name_or_file)
self._read_write = read_write
self._fobj = None
Expand Down Expand Up @@ -121,12 +122,12 @@ def open(self):
"""Open the device if not already open. Triggers _on_open after the underlying OS open has succeeded"""
if not self._fobj:
self.log.info("opening %s", self.filename)
self._fobj = self.io.open(self.filename, self._read_write)
self._fobj = self.io.open(self.filename, self._read_write, blocking=self.blocking)
self._on_open()
self.log.info("opened %s", self.filename)

def close(self):
"""Closes the device if not already closed. Triggers _on_close before the underlying OS open has succeeded"""
"""Closes the device if not already closed. Triggers _on_close before the underlying OS close has succeeded"""
if not self.closed:
self._on_close()
self.log.info("closing %s", self.filename)
Expand All @@ -149,4 +150,12 @@ def is_blocking(self) -> bool:
True if the underlying OS is opened in blocking mode.
Raises error if device is not opened.
"""
return os.get_blocking(self.fileno())
return self.io.os.get_blocking(self.fileno())

def set_blocking(self, yes_no: bool) -> None:
"""
Turns on/off blocking mode.
Raises error if device is not opened.
"""
self.io.os.set_blocking(self.fileno(), yes_no)
self.blocking = yes_no
30 changes: 23 additions & 7 deletions linuxpy/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
# Distributed under the GPLv3 license. See LICENSE for more info.

import functools
import importlib
import os
import select

Expand Down Expand Up @@ -33,19 +34,34 @@ def opener(path, flags):

class IO:
open = functools.partial(fopen, blocking=False)
select = select.select
os = os
select = select


class GeventModule:
def __init__(self, name):
self.name = name
self._module = None

@property
def module(self):
if self._module is None:
self._module = importlib.import_module(f"gevent.{self.name}")
return self._module

def __getattr__(self, name):
attr = getattr(self.module, name)
setattr(self, name, attr)
return attr


class GeventIO:
@staticmethod
def open(path, rw=False):
def open(path, rw=False, blocking=False):
mode = "rb+" if rw else "rb"
import gevent.fileobject

return gevent.fileobject.FileObject(path, mode, buffering=0)

@staticmethod
def select(*args, **kwargs):
import gevent.select

return gevent.select.select(*args, **kwargs)
os = GeventModule("os")
select = GeventModule("select")
6 changes: 4 additions & 2 deletions linuxpy/ioctl.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

"""ioctl helper functions"""

import enum
import fcntl
import logging

Expand Down Expand Up @@ -34,7 +35,7 @@ def IOC(direction, magic, number, size):
magic = ord(magic)
if isinstance(size, str):
size = calcsize(size)
elif size == int:
elif size is int:
size = 4
elif not isinstance(size, int):
size = sizeof(size)
Expand Down Expand Up @@ -63,6 +64,7 @@ def IOWR(magic, number, size):


def ioctl(fd, request, *args):
log.debug("%s, request=%s, arg=%s", fd, request, args)
req = request.name if isinstance(request, enum.Enum) else request
log.debug("request=%s, arg=%s", req, args)
fcntl.ioctl(fd, request, *args)
return args and args[0] or None
2 changes: 1 addition & 1 deletion linuxpy/midi/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,7 @@ def wait_read(self) -> Sequence["Event"]:
non-blocking variants transperently
"""
if self.io.select is not None:
self.io.select((self,), (), ())
self.io.select.select((self,), (), ())
return self.raw_read()

def read(self) -> Sequence["Event"]:
Expand Down
Loading