Skip to content

Commit

Permalink
Merge pull request #127 from logicalzero/feature/docs_and_annotation
Browse files Browse the repository at this point in the history
Annotation and docstring edits
  • Loading branch information
StokesMIDE committed Nov 2, 2023
2 parents 64f9e07 + c4e5d6a commit 45257fa
Show file tree
Hide file tree
Showing 8 changed files with 464 additions and 335 deletions.
317 changes: 200 additions & 117 deletions ebmlite/core.py

Large diffs are not rendered by default.

81 changes: 41 additions & 40 deletions ebmlite/decoding.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

from datetime import datetime, timedelta
import struct
from typing import BinaryIO, Optional, Tuple
import warnings

# ==============================================================================
Expand Down Expand Up @@ -42,10 +43,10 @@
# --- Reading and Decoding
# ==============================================================================

def decodeIntLength(byte):
def decodeIntLength(byte: int) -> Tuple[int, int]:
""" Extract the encoded size from an initial byte.
@return: The size, and the byte with the size removed (it is the first
:return: The size, and the byte with the size removed (it is the first
byte of the value).
"""
# An inelegant implementation, but it's fast.
Expand All @@ -67,11 +68,11 @@ def decodeIntLength(byte):
return 8, 0


def decodeIDLength(byte):
def decodeIDLength(byte: int) -> Tuple[int, int]:
""" Extract the encoded ID size from an initial byte.
@return: The size and the original byte (it is part of the ID).
@raise IOError: raise if the length of an ID is invalid.
:return: The size and the original byte (it is part of the ID).
:raise IOError: raise if the length of an ID is invalid.
"""
if byte >= 128:
return 1, byte
Expand All @@ -86,12 +87,12 @@ def decodeIDLength(byte):
raise IOError('Invalid length for ID: %d' % length)


def readElementID(stream):
def readElementID(stream: BinaryIO) -> Tuple[int, int]:
""" Read an element ID from a file (or file-like stream).
@param stream: The source file-like object.
@return: The decoded element ID and its length in bytes.
@raise IOError: raised if the length of the ID of an element is greater than 4 bytes.
:param stream: The source file-like object.
:return: The decoded element ID and its length in bytes.
:raise IOError: raised if the length of the ID of an element is greater than 4 bytes.
"""
ch = stream.read(1)
length, eid = decodeIDLength(ord(ch))
Expand All @@ -104,11 +105,11 @@ def readElementID(stream):
return eid, length


def readElementSize(stream):
def readElementSize(stream: BinaryIO) -> Tuple[Optional[int], int]:
""" Read an element size from a file (or file-like stream).
@param stream: The source file-like object.
@return: The decoded size (or `None`) and the length of the
:param stream: The source file-like object.
:return: The decoded size (or `None`) and the length of the
descriptor in bytes.
"""
ch = stream.read(1)
Expand All @@ -126,12 +127,12 @@ def readElementSize(stream):
return size, length


def readUInt(stream, size):
def readUInt(stream: BinaryIO, size: int) -> int:
""" Read an unsigned integer from a file (or file-like stream).
@param stream: The source file-like object.
@param size: The number of bytes to read from the stream.
@return: The decoded value.
:param stream: The source file-like object.
:param size: The number of bytes to read from the stream.
:return: The decoded value.
"""

if size == 0:
Expand All @@ -141,12 +142,12 @@ def readUInt(stream, size):
return _struct_uint64_unpack_from(data.rjust(8, b'\x00'))[0]


def readInt(stream, size):
def readInt(stream: BinaryIO, size: int) -> int:
""" Read a signed integer from a file (or file-like stream).
@param stream: The source file-like object.
@param size: The number of bytes to read from the stream.
@return: The decoded value.
:param stream: The source file-like object.
:param size: The number of bytes to read from the stream.
:return: The decoded value.
"""

if size == 0:
Expand All @@ -160,13 +161,13 @@ def readInt(stream, size):
return _struct_int64_unpack_from(data.rjust(8, pad))[0]


def readFloat(stream, size):
""" Read an floating point value from a file (or file-like stream).
def readFloat(stream: BinaryIO, size: int) -> float:
""" Read a floating point value from a file (or file-like stream).
@param stream: The source file-like object.
@param size: The number of bytes to read from the stream.
@return: The decoded value.
@raise IOError: raised if the length of this floating point number is not
:param stream: The source file-like object.
:param size: The number of bytes to read from the stream.
:return: The decoded value.
:raise IOError: raised if the length of this floating point number is not
valid (0, 4, 8 bytes)
"""
if size == 4:
Expand All @@ -180,12 +181,12 @@ def readFloat(stream, size):
"only lengths of 0, 4, or 8 bytes supported." % size)


def readString(stream, size):
def readString(stream: BinaryIO, size: int) -> str:
""" Read an ASCII string from a file (or file-like stream).
@param stream: The source file-like object.
@param size: The number of bytes to read from the stream.
@return: The decoded value.
:param stream: The source file-like object.
:param size: The number of bytes to read from the stream.
:return: The decoded value.
"""
if size == 0:
return u''
Expand All @@ -200,12 +201,12 @@ def readString(stream, size):
return str(value, 'ascii', 'replace')


def readUnicode(stream, size):
""" Read an UTF-8 encoded string from a file (or file-like stream).
def readUnicode(stream: BinaryIO, size: int) -> str:
""" Read a UTF-8 encoded string from a file (or file-like stream).
@param stream: The source file-like object.
@param size: The number of bytes to read from the stream.
@return: The decoded value.
:param stream: The source file-like object.
:param size: The number of bytes to read from the stream.
:return: The decoded value.
"""

