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
57 changes: 32 additions & 25 deletions discid/disc.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@
FEATURES_IMPLEMENTED = list(_FEATURE_MAPPING.keys())


def read(device=None, features=None):
def read(
device: str | bytes | None = None, features: list[str] | None = None
) -> "Disc":
"""Reads the TOC from the device given as string
and returns a :class:`Disc` object.

That string can be either of :obj:`str <python:str>` or :obj:`bytes`.
The device string can be either of :obj:`str <python:str>` or :obj:`bytes`.
However, it should in no case contain non-ASCII characters.
If no device is given, a default, also given by :func:`get_default_device`
is used.
Expand All @@ -48,27 +50,31 @@ def read(device=None, features=None):
A :exc:`DiscError` exception is raised when the reading fails,
and :exc:`NotImplementedError` when libdiscid doesn't support
reading discs on the current platform.

:param device: the device name to use or :obj:`None` for using the default device
:param features: list of features to enable ("read" will always be assumed)
"""
disc = Disc()
disc.read(device, features)
return disc


def put(first, last, disc_sectors, track_offsets):
def put(first: int, last: int, disc_sectors: int, track_offsets: list[int]) -> "Disc":
"""Creates a TOC based on the information given
and returns a :class:`Disc` object.

Takes the `first` track and `last` **audio** track as :obj:`int`.
`disc_sectors` is the end of the last audio track,
normally the total sector count of the disc.
`track_offsets` is a list of all audio track offsets.

Depending on how you get the total sector count,
you might have to subtract 11400 (2:32 min.) for discs with data tracks.

A :exc:`TOCError` exception is raised when illegal parameters
are provided.

:param first: number of the first audio track
:param last: number of the last audio track
:param disc_sectors: the end of the last audio track, normally the total
sector count of the disc
:param track_offsets: list of all audio track offsets

.. seealso:: :musicbrainz:`Disc ID Calculation`
"""
disc = Disc()
Expand Down Expand Up @@ -125,7 +131,9 @@ def _get_error_msg(self):
except AttributeError:
pass

def read(self, device=None, features=None):
def read(
self, device: str | bytes | None = None, features: list[str] | None = None
) -> bool:
"""Reads the TOC from the device given as string

The user is supposed to use :func:`discid.read`.
Expand Down Expand Up @@ -162,7 +170,9 @@ def read(self, device=None, features=None):
_LIB.discid_put.argtypes = (c_void_p, c_int, c_int, c_void_p)
_LIB.discid_put.restype = c_int

def put(self, first, last, disc_sectors, track_offsets):
def put(
self, first: int, last: int, disc_sectors: int, track_offsets: list[int]
) -> bool:
"""Creates a TOC based on the input given.

The user is supposed to use :func:`discid.put`.
Expand Down Expand Up @@ -282,21 +292,21 @@ def _get_mcn(self):
return None

@property
def id(self):
def id(self) -> str:
"""This is the MusicBrainz :musicbrainz:`Disc ID`,
a :obj:`str <python:str>` object.
"""
return self._get_id()

@property
def freedb_id(self):
def freedb_id(self) -> str:
"""This is the :musicbrainz:`FreeDB` Disc ID (without category),
a :obj:`str <python:str>` object.
"""
return self._get_freedb_id()

@property
def submission_url(self):
def submission_url(self) -> str | None:
"""Disc ID / TOC Submission URL for MusicBrainz

With this url you can submit the current TOC
Expand All @@ -313,7 +323,7 @@ def submission_url(self):
return url

@property
def toc_string(self):
def toc_string(self) -> str:
"""The TOC suitable as value of the `toc parameter`
when accessing the MusicBrainz Web Service.

Expand All @@ -337,33 +347,30 @@ def toc_string(self):
return toc_string

@property
def first_track_num(self):
def first_track_num(self) -> int:
"""Number of the first track"""
return self._get_first_track_num()

@property
def last_track_num(self):
def last_track_num(self) -> int:
"""Number of the last **audio** track"""
return self._get_last_track_num()

@property
def sectors(self):
def sectors(self) -> int:
"""Total length in sectors"""
return self._get_sectors()

length = sectors
"""This is an alias for :attr:`sectors`"""

@property
def seconds(self):
def seconds(self) -> int:
"""Total length in seconds"""
if self.sectors is None:
return None
else:
return _sectors_to_seconds(self.sectors)
return _sectors_to_seconds(self.sectors)

