Skip to content

Commit

Permalink
Merge pull request #2491 from L2501/matrix-soupsieve-2.4.1
Browse files Browse the repository at this point in the history
[script.module.soupsieve@matrix] 2.4.1
  • Loading branch information
basrieter authored Aug 14, 2023
2 parents c8c4223 + fe286b0 commit 5e7f9f5
Show file tree
Hide file tree
Showing 11 changed files with 881 additions and 505 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2018 Isaac Muse <[email protected]>
Copyright (c) 2018 - 2023 Isaac Muse <[email protected]>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
73 changes: 0 additions & 73 deletions script.module.soupsieve/README.md

This file was deleted.

32 changes: 16 additions & 16 deletions script.module.soupsieve/addon.xml
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="script.module.soupsieve" name="Soup Sieve" version="2.1.0+matrix.1" provider-name="Isaac Muse">
<requires>
<import addon="xbmc.python" version="3.0.0"/>
</requires>
<extension point="xbmc.python.module" library="lib" />
<extension point="xbmc.addon.metadata">
<summary lang="en_GB">A CSS selector library</summary>
<description lang="en_GB">Soup Sieve is a CSS selector library designed to be used with Beautiful Soup 4. It aims to provide selecting, matching, and filtering using modern CSS selectors.</description>
<license>MIT</license>
<platform>all</platform>
<website>https://pypi.org/project/soupsieve/</website>
<source>https://github.com/facelessuser/soupsieve</source>
<assets>
<icon>icon.png</icon>
</assets>
</extension>
<addon id="script.module.soupsieve" name="soupsieve" version="2.4.1" provider-name="Isaac Muse">
<requires>
<import addon="xbmc.python" version="3.0.0"/>
</requires>
<extension point="xbmc.python.module" library="lib" />
<extension point="xbmc.addon.metadata">
<summary lang="en_GB">A modern CSS selector implementation for Beautiful Soup.</summary>
<description lang="en_GB">A modern CSS selector implementation for Beautiful Soup.</description>
<license>MIT</license>
<platform>all</platform>
<website>https://facelessuser.github.io/soupsieve/</website>
<source>https://github.com/facelessuser/soupsieve</source>
<assets>
<icon>resources/icon.png</icon>
</assets>
</extension>
</addon>
92 changes: 75 additions & 17 deletions script.module.soupsieve/lib/soupsieve/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,14 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
from __future__ import annotations
from .__meta__ import __version__, __version_info__ # noqa: F401
from . import css_parser as cp
from . import css_match as cm
from . import css_types as ct
from .util import DEBUG, SelectorSyntaxError # noqa: F401
import bs4 # type: ignore[import]
from typing import Any, Iterator, Iterable