if size == 0:
Expand All @@ -216,14 +217,14 @@ def readUnicode(stream, size):
return str(data, 'utf_8')


def readDate(stream, size=8):
def readDate(stream: BinaryIO, size: int = 8) -> datetime:
""" Read an EBML encoded date (nanoseconds since UTC 2001-01-01T00:00:00)
from a file (or file-like stream).
@param stream: The source file-like object.
@param size: The number of bytes to read from the stream.
@return: The decoded value (as `datetime.datetime`).
@raise IOError: raised if the length of the date is not 8 bytes.
:param stream: The source file-like object.
:param size: The number of bytes to read from the stream.
:return: The decoded value (as `datetime.datetime`).
:raise IOError: raised if the length of the date is not 8 bytes.
"""
if size != 8:
raise IOError("Cannot read date value of length %d, only 8." % size)
Expand Down
95 changes: 48 additions & 47 deletions ebmlite/encoding.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import datetime
import struct
import sys
from typing import AnyStr, Optional
import warnings

from .decoding import _struct_uint64, _struct_int64
Expand Down Expand Up @@ -45,11 +46,11 @@
# ==============================================================================


def getLength(val):
def getLength(val: int) -> int:
""" Calculate the encoded length of a value.
@param val: A value to be encoded, generally either an ID or a size for
:param val: A value to be encoded, generally either an ID or a size for
an EBML element
@return The minimum length, in bytes, that can be used to represent val
:return The minimum length, in bytes, that can be used to represent val
"""
# Brute force it. Ugly but faster than calculating it.
if val <= 126:
Expand All @@ -70,15 +71,15 @@ def getLength(val):
return 8


def encodeSize(val, length=None):
def encodeSize(val: Optional[int], length: Optional[int] = None) -> bytes:
""" Encode an element size.
@param val: The size to encode. If `None`, the EBML 'unknown' size
:param val: The size to encode. If `None`, the EBML 'unknown' size
will be returned (1 or `length` bytes, all bits 1).
@keyword length: An explicit length for the encoded size. If `None`,
:param length: An explicit length for the encoded size. If `None`,
the size will be encoded at the minimum length required.
@return: an encoded size for an EBML element.
@raise ValueError: raised if the length is invalid, or the length cannot
:return: an encoded size for an EBML element.
:raise ValueError: raised if the length is invalid, or the length cannot
be encoded.
"""
if val is None:
Expand All @@ -98,16 +99,16 @@ def encodeSize(val, length=None):
# --- Encoding
# ==============================================================================

def encodeId(eid, length=None):
def encodeId(eid: int, length: Optional[int] = None) -> bytes:
""" Encode an element ID.
@param eid: The EBML ID to encode.
@keyword length: An explicit length for the encoded data. A `ValueError`
:param eid: The EBML ID to encode.
:param length: An explicit length for the encoded data. A `ValueError`
will be raised if the length is too short to encode the value.
@return: The binary representation of ID, left-padded with ``0x00`` if
:return: The binary representation of ID, left-padded with ``0x00`` if
`length` is not `None`.
@return: The encoded version of the ID.
@raise ValueError: raised if length is less than one or more than 4.
:return: The encoded version of the ID.
:raise ValueError: raised if length is less than one or more than 4.
"""
if length is not None:
if length < 1 or length > 4:
Expand All @@ -119,15 +120,15 @@ def encodeId(eid, length=None):
raise TypeError('Cannot encode {} {!r} as ID'.format(type(eid).__name__, eid))


def encodeUInt(val, length=None):
def encodeUInt(val: int, length: Optional[int] = None) -> bytes:
""" Encode an unsigned integer.
@param val: The unsigned integer value to encode.
@keyword length: An explicit length for the encoded data. A `ValueError`
:param val: The unsigned integer value to encode.
:param length: An explicit length for the encoded data. A `ValueError`
will be raised if the length is too short to encode the value.
@return: The binary representation of val as an unsigned integer,
:return: The binary representation of val as an unsigned integer,
left-padded with ``0x00`` if `length` is not `None`.
@raise ValueError: raised if val is longer than length.
:raise ValueError: raised if val is longer than length.
"""
if isinstance(val, float):
fval, val = val, int(val)
Expand Down Expand Up @@ -155,16 +156,16 @@ def encodeUInt(val, length=None):
return packed.rjust(length, pad)


