Skip to content

Releases

4.1

Choose a tag to compare

@facelessuser facelessuser released this 10 Nov 01:59

4.1

  • NEW: Soup Sieve is now thread safe, and this is now tested, including on free-threaded Python. This is assuming
    read only operations on a document. Check out our documentation for more details.
  • FIX: Fix a race where compiling selectors in multiple threads at the same time could fail with a bogus
    syntax error or silently parse a selector incorrectly.
  • FIX: Fix issue where trailing whitespace and comments can sometimes fail to be consumed properly.
  • FIX: Fix a selector that starts with a comment followed by whitespace, such as /* comment */ div, failing
    with a syntax error.
  • FIX: Custom selector names should be checked with ASCII only lowercase.
  • FIX: Fix [attr=value], [attr$=value], [attr|=value], and [attr!=value] ignoring a trailing newline in
    the attribute value, so [type=hidden] no longer matches type="hidden
".
  • FIX: Cache the language, the contenteditable state, the select of an option, and the tree that an element
    is in, as found from its ancestors, when matching :lang(), :read-write, :read-only, :checked, :default,
    and :indeterminate to avoid quadratic time on deeply nested documents.
  • FIX: Fix :lang('') not matching every element without any language information, which per the HTML spec has
    an empty language tag. It missed the first such element checked, or all of them when there was no head.
  • FIX: :lang() no longer matches an element whose language tag is not well-formed BCP 47 syntax, such as -,
    en-, en_US, or a subtag longer than 8 characters, and a language range that is not well-formed matches
    nothing. Previously, lang="-" matched :lang(''), and ill-formed tags matched :lang('*').
  • FIX: Fix :lang() ranges that end in a wildcard, such as en-* or *-*, never matching. As RFC 4647 extended
    filtering specifies, wildcards after the first subtag are ignored, so en-* matches the same as en.
  • FIX: Fix [attr*=value] and [attr$=value] taking time quadratic in the length of the value when it nearly
    matches a long attribute value, including when matching case insensitive.

4.0.1

Choose a tag to compare

@facelessuser facelessuser released this 04 Nov 16:35

4.0.1

  • FIX: Cache whether an element is or has a matching descendant when matching :has() to avoid quadratic time
    on deeply nested documents.
  • FIX: Cache whether the parent matches when matching the child combinator (a > b) so it is matched once for
    all of its children, avoiding quadratic time when the parent is expensive, such as div:has(> p) > span.

4.0

4.0

Choose a tag to compare

@facelessuser facelessuser released this 03 Nov 14:28
ff1a5ce

