Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions soupsieve/css_match.py
Original file line number Diff line number Diff line change
Expand Up @@ -447,9 +447,18 @@ def validate_day(year: int, month: int, day: int) -> bool:
def validate_week(year: int, week: int) -> bool:
"""Validate week."""

max_week = datetime.strptime(f"{12}-{31}-{year}", "%m-%d-%Y").isocalendar()[1]
if max_week == 1:
max_week = 53
# Validate an ISO week number for `year`.
#
# Per ISO 8601 rules, the last ISO week of a year is the week
# containing Dec 28. Using Dec 28 guarantees we obtain the
# correct ISO week-number for the final week of `year`, even in
# years where Dec 31 falls in ISO week 01 of the following year.
#
# Example: if Dec 31 is a Thursday the year's last ISO week will
# be week 53; if Dec 31 is a Monday and that week is counted as
# week 1 of the next year, Dec 28 still belongs to the final
# week of the current ISO year and yields the correct max week.
max_week = datetime(year, 12, 28).isocalendar()[1]
return 1 <= week <= max_week

@staticmethod
Expand Down
14 changes: 9 additions & 5 deletions soupsieve/css_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -1091,16 +1091,20 @@ def parse_selectors(
# Some patterns require additional logic, such as default. We try to make these the
# last pattern, and append the appropriate flag to that selector which communicates
# to the matcher what additional logic is required.
# Preserve any flags that were set during parsing (e.g. :empty, :root)
Copy link
Owner

Choose a reason for hiding this comment

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

I would need to see evidence via new test(s), showing why this is needed.

# by combining them with these special processing flags. Using
# assignment here would overwrite previously-set flags and break
# matching logic that depends on combined bitflags.
if is_default:
selectors[-1].flags = ct.SEL_DEFAULT
selectors[-1].flags |= ct.SEL_DEFAULT
if is_indeterminate:
selectors[-1].flags = ct.SEL_INDETERMINATE
selectors[-1].flags |= ct.SEL_INDETERMINATE
if is_in_range:
selectors[-1].flags = ct.SEL_IN_RANGE
selectors[-1].flags |= ct.SEL_IN_RANGE
if is_out_of_range:
selectors[-1].flags = ct.SEL_OUT_OF_RANGE
selectors[-1].flags |= ct.SEL_OUT_OF_RANGE
if is_placeholder_shown:
selectors[-1].flags = ct.SEL_PLACEHOLDER_SHOWN
selectors[-1].flags |= ct.SEL_PLACEHOLDER_SHOWN

# Return selector list
return ct.SelectorList([s.freeze() for s in selectors], is_not, is_html)
Expand Down
38 changes: 30 additions & 8 deletions soupsieve/css_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,21 @@ def _validate(self, arg: dict[str, str] | Iterable[tuple[str, str]]) -> None:
"""Validate arguments."""

if isinstance(arg, dict):
if not all(isinstance(v, str) for v in arg.values()):
raise TypeError(f'{self.__class__.__name__} values must be hashable')
elif not all(isinstance(k, str) and isinstance(v, str) for k, v in arg):
raise TypeError(f'{self.__class__.__name__} keys and values must be Unicode strings')
if not all(
isinstance(k, str) and isinstance(v, str)
for k, v in arg.items()
):
raise TypeError(
f'{self.__class__.__name__} keys and values must be Unicode strings'
)
elif not all(
isinstance(k, str) and isinstance(v, str)
for k, v in arg
):
raise TypeError(
f'{self.__class__.__name__} keys and values '
'must be Unicode strings'
)


class CustomSelectors(ImmutableDict):
Expand All @@ -174,10 +185,21 @@ def _validate(self, arg: dict[str, str] | Iterable[tuple[str, str]]) -> None:
"""Validate arguments."""

if isinstance(arg, dict):
if not all(isinstance(v, str) for v in arg.values()):
raise TypeError(f'{self.__class__.__name__} values must be hashable')
elif not all(isinstance(k, str) and isinstance(v, str) for k, v in arg):
raise TypeError(f'{self.__class__.__name__} keys and values must be Unicode strings')
if not all(
isinstance(k, str) and isinstance(v, str)
for k, v in arg.items()
):
raise TypeError(
f'{self.__class__.__name__} keys and values must be Unicode strings'
)
elif not all(
isinstance(k, str) and isinstance(v, str)
for k, v in arg
):
raise TypeError(
f'{self.__class__.__name__} keys and values '
'must be Unicode strings'
)


class Selector(Immutable):
Expand Down
5 changes: 5 additions & 0 deletions soupsieve/pretty.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,9 @@ def pretty(obj: Any) -> str: # pragma: no cover
output.append(f'{m.group(1)} ')
break

# prevent never-ending loop
if m is None:
# Skip character that doesn't match any token
index += 1

return ''.join(output)
2 changes: 1 addition & 1 deletion soupsieve/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def get_pattern_context(pattern: str, index: int) -> tuple[str, int, int]:
col = index - last + 1
elif last <= index < m.end(0):
indent = '--> '
offset = (-1 if index > m.start(0) else 0) + 3
Copy link
Owner

Choose a reason for hiding this comment

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

I'm not convinced this fixes anything, but tests show it regresses one of our tests. I'm open to making a change if a real case can be demonstrated, but it seems like the logic would have to be altered to keep the existing tests working and solve the new test case, if one was shown to need fixing.

offset = (-1 if index > m.start(0) else 0) + 2
col = index - last + 1
else:
indent = ' '
Expand Down
Loading