def encodeInt(val, length=None):
def encodeInt(val: int, length: Optional[int] = None) -> bytes:
""" Encode a signed integer.
@param val: The signed integer value to encode.
@keyword length: An explicit length for the encoded data. A `ValueError`
:param val: The signed integer value to encode.
:param length: An explicit length for the encoded data. A `ValueError`
will be raised if the length is too short to encode the value.
@return: The binary representation of val as a signed integer,
:return: The binary representation of val as a signed integer,
left-padded with either ```0x00`` (for positive values) or ``0xFF``
(for negative) if `length` is not `None`.
@raise ValueError: raised if val is longer than length.
:raise ValueError: raised if val is longer than length.
"""
if isinstance(val, float):
fval, val = val, int(val)
Expand Down Expand Up @@ -194,15 +195,15 @@ def encodeInt(val, length=None):
raise TypeError('Cannot encode {} {!r} as integer'.format(type(val).__name__, val))


def encodeFloat(val, length=None):
def encodeFloat(val: float, length: Optional[int] = None) -> bytes:
""" Encode a floating point value.
@param val: The floating point value to encode.
@keyword length: An explicit length for the encoded data. Must be
:param val: The floating point value to encode.
:param length: An explicit length for the encoded data. Must be
`None`, 0, 4, or 8; otherwise, a `ValueError` will be raised.
@return: The binary representation of val as a float, left-padded with
:return: The binary representation of val as a float, left-padded with
``0x00`` if `length` is not `None`.
@raise ValueError: raised if val not length 0, 4, or 8
:raise ValueError: raised if val not length 0, 4, or 8
"""
if length is None:
if val is None or val == 0.0:
Expand All @@ -224,16 +225,16 @@ def encodeFloat(val, length=None):
raise TypeError('Cannot encode {} {!r} as float'.format(type(val).__name__, val))


def encodeBinary(val, length=None):
def encodeBinary(val: AnyStr, length: Optional[int] = None) -> bytes:
""" Encode binary data.
@param val: A string, bytes, or bytearray containing the data to encode.
@keyword length: An explicit length for the encoded data. A
:param val: A string, bytes, or bytearray containing the data to encode.
:param length: An explicit length for the encoded data. A
`ValueError` will be raised if `length` is shorter than the
actual length of the binary data.
@return: The binary representation of value as binary data, left-padded
:return: The binary representation of value as binary data, left-padded
with ``0x00`` if `length` is not `None`.
@raise ValueError: raised if val is longer than length.
:raise ValueError: raised if val is longer than length.
"""
if val is None:
val = b''
Expand All @@ -251,13 +252,13 @@ def encodeBinary(val, length=None):
(len(val), length))


def encodeString(val, length=None):
def encodeString(val: AnyStr, length: Optional[int] = None) -> bytes:
""" Encode an ASCII string.
@param val: The string (or bytearray) to encode.
@keyword length: An explicit length for the encoded data. The result
:param val: The string (or bytearray) to encode.
:param length: An explicit length for the encoded data. The result
will be truncated if the original string is longer.
@return: The binary representation of val as a string, truncated or
:return: The binary representation of val as a string, truncated or
left-padded with ``0x00`` if `length` is not `None`.
"""
if isinstance(val, str):
Expand All @@ -271,13 +272,13 @@ def encodeString(val, length=None):
return encodeBinary(val.translate(STRING_CHARACTERS), length)


def encodeUnicode(val, length=None):
def encodeUnicode(val: str, length: Optional[int] = None) -> bytes:
""" Encode a Unicode string.
@param val: The Unicode string to encode.
@keyword length: An explicit length for the encoded data. The result
:param val: The Unicode string to encode.
:param length: An explicit length for the encoded data. The result
will be truncated if the original string is longer.
@return: The binary representation of val as a string, truncated or
:return: The binary representation of val as a string, truncated or
left-padded with ``0x00`` if `length` is not `None`.
"""
if not isinstance(val, (bytearray, bytes, str)):
Expand All @@ -291,15 +292,15 @@ def encodeUnicode(val, length=None):
return encodeBinary(val, length)


def encodeDate(val, length=None):
def encodeDate(val: datetime.datetime, length: Optional[int] = None) -> bytes:
""" Encode a `datetime` object as an EBML date (i.e. nanoseconds since
2001-01-01T00:00:00).
@param val: The `datetime.datetime` object value to encode.
@keyword length: An explicit length for the encoded data. Must be
:param val: The `datetime.datetime` object value to encode.
:param length: An explicit length for the encoded data. Must be
`None` or 8; otherwise, a `ValueError` will be raised.
@return: The binary representation of val as an 8-byte dateTime.
@raise ValueError: raised if the length of the input is not 8 bytes.
:return: The binary representation of val as an 8-byte dateTime.
:raise ValueError: raised if the length of the input is not 8 bytes.
"""
if length is None:
length = 8
Expand Down
Loading

0 comments on commit 45257fa

Please sign in to comment.