4.0

  • BREAK: The contents of an HTML template are no longer treated as part of the document. Selectors do not
    descend into a template, and elements inside one can't see beyond it, which also applies to pseudo-classes such
    as :lang(), :disabled, and :default. select and filter will still reach the contents of a template when
    they are called on the template itself, for example soup.template.select('p'). See the
    FAQ for more information.
  • BREAK: The contents of an iframe are no longer treated as a separate document, as they are not one in a
    browser. Elements in an iframe are normal children, so :root does not match them, and :default,
    :indeterminate, :checked, :dir(), :lang(), and :read-write treat them like any other element. Recent
    versions of html.parser, lxml, and html5lib all escape the contents of an iframe like a browser does, so
    this mostly affects XHTML, Python versions that still don't escape such content in their specific html.parser,
    and elements added to an iframe by hand. :-soup-contains() now also includes the escaped text in an iframe.
    See the FAQ for more information.
  • NEW: Update :disabled and :enabled to treat option and optgroup elements inside a disabled select as
    disabled, following a recent HTML specification update.
  • FIX: Fix the subsequent-sibling combinator and :has(~ ...) never matching top level elements of a fragment
    that has no root element, unless caching was disabled.
  • FIX: :indeterminate now treats a radio button group without a form as extending across the whole document
    instead of the top level element that contains it, and it no longer ignores radio buttons at the top level.
  • FIX: Fix :default to treat a button with a missing or invalid type, and an input with type="image",
    as submit buttons.
  • FIX: Fix :in-range and :out-of-range raising TypeError when the document contains an input without a
    type attribute.
  • FIX: :read-write (and :read-only) now treat contenteditable="plaintext-only" elements as editing hosts and
    match the editable elements inside an editing host, following the nearest contenteditable state.
  • FIX: :read-write (and :read-only) now ignore contenteditable on non-HTML elements, such as an SVG
    foreignObject, as the attribute only applies to HTML elements. Such elements inherit the state of their parent.
  • FIX: :checked now only matches the last radio button with a checked attribute in a radio button group, as
    in a browser, where a group is the radio buttons with the same name and form, including the form attribute.
    :default still matches all of them, and the contents of a template are unaffected as they are not connected.
  • FIX: :dir() and :defined no longer affect the rest of the selector list they are in. Previously, a selector
    list that used either of them could not have its combinators cross an iframe, ignored the specified namespaces,
    and matched nothing in XML, not even the parts that didn't use them. They still do not match in XML, but
    :not(:dir(ltr)) and :not(:defined) now do.
  • FIX: :default and :indeterminate now honor the form attribute, which can associate a submit button or
    radio button with a form it is not in, or leave it without a form. It is ignored in a template, as in the HTML
    specification. Submit buttons outside of a form can now be its default button.
  • FIX: :default, :checked, and :indeterminate now handle a form inside a form like a browser. In HTML, a
    browser ignores the nested form and takes its end tag for that of the parent, which matters for parsers that keep
    the nested form, such as html.parser. In XHTML and in a template, nested forms are real forms.
  • FIX: :checked now matches the option that a browser would select. Without multiple, that is only the
    last option with a selected attribute in a select, and if there are none and the select shows a single
    option, the first option that is not disabled. :default still only considers the selected attribute.
  • FIX: Fix [attribute!=value] excluding values that only start with the specified value, and in XML documents,
    type values that contain a newline.
  • FIX: Match the values of every attribute that the HTML specification lists as ASCII case insensitive (rel,
    method, lang, dir, ...) without ASCII case sensitivity on HTML elements in HTML documents, not just type.
    Non-HTML elements are matched case sensitively.
  • FIX: :dir() now matches non-HTML elements, such as SVG and MathML, by the direction they inherit from their
    parent (the dir attribute only applies to HTML elements), and HTML elements inside them, such as in an SVG
    foreignObject, by the direction of their ancestors.
  • FIX: Fix :dir() ignoring the value of an input with dir="auto" and a missing or empty type.
  • FIX: :dir() now uses a table generated from Unicode 18 instead of unicodedata, so results no longer depend
    on the Unicode version of the Python in use, and unassigned code points use their default direction.
  • FIX: :dir() now uses the value of an input with dir="auto" when type is hidden, password, submit,
    reset, or button, as the HTML specification does.
  • FIX: dir="auto" and bdi now analyze the text of non-HTML elements, such as SVG and MathML, and only ignore
    HTML elements such as script, style, and bdi. The text directly in an iframe is analyzed as well, and the
    contents of a template, which are not part of the document, are not.
  • FIX: :dir() no longer lets the contents of a template inherit direction from the template or its
    ancestors, as they are not part of the document. They are left to right unless they specify otherwise.
  • FIX: :dir() now matches a bdi element that is the root by its text instead of assuming left to right.
  • FIX: :dir() now treats any element without a parent element as left to right, not just the root. Before,
    all but the first top level element of a fragment parsed with html.parser never matched.
  • FIX: :dir() now accepts any identifier, as CSS requires, including escaped ones. Values other than ltr and
    rtl do not match anything.
  • FIX: Respect namespaces provided by the parser when the root is not an HTML element, such as an svg element
    extracted from an html5lib document. Previously every element in such a tree was treated as an HTML element,
    which affected namespace selectors, namespaced attributes, and HTML specific pseudo-classes.
  • FIX: Fix :lang() treating an element with an empty lang or xml:lang attribute as having the language of
    a tagged ancestor.
  • FIX: Cache forms without a default button when matching :default to avoid quadratic time on forms with
    many non-default submit candidates.
  • FIX: Fix :in-range and :out-of-range matching elements whose value, min, or max has a trailing newline;
    per HTML, such values are not valid, so they are treated like a missing value and cannot be out of range.
  • FIX: Fix :in-range and :out-of-range raising ValueError when a date, month, week, or local date and
    time input has a year with more digits than Python will convert; such a year is now treated as invalid.
  • FIX: Fix :in-range and :out-of-range not recognizing a datetime-local value, min, or max that uses a
    single space instead of T to separate the date and time.
  • FIX: Search a form only once for checked radio buttons when matching :indeterminate to avoid quadratic time
    on forms with many radio button groups.
  • FIX: :lang() now uses the lang attribute of SVG elements, as the HTML specification does (with xml:lang
    taking precedence). Previously only xml:lang was considered for elements outside the HTML namespace.
  • FIX: :defined no longer matches HTML elements with an is attribute (customized built-in elements), which,
    like custom elements, are undefined until they are registered.
  • FIX: Cache the index of each child when matching the general sibling combinator (a ~ b and :has(~ b)) to
    avoid quadratic time when the first matching sibling is far into a long list of siblings.
  • FIX: Match attribute values with the i modifier using ASCII case insensitivity.
  • FIX: Treat an input with an unknown type, such as type="foo", as a text input in :placeholder-shown,
    :read-write, :read-only and :dir(), as an invalid type is in the Text state in the HTML specification.
  • FIX: Fix :enabled and :disabled never matching input elements with type="hidden".
  • FIX: :required no longer matches input elements of types the required attribute does not apply to
    (hidden, range, color, submit, image, reset and button). Like in browsers, they match :optional.
  • FIX: Cache whether an element is or has a matching ancestor when matching the descendant combinator (a b) to
    avoid time that grows with the depth of the document to the power of the number of combinators.

3.0.4

Choose a tag to compare

@facelessuser facelessuser released this 22 Oct 00:44

3.0.4

  • FIX: Keep own and descendant text separate when combining :-soup-contains() and :-soup-contains-own().
  • FIX: Cache resolved directionality when matching :dir() to avoid quadratic time on deeply nested
    dir="auto" elements.
  • FIX: Fix auto handling in regard to :dir() selector. When an element with dir="auto" (or a <bdi>)
    contains no strong direction characters, it should be assumed as left-to-right and not assume the direction of the
    parent.
  • FIX: Match numeric input ranges correctly when values or bounds use scientific notation.
  • FIX: Fix :in-range and :out-of-range raising ValueError for week inputs whose week-year is 10000 or
    greater.
  • FIX: Fix :nth-last-child(an+b of S) matching the wrong elements when the first sibling tested does not
    match S.
  • FIX: escape() now only leaves non-ASCII identifier code points unescaped, as defined by the CSS specification.
    All other non-ASCII characters are escaped.
  • FIX: Allow unescaped U+0080 - U+009F characters in identifiers to match browser behavior.
  • FIX: Fix :in-range and :out-of-range treating time and datetime-local values with seconds as
    invalid.

3.0.3

Choose a tag to compare

@facelessuser facelessuser released this 12 Oct 13:20

3.0.3

  • FIX: When matching with >, the BeautifulSoup object should not count as a parent.
  • FIX: Fix case where relative sibling matching under :has(~) would incorrectly match a sibling that came before
    the target element.

3.0.2

Choose a tag to compare

@facelessuser facelessuser released this 12 Oct 01:35
99445a1

3.0.2

  • FIX: Python versions less than 3.11.5 have broken possessive quantifiers, so require at least 3.11.5 for Soup
    Sieve 3.0+. 3.0 and 3.0.1 will be yanked from PyPI to avoid having the 3.0 series installed on Python < 3.11.5
    which will cause the library to be broken.

3.0.1

Choose a tag to compare

@facelessuser facelessuser released this 10 Oct 17:09
b2cfcf4

3.0.1

  • FIX: Fix regression related to [attribute!=value] where previously it would match if the attribute was not
    present and recent changes broke that. Essentially, [attribute!=value] should be the same as
    :not([attribute=value]).

3.0

3.0

Choose a tag to compare

@facelessuser facelessuser released this 10 Oct 13:04

3.0

  • BREAK: :has(), by default, now behaves according to CSS more strictly, disallowing complex selectors that use
    combinators and prevents nesting :has() within :has(). This gives a safer, more performant default for Soup
    Sieve. If in a safe environment, these restrictions can be lifted by passing the new NOSTRICT flag restoring
    behavior to before 3.0.
  • BREAK: Custom selectors must be provided in order. If one custom selector depends on another custom selector,
    the custom selector dependency must be defined first in the dictionary. This eliminates recursive processing of
    custom selectors. Users are encouraged to pre-compile custom selectors via
    my_selectors = soupsive.CustomSelectors({custm: selector, ...}).
  • NEW: Drop Python 3.10.
  • NEW: Aggressively use possessive quantifiers and atomic groups in CSS pattern parsing to reduce backtracking.
  • NEW: Eliminate recursion within the parser and the matcher.
  • NEW: Expose new parameter max_selectors that can be used to control the maximum allowed selectors in a given
    input.
  • FIX: A CSS escape that is a surrogate, or past the Unicode range, is U+FFFD. (#307)

2.10

Choose a tag to compare

@facelessuser facelessuser released this 24 Sep 02:35
fc195cd

2.10

  • NEW: Support Python 3.15.
  • NEW: Add new ignore option to API methods that allows the specification of specific pseudo-classes to be
    ignored.
  • NEW: Tighten restrictions such that namespaces and custom objects must always be a Mapping, previously lists
    of tuples were also allowed.
  • NEW: Use a singleton for null selectors internally via called Null of type SelectorNull.
  • NEW: For performance, Soup Sieve will no longer try and coerce bad attribute values to useable strings.
  • NEW: Add NOCACHE flag that can be used to disable caching optimizations selectors and possibly other future
    caching optimizations. Provided for disabling and also disabling if issues are found with the new caching approach.
  • FIX: Improve performance of ~ for various cases by employing caching.
  • FIX: Improve performance of nth-* family of selectors in certain scenarios by employing caching.
  • FIX: Ensure custom is properly passed down from API functions to compilation.

2.9.2

Choose a tag to compare

@facelessuser facelessuser released this 07 Aug 00:56
78661a6

2.9.2

  • FIX: Fix issue where :is() and :where() were not accounting for empty selectors in the max selector count as
    they should (@arpitjain099).
  • FIX: Fix issue where :has() was allowing empty selectors in some circumstances even though it is not
    forgiving.
  • FIX: Reduce selector object size when :is() and :where() contain empty selectors.