Skip to content

Commit 94ca200

Browse files
author
Onimock
committed
Remove: restriction on pseudo-states in comma-separated selectors and enhance rule splitting
1 parent f294556 commit 94ca200

File tree

2 files changed

+265
-14
lines changed

2 files changed

+265
-14
lines changed

src/qss_parser/qss_parser.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -599,12 +599,6 @@ def validate_selector_syntax(selector: str, line_num: int) -> List[str]:
599599
f"Error on line {line_num}: Duplicate selector '{sel}' in comma-separated list"
600600
)
601601
seen_selectors.add(sel)
602-
if ":" in sel and not sel.endswith(":"):
603-
errors.append(
604-
f"Error: Pseudo-states in comma-separated selectors are not supported. "
605-
f"Split into separate rules for {selector}"
606-
)
607-
return errors
608602

609603
for sel in selectors:
610604
attributes = SelectorUtils.extract_attributes(sel)
@@ -1204,7 +1198,7 @@ def _process_complete_rule(
12041198
continue
12051199
try:
12061200
self._property_processor.process_property(
1207-
prop_line + ";",
1201+
prop_line + (";" if not prop_line.endswith(";") else ""),
12081202
state.current_rules,
12091203
variable_manager,
12101204
state.current_line,

tests/test_qss_parser.py

Lines changed: 264 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -294,22 +294,28 @@ def test_parse_multiple_selectors_with_pseudo_state(self) -> None:
294294
Test parsing QSS with multiple selectors including pseudo-_states.
295295
"""
296296
qss: str = """
297-
#myButton:hover, QFrame:disabled {
297+
#myButton:hover, QFrame:disabled, #secondButton QPushButton, #anyButton QCheckBox::drop-down:disabled {
298298
color: red;
299+
width: 15px;
300+
height: 15px;
301+
border-radius: 10px;
299302
}
300303
"""
301304
self.parser.parse(qss)
302305
self.assertEqual(
303306
len(self.parser._state.rules),
304-
0,
305-
"Pseudo-states in comma-separated selectors should be rejected",
307+
4,
308+
"Pseudo-states in comma-separated selectors should accept",
309+
)
310+
self.assertEqual(
311+
len(self.parser._state.rules[0].properties),
312+
4,
313+
"Pseudo-states in comma-separated selectors should accept",
306314
)
307315
self.assertEqual(
308316
self.errors,
309-
[
310-
"Error: Pseudo-states in comma-separated selectors are not supported. Split into separate rules for #myButton:hover, QFrame:disabled"
311-
],
312-
"Should report pseudo-state error",
317+
[],
318+
"Should not report pseudo-state error",
313319
)
314320

315321
def test_parse_attribute_selector_complex(self) -> None:
@@ -539,6 +545,94 @@ def test_parse_single_line_multiple_properties(self) -> None:
539545
self.assertEqual(rule.properties[3].value, "#ffffff")
540546
self.assertEqual(self.errors, [], "Single-line rule should produce no errors")
541547

548+
def test_parsing_multiple_selectors_separate_with_comma_in_single_line_with_pseudo_state(
549+
self,
550+
) -> None:
551+
"""
552+
Test parsing QSS with multiple selectors including pseudo-_states.
553+
"""
554+
qss: str = """
555+
#myButton:hover, QFrame:disabled, #secondButton QPushButton, #anyButton QCheckBox::drop-down:disabled {
556+
color: red;
557+
width: 15px;
558+
height: 15px;
559+
border-radius: 10px;
560+
}
561+
"""
562+
self.parser.parse(qss)
563+
self.assertEqual(len(self.parser._state.rules), 4, "Should parse 4 rule")
564+
rule = self.parser._state.rules[3]
565+
self.assertEqual(rule.selector, "#anyButton QCheckBox::drop-down:disabled")
566+
self.assertEqual(rule.pseudo_states[0], "disabled", "Should have pseudo-state")
567+
self.assertEqual(len(rule.properties), 4, "Should parse all four properties")
568+
self.assertEqual(rule.properties[0].name, "color")
569+
self.assertEqual(rule.properties[0].value, "red")
570+
self.assertEqual(rule.properties[1].name, "width")
571+
self.assertEqual(rule.properties[1].value, "15px")
572+
self.assertEqual(rule.properties[2].name, "height")
573+
self.assertEqual(rule.properties[2].value, "15px")
574+
self.assertEqual(rule.properties[3].name, "border-radius")
575+
self.assertEqual(rule.properties[3].value, "10px")
576+
self.assertEqual(self.errors, [], "Single-line rule should produce no errors")
577+
578+
def test_parsing_multiple_selectors_separate_with_comma_all_single_line_with_pseudo_state(
579+
self,
580+
) -> None:
581+
"""
582+
Test parsing QSS with multiple selectors including pseudo-_states.
583+
"""
584+
qss: str = """
585+
#myButton:hover, QFrame:disabled, #secondButton QPushButton, #anyButton QCheckBox::drop-down:disabled { color: red; width: 15px; height: 15px; border-radius: 10px;}
586+
"""
587+
self.parser.parse(qss)
588+
self.assertEqual(len(self.parser._state.rules), 4, "Should parse 4 rule")
589+
rule = self.parser._state.rules[3]
590+
self.assertEqual(rule.selector, "#anyButton QCheckBox::drop-down:disabled")
591+
self.assertEqual(rule.pseudo_states[0], "disabled", "Should have pseudo-state")
592+
self.assertEqual(len(rule.properties), 4, "Should parse all four properties")
593+
self.assertEqual(rule.properties[0].name, "color")
594+
self.assertEqual(rule.properties[0].value, "red")
595+
self.assertEqual(rule.properties[1].name, "width")
596+
self.assertEqual(rule.properties[1].value, "15px")
597+
self.assertEqual(rule.properties[2].name, "height")
598+
self.assertEqual(rule.properties[2].value, "15px")
599+
self.assertEqual(rule.properties[3].name, "border-radius")
600+
self.assertEqual(rule.properties[3].value, "10px")
601+
self.assertEqual(self.errors, [], "Single-line rule should produce no errors")
602+
603+
def test_parsing_multiple_selectors_separate_with_comma_in_each_line_with_pseudo_state(
604+
self,
605+
) -> None:
606+
"""
607+
Test parsing QSS with multiple selectors including pseudo-_states.
608+
"""
609+
qss: str = """
610+
#myButton:hover,
611+
QFrame:disabled,
612+
#secondButton QPushButton,
613+
#anyButton QCheckBox::drop-down:disabled {
614+
color: red;
615+
width: 15px;
616+
height: 15px;
617+
border-radius: 10px;
618+
}
619+
"""
620+
self.parser.parse(qss)
621+
self.assertEqual(len(self.parser._state.rules), 4, "Should parse 4 rule")
622+
rule = self.parser._state.rules[3]
623+
self.assertEqual(rule.selector, "#anyButton QCheckBox::drop-down:disabled")
624+
self.assertEqual(rule.pseudo_states[0], "disabled", "Should have pseudo-state")
625+
self.assertEqual(len(rule.properties), 4, "Should parse all four properties")
626+
self.assertEqual(rule.properties[0].name, "color")
627+
self.assertEqual(rule.properties[0].value, "red")
628+
self.assertEqual(rule.properties[1].name, "width")
629+
self.assertEqual(rule.properties[1].value, "15px")
630+
self.assertEqual(rule.properties[2].name, "height")
631+
self.assertEqual(rule.properties[2].value, "15px")
632+
self.assertEqual(rule.properties[3].name, "border-radius")
633+
self.assertEqual(rule.properties[3].value, "10px")
634+
self.assertEqual(self.errors, [], "Single-line rule should produce no errors")
635+
542636

543637
class TestQSSParserStyleSelection(unittest.TestCase):
544638
def setUp(self) -> None:
@@ -1181,6 +1275,79 @@ def test_get_styles_for_single_line_multiple_properties(self) -> None:
11811275
errors, [], "Valid variables and properties should produce no errors"
11821276
)
11831277

1278+
def test_get_style_for_multiple_selectors_separate_with_comma_in_single_line_with_pseudo_state(
1279+
self,
1280+
) -> None:
1281+
"""
1282+
Test get_style_for QSS with multiple selectors including pseudo-_states.
1283+
"""
1284+
qss: str = """
1285+
#myButton:hover, QFrame:disabled, #secondButton QPushButton, #anyButton QCheckBox::drop-down:disabled {
1286+
color: red;
1287+
width: 15px;
1288+
height: 15px;
1289+
border-radius: 10px;
1290+
}
1291+
"""
1292+
errors: List[str] = []
1293+
self.parser.on(ParserEvent.ERROR_FOUND, lambda error: errors.append(error))
1294+
self.parser.parse(qss)
1295+
widget: Mock = Mock()
1296+
widget.objectName.return_value = "anyButton"
1297+
widget.metaObject.return_value.className.return_value = "QCheckBox"
1298+
stylesheet: str = self.parser.get_styles_for(widget)
1299+
expected: str = """#anyButton QCheckBox::drop-down:disabled {
1300+
color: red;
1301+
width: 15px;
1302+
height: 15px;
1303+
border-radius: 10px;
1304+
}
1305+
"""
1306+
self.assertEqual(stylesheet.strip(), expected.strip())
1307+
self.assertEqual(
1308+
errors,
1309+
[],
1310+
"Valid selector and pseudo elements/states in separate comma should produce no errors",
1311+
)
1312+
1313+
def test_get_style_for_multiple_selectors_separate_with_comma_in_each_line_with_pseudo_state(
1314+
self,
1315+
) -> None:
1316+
"""
1317+
Test get_style_for QSS with multiple selectors including pseudo-_states.
1318+
"""
1319+
qss: str = """
1320+
#myButton:hover,
1321+
QFrame:disabled,
1322+
#secondButton QPushButton,
1323+
#anyButton QCheckBox::drop-down:disabled {
1324+
color: red;
1325+
width: 15px;
1326+
height: 15px;
1327+
border-radius: 10px;
1328+
}
1329+
"""
1330+
errors: List[str] = []
1331+
self.parser.on(ParserEvent.ERROR_FOUND, lambda error: errors.append(error))
1332+
self.parser.parse(qss)
1333+
widget: Mock = Mock()
1334+
widget.objectName.return_value = "anyButton"
1335+
widget.metaObject.return_value.className.return_value = "QCheckBox"
1336+
stylesheet: str = self.parser.get_styles_for(widget)
1337+
expected: str = """#anyButton QCheckBox::drop-down:disabled {
1338+
color: red;
1339+
width: 15px;
1340+
height: 15px;
1341+
border-radius: 10px;
1342+
}
1343+
"""
1344+
self.assertEqual(stylesheet.strip(), expected.strip())
1345+
self.assertEqual(
1346+
errors,
1347+
[],
1348+
"Valid selector and pseudo elements/states in separate comma should produce no errors",
1349+
)
1350+
11841351

11851352
class TestQSSParserEvents(unittest.TestCase):
11861353
def setUp(self) -> None:
@@ -1459,6 +1626,96 @@ def test_to_string_for_single_line_multiple_properties(self) -> None:
14591626
self.errors, [], "Valid variables and properties should produce no errors"
14601627
)
14611628

1629+
def test_to_string_multiple_selectors_separate_with_comma_all_single_line_with_pseudo_state(
1630+
self,
1631+
) -> None:
1632+
"""
1633+
Test to_string QSS with multiple selectors including pseudo-_states.
1634+
"""
1635+
qss: str = """
1636+
#myButton:hover, QFrame:disabled, #secondButton QPushButton, #anyButton QCheckBox::drop-down:disabled { color: red; width: 15px; height: 15px; border-radius: 10px;}
1637+
"""
1638+
self.parser.parse(qss)
1639+
expected = """#myButton:hover {
1640+
color: red;
1641+
width: 15px;
1642+
height: 15px;
1643+
border-radius: 10px;
1644+
}
1645+
1646+
QFrame:disabled {
1647+
color: red;
1648+
width: 15px;
1649+
height: 15px;
1650+
border-radius: 10px;
1651+
}
1652+
1653+
#secondButton QPushButton {
1654+
color: red;
1655+
width: 15px;
1656+
height: 15px;
1657+
border-radius: 10px;
1658+
}
1659+
1660+
#anyButton QCheckBox::drop-down:disabled {
1661+
color: red;
1662+
width: 15px;
1663+
height: 15px;
1664+
border-radius: 10px;
1665+
}
1666+
"""
1667+
self.assertEqual(self.parser.to_string().strip(), expected.strip())
1668+
self.assertEqual(self.errors, [], "Single-line rule should produce no errors")
1669+
1670+
def test_parsing_multiple_selectors_separate_with_comma_in_each_line_with_pseudo_state(
1671+
self,
1672+
) -> None:
1673+
"""
1674+
Test parsing QSS with multiple selectors including pseudo-_states.
1675+
"""
1676+
qss: str = """
1677+
#myButton:hover,
1678+
QFrame:disabled,
1679+
#secondButton QPushButton,
1680+
#anyButton QCheckBox::drop-down:disabled {
1681+
color: red;
1682+
width: 15px;
1683+
height: 15px;
1684+
border-radius: 10px;
1685+
}
1686+
"""
1687+
self.parser.parse(qss)
1688+
expected = """#myButton:hover {
1689+
color: red;
1690+
width: 15px;
1691+
height: 15px;
1692+
border-radius: 10px;
1693+
}
1694+
1695+
QFrame:disabled {
1696+
color: red;
1697+
width: 15px;
1698+
height: 15px;
1699+
border-radius: 10px;
1700+
}
1701+
1702+
#secondButton QPushButton {
1703+
color: red;
1704+
width: 15px;
1705+
height: 15px;
1706+
border-radius: 10px;
1707+
}
1708+
1709+
#anyButton QCheckBox::drop-down:disabled {
1710+
color: red;
1711+
width: 15px;
1712+
height: 15px;
1713+
border-radius: 10px;
1714+
}
1715+
"""
1716+
self.assertEqual(self.parser.to_string().strip(), expected.strip())
1717+
self.assertEqual(self.errors, [], "Single-line rule should produce no errors")
1718+
14621719

14631720
class TestQSSParserQProperty(unittest.TestCase):
14641721
def setUp(self) -> None:

0 commit comments

Comments
 (0)