@property
def mcn(self):
def mcn(self) -> str | None:
"""This is the Media Catalogue Number (MCN/UPC/EAN)

It is set after the `"mcn"` feature was requested on a read
Expand All @@ -373,7 +380,7 @@ def mcn(self):
return self._get_mcn()

@property
def tracks(self):
def tracks(self) -> list[Track]:
"""A list of :class:`Track` objects for this Disc."""
tracks = []
assert self._success
Expand All @@ -382,7 +389,7 @@ def tracks(self):
return tracks

@property
def cddb_query_string(self):
def cddb_query_string(self) -> str:
"""A CDDB query string suitable for querying CDDB servers.

This is a :obj:`str <python:str>` object
Expand Down
2 changes: 1 addition & 1 deletion discid/libdiscid.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def _get_version_string():
_LIB.discid_get_default_device.restype = c_char_p


def get_default_device():
def get_default_device() -> str:
"""The default device to use for :func:`read` on this platform
given as a :obj:`str <python:str>` object.
"""
Expand Down
26 changes: 17 additions & 9 deletions discid/track.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,26 @@
"""Track class"""

from ctypes import c_char_p, c_int, c_void_p
from typing import TYPE_CHECKING

from discid.libdiscid import _LIB
from discid.util import _decode, _sectors_to_seconds

if TYPE_CHECKING:
from .disc import Disc


class Track(object):
"""Track objects are part of the :class:`Disc` class."""
"""Track objects are part of the :class:`Disc` class.

:param disc: the :class:`Disc` object
:param number: the track number
"""

def __init__(self, disc, number):
def __init__(self, disc: "Disc", number: int):
self._disc = disc
self._number = number
assert self._disc._handle.value is not None
assert self._disc._handle and self._disc._handle.value is not None

def __str__(self):
assert self._disc._success
Expand Down Expand Up @@ -68,33 +76,33 @@ def _get_track_isrc(self):
return None

@property
def number(self):
def number(self) -> int:
"""The track number"""
return self._number

@property
def offset(self):
def offset(self) -> int:
"""The track offset"""
return self._get_track_offset()

@property
def sectors(self):
def sectors(self) -> int:
"""The track length in sectors"""
return self._get_track_length()

length = sectors
"""This is an alias for :attr:`sectors`"""

@property
def seconds(self):
def seconds(self) -> int:
"""Track length in seconds"""
return _sectors_to_seconds(self.sectors)

@property
def isrc(self):
def isrc(self) -> str | None:
"""The International Standard Recording Code

This will be `None` when the `"isrc"` feature was not requested
This will be :obj:`None` when the `"isrc"` feature was not requested
or not supported, otherwise this is a :obj:`str <python:str>` object.
"""
return self._get_track_isrc()
Expand Down
4 changes: 2 additions & 2 deletions discid/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
SECTORS_PER_SECOND = 75


def _encode(string: str | bytes):
def _encode(string: str | bytes) -> bytes:
"""Encode (unicode) string to byte string"""
if isinstance(string, str):
return string.encode()
Expand All @@ -33,7 +33,7 @@ def _encode(string: str | bytes):
# device names should be ASCII


def _decode(byte_string: bytes | str):
def _decode(byte_string: bytes | str) -> str:
"""Decode byte string to (unicode) string"""
# this test for bytes works on Python 2 and 3
if isinstance(byte_string, bytes):
Expand Down
6 changes: 4 additions & 2 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,23 @@ def __getattr__(cls, name):
return Mock()


ctypes.cdll.LoadLibrary = Mock()
ctypes.cdll.LoadLibrary = Mock() # type: ignore

# -- General configuration -----------------------------------------------------

needs_sphinx = "1.0"

extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.autosummary",
"sphinx.ext.coverage",
"sphinx.ext.extlinks",
"sphinx.ext.intersphinx",
"sphinx_autodoc_typehints",
]
source_suffix = ".rst"
master_doc = "index"
exclude_patterns = ["_build"]
exclude_patterns = ["_build", ".venv"]

# General information about the project.
project = "python-discid"
Expand Down
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ Changelog = "https://github.com/metabrainz/python-discid/blob/master/CHANGES.rst
[dependency-groups]
test = ["pytest"]
docs = [
"Sphinx~=8.1.3",
"sphinx-rtd-theme~=3.0.2",
"Sphinx>=8.1.3",
"sphinx-autodoc-typehints>=3.0.1",
"sphinx-rtd-theme>=3.0.2",
]
dev = [
"pre-commit>=4.5.1",
Expand Down
Loading