__all__ = (
'DEBUG', 'SelectorSyntaxError', 'SoupSieve',
Expand All @@ -40,16 +43,16 @@
SoupSieve = cm.SoupSieve


def compile(pattern, namespaces=None, flags=0, **kwargs): # noqa: A001
def compile( # noqa: A001
pattern: str,
namespaces: dict[str, str] | None = None,
flags: int = 0,
*,
custom: dict[str, str] | None = None,
**kwargs: Any
) -> cm.SoupSieve:
"""Compile CSS pattern."""

if namespaces is not None:
namespaces = ct.Namespaces(**namespaces)

custom = kwargs.get('custom')
if custom is not None:
custom = ct.CustomSelectors(**custom)

if isinstance(pattern, SoupSieve):
if flags:
raise ValueError("Cannot process 'flags' argument on a compiled selector list")
Expand All @@ -59,53 +62,108 @@ def compile(pattern, namespaces=None, flags=0, **kwargs): # noqa: A001
raise ValueError("Cannot process 'custom' argument on a compiled selector list")
return pattern

return cp._cached_css_compile(pattern, namespaces, custom, flags)
return cp._cached_css_compile(
pattern,
ct.Namespaces(namespaces) if namespaces is not None else namespaces,
ct.CustomSelectors(custom) if custom is not None else custom,
flags
)


def purge():
def purge() -> None:
"""Purge cached patterns."""

cp._purge_cache()


def closest(select, tag, namespaces=None, flags=0, **kwargs):
def closest(
select: str,
tag: 'bs4.Tag',
namespaces: dict[str, str] | None = None,
flags: int = 0,
*,
custom: dict[str, str] | None = None,
**kwargs: Any
) -> 'bs4.Tag':
"""Match closest ancestor."""

return compile(select, namespaces, flags, **kwargs).closest(tag)


def match(select, tag, namespaces=None, flags=0, **kwargs):
def match(
select: str,
tag: 'bs4.Tag',
namespaces: dict[str, str] | None = None,
flags: int = 0,
*,
custom: dict[str, str] | None = None,
**kwargs: Any
) -> bool:
"""Match node."""

return compile(select, namespaces, flags, **kwargs).match(tag)


def filter(select, iterable, namespaces=None, flags=0, **kwargs): # noqa: A001
def filter( # noqa: A001
select: str,
iterable: Iterable['bs4.Tag'],
namespaces: dict[str, str] | None = None,
flags: int = 0,
*,
custom: dict[str, str] | None = None,
**kwargs: Any
) -> list['bs4.Tag']:
"""Filter list of nodes."""

return compile(select, namespaces, flags, **kwargs).filter(iterable)


def select_one(select, tag, namespaces=None, flags=0, **kwargs):
def select_one(
select: str,
tag: 'bs4.Tag',
namespaces: dict[str, str] | None = None,
flags: int = 0,
*,
custom: dict[str, str] | None = None,
**kwargs: Any
) -> 'bs4.Tag':
"""Select a single tag."""

return compile(select, namespaces, flags, **kwargs).select_one(tag)


def select(select, tag, namespaces=None, limit=0, flags=0, **kwargs):
def select(
select: str,
tag: 'bs4.Tag',
namespaces: dict[str, str] | None = None,
limit: int = 0,
flags: int = 0,
*,
custom: dict[str, str] | None = None,
**kwargs: Any
) -> list['bs4.Tag']:
"""Select the specified tags."""

return compile(select, namespaces, flags, **kwargs).select(tag, limit)


def iselect(select, tag, namespaces=None, limit=0, flags=0, **kwargs):
def iselect(
select: str,
tag: 'bs4.Tag',
namespaces: dict[str, str] | None = None,
limit: int = 0,
flags: int = 0,
*,
custom: dict[str, str] | None = None,
**kwargs: Any
) -> Iterator['bs4.Tag']:
"""Iterate the specified tags."""

for el in compile(select, namespaces, flags, **kwargs).iselect(tag, limit):
yield el


def escape(ident):
def escape(ident: str) -> str:
"""Escape identifier."""

return cp.escape(ident)
28 changes: 18 additions & 10 deletions script.module.soupsieve/lib/soupsieve/__meta__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Meta related things."""
from __future__ import annotations
from collections import namedtuple
import re

Expand Down Expand Up @@ -79,7 +80,11 @@ class Version(namedtuple("Version", ["major", "minor", "micro", "release", "pre"
"""

def __new__(cls, major, minor, micro, release="final", pre=0, post=0, dev=0):
def __new__(
cls,
major: int, minor: int, micro: int, release: str = "final",
pre: int = 0, post: int = 0, dev: int = 0
) -> Version:
"""Validate version info."""

# Ensure all parts are positive integers.
Expand Down Expand Up @@ -115,27 +120,27 @@ def __new__(cls, major, minor, micro, release="final", pre=0, post=0, dev=0):

return super(Version, cls).__new__(cls, major, minor, micro, release, pre, post, dev)

def _is_pre(self):
def _is_pre(self) -> bool:
"""Is prerelease."""

return self.pre > 0
return bool(self.pre > 0)

def _is_dev(self):
def _is_dev(self) -> bool:
"""Is development."""

return bool(self.release < "alpha")

def _is_post(self):
def _is_post(self) -> bool:
"""Is post."""

return self.post > 0
return bool(self.post > 0)

def _get_dev_status(self): # pragma: no cover
def _get_dev_status(self) -> str: # pragma: no cover
"""Get development status string."""

return DEV_STATUS[self.release]

def _get_canonical(self):
def _get_canonical(self) -> str:
"""Get the canonical output string."""

# Assemble major, minor, micro version and append `pre`, `post`, or `dev` if needed..
Expand All @@ -153,11 +158,14 @@ def _get_canonical(self):
return ver


def parse_version(ver, pre=False):
def parse_version(ver: str) -> Version:
"""Parse version into a comparable Version tuple."""

m = RE_VER.match(ver)

if m is None:
raise ValueError("'{}' is not a valid version".format(ver))

# Handle major, minor, micro
major = int(m.group('major'))
minor = int(m.group('minor')) if m.group('minor') else 0
Expand Down Expand Up @@ -185,5 +193,5 @@ def parse_version(ver, pre=False):
return Version(major, minor, micro, release, pre, post, dev)


__version_info__ = Version(2, 1, 0, "final")
__version_info__ = Version(2, 4, 1, "final")
__version__ = __version_info__._get_canonical()
Loading

0 comments on commit 5e7f9f5

Please sign in to comment.