Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add version_matching module #341

Closed
wants to merge 6 commits into from

Conversation

davigsousa
Copy link
Contributor

@davigsousa davigsousa commented Nov 1, 2023

As described at #220

I implemented an initial version of a version_matching module.

  • As suggested by @leandromoreira, it is a separated module from parser.
  • I added some version matching rules from the reference specified by @thenewguy. I hope we can add the following ones in next PRs.
  • It only runs in strict mode

m3u8/version_matching.py Outdated Show resolved Hide resolved
m3u8/version_matching_rules.py Outdated Show resolved Hide resolved
m3u8/parser.py Show resolved Hide resolved
@davigsousa
Copy link
Contributor Author

davigsousa commented Apr 8, 2024

All errors are now raised when we finish to validate a file in strict mode, example:

Traceback (most recent call last):
  File "/home/..././invalid.py", line 4, in <module>
    content = m3u8.parse(invalid_versioned_playlists.M3U8_RULE_IV, strict=True)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/.../m3u8/m3u8/parser.py", line 82, in parse
    raise Exception(found_errors)
Exception: [VersionMatchingError(line_number=2, line='#EXT-X-KEY: METHOD=AES-128, IV=0x123456789ABCDEF0123456789ABCDEF0, URI="https://example.com/key.bin"', how_to_fix='Change the protocol version to 2 or higher.', description='You must use at least protocol version 2 if you have IV in EXT-X-KEY.'), VersionMatchingError(line_number=4, line='#EXTINF: 10.0,', how_to_fix='Change the protocol version to 3 or higher.', description='You must use at least protocol version 3 if you have floating point EXTINF duration values.')]

I thought about printing them, but I didn't see any other prints in Parse, so I just raised

Copy link
Contributor

@leandromoreira leandromoreira left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Outstanding work you've done here, thanks a lot. I left some comments/nit but if you want, you can handle them in a new PR.

m3u8/parser.py Show resolved Hide resolved
def validate(file_lines: List[str]) -> List[VersionMatchingError]:
found_version = get_version(file_lines)
if found_version is None:
return []
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For master variant is it mandatory to have version? if that's the case then I think we should return an error here stating that the master playlist doesn't contain a version.

Again we can leave that for a future PR if you wish.

class VersionMatchingError(Exception):
line_number: int
line: str
how_to_fix: str = "Please fix the version matching error."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️

try:
float(value)
return True
except:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: we should catch ValueError

@leandromoreira
Copy link
Contributor

All errors are now raised when we finish to validate a file in strict mode, example:

Traceback (most recent call last):
  File "/home/..././invalid.py", line 4, in <module>
    content = m3u8.parse(invalid_versioned_playlists.M3U8_RULE_IV, strict=True)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/.../m3u8/m3u8/parser.py", line 82, in parse
    raise Exception(found_errors)
Exception: [VersionMatchingError(line_number=2, line='#EXT-X-KEY: METHOD=AES-128, IV=0x123456789ABCDEF0123456789ABCDEF0, URI="https://example.com/key.bin"', how_to_fix='Change the protocol version to 2 or higher.', description='You must use at least protocol version 2 if you have IV in EXT-X-KEY.'), VersionMatchingError(line_number=4, line='#EXTINF: 10.0,', how_to_fix='Change the protocol version to 3 or higher.', description='You must use at least protocol version 3 if you have floating point EXTINF duration values.')]

I thought about printing them, but I didn't see any other prints in Parse, so I just raised

I think raising it's the best we can do, I suggested we use the group exception so the natural print will be easier to read.

@davigsousa
Copy link
Contributor Author

I suggested we use the group exception

As I said, I wanted to use ExceptionGroup, but it is from Python 3.11 and the project requires only Python3.7+, I dont know if we can use it here. 😕

@leandromoreira
Copy link
Contributor

I suggested we use the group exception

As I said, I wanted to use ExceptionGroup, but it is from Python 3.11 and the project requires only Python3.7+, I dont know if we can use it here. 😕

that's okay, thanks for the efforts

@leandromoreira
Copy link
Contributor

leandromoreira commented Apr 8, 2024

A minor feature we can add, if the verification list keeps growing, is a map/dict like structure to avoid N*N:

if hash(line) in verifications:
  verifications[verification_hash(line)].valid?

def verification_hash(line):
  if line.contains("version"):
    return "version"

@mauricioabreu
Copy link
Member

Awesome work, @davigsousa

@mauricioabreu
Copy link
Member

Could you check why it is failing for 3.7 version @davigsousa ?

@leandromoreira
Copy link
Contributor

It looks like related to:

available_rules: List[type[VersionMatchRuleBase]] = []

Maybe replacing type[ by typing.Type or type(.

https://docs.python.org/3.7/library/typing.html?highlight=type#typing.Type

tests/test_invalid_versioned_playlists.py:9: in <module>
    import m3u8
m3u8/__init__.py:9: in <module>
    from m3u8.model import (
m3u8/model.py:8: in <module>
    from m3u8.parser import format_date_time, parse
m3u8/parser.py:17: in <module>
    from m3u8 import protocol, version_matching
m3u8/version_matching.py:4: in <module>
    from m3u8.version_matching_rules import VersionMatchingError, available_rules
m3u8/version_matching_rules.py:108: in <module>
    ValidEXTXBYTERANGEOrEXTXIFRAMESONLY,
E   TypeError: 'type' object is not subscriptable

@leandromoreira leandromoreira mentioned this pull request May 11, 2024
Copy link
Contributor

@leandromoreira leandromoreira left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good work


@pytest.mark.xfail
def test_should_fail_if_iv_in_EXT_X_KEY_and_version_less_than_2():
m3u8.parse(invalid_versioned_playlists.M3U8_RULE_IV)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: maybe we should assert the exception or at least partially the error message/help

def test_raises():
    with pytest.raises(Exception) as exc_info:   
        raise Exception('some info')
    # these asserts are identical; you can use either one   
    assert exc_info.value.args[0] == 'some info'
    assert str(exc_info.value) == 'some info'

ref https://stackoverflow.com/a/23514853

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mauricioabreu
Copy link
Member

Hello @davigsousa, are you still working on these awesome changes? If you don't have time to complete the task, I can take over the PR from here and handle the finalization and merging.

@davigsousa
Copy link
Contributor Author

Hi @mauricioabreu , unfortunately I have very little time for open source lately, sorry for that! I wanted to finish the work here, but thank you for your help, I'll accept it! We could speed up the merge here.

You can take over, thank you very much!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants