Skip to content

Commit

Permalink
Merge branch 'release/3.3.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
StokesMIDE committed Dec 13, 2022
2 parents 4b89009 + ac0b1b0 commit 52bad65
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 77 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/publish-to-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
- uses: actions/checkout@master

- name: Set up Python 3.8
uses: actions/setup-python@v1
uses: actions/setup-python@v3
with:
python-version: 3.8

Expand Down
13 changes: 8 additions & 5 deletions .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ jobs:
fail-fast: false
matrix:
os: [windows-latest, ubuntu-latest, macos-latest]
python-version: ['3.6', '3.7', '3.8', '3.9', '3.10']
python-version: ['3.6', '3.7', '3.8', '3.9', '3.10', '3.11']
exclude:
- os: ubuntu-latest
python-version: '3.6'

env:
OS: ${{ matrix.os }}
Expand All @@ -22,18 +25,18 @@ jobs:

steps:

- uses: actions/setup-python@v2
- uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- run: python -m pip install --upgrade pip

- uses: actions/checkout@v2
- uses: actions/checkout@v3

- run: python -m pip install .[test]

- run: python -m pytest ./tests/ --cov=ebmlite --cov-report=xml --flake8 -n auto

- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v3
with:
name: multi-file-stuff
path: |
Expand All @@ -42,7 +45,7 @@ jobs:
./tests/ssx-1.xml
./tests/ssx-2.xml
- uses: codecov/codecov-action@v2
- uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
env_vars: OS,PYTHON-VERSION
Expand Down
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ The ``Schema`` class is a factory used to encode and decode EBML files. When it
### Documents
``Documents`` are subclasses of MasterElements, which act as an interface to EBML files and act as the root node of the EBML tree. Each ``Schema`` also creates a ``Document`` subclass to use, and the base ``Document`` class will not function without class variables defined by the ``Schema``.

### Utils
### Utilities
The functions provided by util.py will expose the majority of functionality needed to users, without the need to interface too deeply with this library. The following functions are provided:
* util.**toXml**(el, [parent=``None``,] [offsets=``True``,] [sizes=``True``,] [types=``True``,] [ids=``True``]):
Recursively converts EBML elements into xml elements.
Expand Down Expand Up @@ -231,8 +231,8 @@ Optional argument *indent*: The string containing the character(s) used for each

