Skip to content

File valuefd.py#

File List > libpisoundmicro > pypisoundmicro > pypisoundmicro > valuefd.py

Go to the documentation of this file

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# SPDX-License-Identifier: LGPL-2.1-only
#
# libpisoundmicro - a utility library for Pisound Micro I/O expander capabilities.
# Copyright (c) 2017-2025 Vilniaus Blokas UAB, https://blokas.io/
#
# This file is part of libpisoundmicro.
#
# libpisoundmicro is free software: you can redistribute it and/or modify it under the terms of the
# GNU Lesser General Public License as published by the Free Software Foundation, version 2.1 of the License.
#
# libpisoundmicro is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along with libpisoundmicro. If not, see <https://www.gnu.org/licenses/>.

import os
from .swig import pypisoundmicro as psm
from ._utils import copy_doc
from typing import Union

@copy_doc(psm.ValueFd)
class ValueFd:
    """A wrapper around a file descriptor for reading/writing element values."""

    _fd_obj: psm.ValueFd = None

    def __init__(self, fd : Union[psm.ValueFd, int]):
        """Create a ValueFd from an existing file descriptor.

        Args:
            fd (int): The file descriptor to wrap
        """
        if isinstance(fd, psm.ValueFd):
            self._fd_obj = fd
        elif isinstance(fd, int):
            self._fd_obj = psm.ValueFd(fd)
        else:
            raise TypeError("fd must be a psm.ValueFd or an int")

    def __del__(self):
        """Close the file descriptor when the object is garbage collected."""
        self.close()

    @property
    @copy_doc(psm.ValueFd.isValid)
    def is_valid(self) -> bool:
        return self._fd_obj.isValid() if self._fd_obj else False

    @copy_doc(psm.ValueFd.take)
    def take(self) -> int:
        if self._fd_obj is None:
            return -1

        fd = self._fd_obj.take()
        self._fd_obj = None
        return fd

    @copy_doc(psm.ValueFd.get)
    def get(self) -> int:
        return self._fd_obj.get() if self._fd_obj else -1

    @copy_doc(psm.ValueFd.close)
    def close(self) -> None:
        if hasattr(self, '_fd_obj') and self._fd_obj is not None:
            err = self._fd_obj.close()
            if err != 0:
                raise OSError(f"Failed to close file descriptor: {err}")
            self._fd_obj = None

    @copy_doc(psm.ValueFd.write)
    def write(self, value) -> int:
        return self._fd_obj.write(value) if self._fd_obj else -1

    def read(self) -> int:
        """
        Reads a decimal number from the fd and returns it as integer.

        :raises OSError: If the file descriptor is invalid or if an error occurs during reading.
        :return: The read value.
        """
        if self._fd_obj is None:
            return (-1, os.EBADF)

        result, err = self._fd_obj.read()
        if err != 0:
            raise OSError(f"Failed to read from file descriptor: {err}")

        return result