Command Line Utilities
----------------------
When EBMLite is installed as a Python library, the Utils can be called from the command line. From the command line,
documentation can be viewed using one of the following:
When `ebmlite` is installed as a Python library, the utilities can be called from the command line.
From the command line, documentation can be viewed using one of the following:
```commandline
python -m ebmlite.tools.ebml2xml -h
python -m ebmlite.tools.xml2ebml -h
Expand All @@ -247,14 +247,15 @@ python -m ebmlite.tools.ebml2xml <EBML file> <schema> -o <file.XML>
```commandline
python -m ebmlite.tools.ebml2xml DAQ11093_000001.ide mide_ide.xml -o DAQ11093_000001.xml
```
will translate `DAQ11093_000001.ide` (an enDAQ data recorder file) into XML, and write the result into
`DAQ11093_000001.xml`. The schema `mide_ide.xml` is built in to the EBMLite library.
will translate the EBML file `DAQ11093_000001.ide` (an enDAQ data recorder file) into XML,
and write the result into `DAQ11093_000001.xml`. The schema `mide_ide.xml` is built in to
the EBMLite library.

### xml2ebml
```
python -m ebmlite.tools.xml2ebml <file.XML> <schema> -o <EBML file>
```
`xml2ebml` will translate EBML back in to XML. For example
`xml2ebml` will translate XML back in to EBML. For example
```commandline
python -m ebmlite.tools.xml2ebml DAQ11093_000001.xml mide_ide.xml -o DAQ11093_000001b.ide
```
Expand Down
100 changes: 64 additions & 36 deletions ebmlite/encoding.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
'encodeSize', 'encodeString', 'encodeUInt', 'encodeUnicode']

import datetime
import struct
import sys
import warnings

Expand Down Expand Up @@ -89,7 +90,7 @@ def encodeSize(val, length=None):
try:
prefix = LENGTH_PREFIXES[length]
return encodeUInt(val | prefix, length)
except (IndexError, TypeError):
except (IndexError, TypeError, ValueError):
raise ValueError("Cannot encode element size %s" % length)


Expand All @@ -112,7 +113,10 @@ def encodeId(eid, length=None):
if length < 1 or length > 4:
raise ValueError("Cannot encode an ID 0x%0x to length %d" %
(eid, length))
return encodeUInt(eid, length)
try:
return encodeUInt(eid, length)
except TypeError:
raise TypeError('Cannot encode {} {!r} as ID'.format(type(eid).__name__, eid))


def encodeUInt(val, length=None):
Expand All @@ -129,9 +133,19 @@ def encodeUInt(val, length=None):
fval, val = val, int(val)
if fval != val:
warnings.warn('encodeUInt: float value {} encoded as {}'.format(fval, val))
elif not isinstance(val, int):
raise TypeError('Cannot encode {} {!r} as unsigned integer'.format(type(val).__name__, val))

if val < 0:
raise ValueError('Cannot encode negative value {} as unsigned integer'.format(val))

pad = b'\x00'
packed = _struct_uint64.pack(val).lstrip(pad) or pad

try:
packed = _struct_uint64.pack(val).lstrip(pad) or pad
except struct.error as err:
# Catch other errors. Value is probably too large for struct.
raise ValueError(str(err))

if length is None:
return packed
Expand All @@ -157,23 +171,27 @@ def encodeInt(val, length=None):
if fval != val:
warnings.warn('encodeInt: float value {} encoded as {}'.format(fval, val))

if val >= 0:
pad = b'\x00'
packed = _struct_int64.pack(val).lstrip(pad) or pad
if packed[0] & 0b10000000:
packed = pad + packed
else:
pad = b'\xff'
packed = _struct_int64.pack(val).lstrip(pad) or pad
if not packed[0] & 0b10000000:
packed = pad + packed
try:
if val >= 0:
pad = b'\x00'
packed = _struct_int64.pack(val).lstrip(pad) or pad
if packed[0] & 0b10000000:
packed = pad + packed
else:
pad = b'\xff'
packed = _struct_int64.pack(val).lstrip(pad) or pad
if not packed[0] & 0b10000000:
packed = pad + packed

if length is None:
return packed
if len(packed) > length:
raise ValueError("Encoded length (%d) greater than specified length "
"(%d)" % (len(packed), length))
return packed.rjust(length, pad)
if length is None:
return packed
if len(packed) > length:
raise ValueError("Encoded length (%d) greater than specified length "
"(%d)" % (len(packed), length))
return packed.rjust(length, pad)

except (TypeError, struct.error):
raise TypeError('Cannot encode {} {!r} as integer'.format(type(val).__name__, val))


def encodeFloat(val, length=None):
Expand All @@ -192,32 +210,37 @@ def encodeFloat(val, length=None):
else:
length = DEFAULT_FLOAT_SIZE

if length == 0:
return b''
if length == 4:
return _struct_float32.pack(val)
elif length == 8:
return _struct_float64.pack(val)
else:
raise ValueError("Cannot encode float of length %d; only 0, 4, or 8" %
length)
try:
if length == 0:
return b''
if length == 4:
return _struct_float32.pack(val)
elif length == 8:
return _struct_float64.pack(val)
else:
raise ValueError("Cannot encode float of length %d; only 0, 4, or 8" %
length)
except struct.error:
raise TypeError('Cannot encode {} {!r} as float'.format(type(val).__name__, val))


def encodeBinary(val, length=None):
""" Encode binary data.
@param val: A string or bytearray containing the data to encode.
@param val: A string, bytes, or bytearray containing the data to encode.
@keyword 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
with ``0x00`` if `length` is not `None`.
@raise ValueError: raised if val is longer than length.
"""
if isinstance(val, str):
val = val.encode('utf_8')
elif val is None:
if val is None:
val = b''
elif isinstance(val, str):
val = val.encode('utf_8')
elif not isinstance(val, (bytearray, bytes)):
raise TypeError('Cannot encode {} {!r} as binary'.format(type(val).__name__, val))

if length is None:
return val
Expand All @@ -232,15 +255,15 @@ def encodeString(val, length=None):
""" Encode an ASCII string.
@param val: The string (or bytearray) to encode.
@keyword length: An explicit length for the encoded data. Longer
strings will be truncated.
@keyword length: An explicit length for the encoded data. The result
will be truncated if the length is less than that of the original.
will be truncated if the original string is longer.
@return: The binary representation of val as a string, truncated or
left-padded with ``0x00`` if `length` is not `None`.
"""
if isinstance(val, str):
val = val.encode('ascii', 'replace')
elif not isinstance(val, (bytearray, bytes)):
raise TypeError('Cannot encode {} {!r} as ASCII string'.format(type(val).__name__, val))

if length is not None:
val = val[:length]
Expand All @@ -253,10 +276,13 @@ def encodeUnicode(val, length=None):
@param val: The Unicode string to encode.
@keyword length: An explicit length for the encoded data. The result
will be truncated if the length is less than that of the original.
will be truncated if the original string is longer.
@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)):
raise TypeError('Cannot encode {} {!r} as string'.format(type(val).__name__, val))

val = val.encode('utf_8')

if length is not None:
Expand All @@ -282,6 +308,8 @@ def encodeDate(val, length=None):

if val is None:
val = datetime.datetime.utcnow()
elif not isinstance(val, datetime.datetime):
raise TypeError('Cannot encode {} {!r} as datetime'.format(type(val).__name__, val))

delta = val - datetime.datetime(2001, 1, 1, tzinfo=None)
nanoseconds = (delta.microseconds +
Expand Down
7 changes: 5 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
'pytest>=4.6',
'codecov',
'pytest-cov',
'flake8<5.0',
'pytest-flake8',
'pytest-console-scripts',
'pytest-xdist[psutil]',
Expand All @@ -20,7 +21,7 @@

setuptools.setup(
name='ebmlite',
version='3.3.0',
version='3.3.1',
author='Mide Technology',
author_email='help@mide.com',
description='A lightweight, "pure Python" library for parsing EBML (Extensible Binary Markup Language) data.',
Expand All @@ -35,7 +36,9 @@
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10'],
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
],
keywords='ebml binary matroska webm',
packages=setuptools.find_packages(exclude="tests"),
package_dir={'': '.'},
Expand Down
Loading

0 comments on commit 52bad65

Please sign in to comment.