{"record":{"id":"07097aef898e8717","repo":"D4Vinci/Scrapling","slug":"the-functional-pseudo-element-pseudo-element-na","errorCode":null,"errorMessage":"The functional pseudo-element ::{pseudo_element.name}() is unknown","messagePattern":"The functional pseudo-element ::(.+?)\\(\\) is unknown","errorType":"exception","errorClass":"ExpressionError","httpStatus":null,"severity":"error","filePath":"scrapling/core/translator.py","lineNumber":99,"sourceCode":"    \"\"\"This mixin adds support to CSS pseudo elements via dynamic dispatch.\n\n    Currently supported pseudo-elements are ``::text`` and ``::attr(ATTR_NAME)``.\n    \"\"\"\n\n    def xpath_element(self: TranslatorProtocol, selector: Element) -> XPathExpr:\n        # https://github.com/python/mypy/issues/14757\n        xpath = super().xpath_element(selector)  # type: ignore[safe-super]\n        return XPathExpr.from_xpath(xpath)\n\n    def xpath_pseudo_element(self, xpath: OriginalXPathExpr, pseudo_element: PseudoElement) -> OriginalXPathExpr:\n        \"\"\"\n        Dispatch method that transforms XPath to support the pseudo-element.\n        \"\"\"\n        if isinstance(pseudo_element, FunctionalPseudoElement):\n            method_name = f\"xpath_{pseudo_element.name.replace('-', '_')}_functional_pseudo_element\"\n            method = getattr(self, method_name, None)\n            if not method:  # pragma: no cover\n                raise ExpressionError(f\"The functional pseudo-element ::{pseudo_element.name}() is unknown\")\n            xpath = method(xpath, pseudo_element)\n        else:\n            method_name = f\"xpath_{pseudo_element.replace('-', '_')}_simple_pseudo_element\"\n            method = getattr(self, method_name, None)\n            if not method:  # pragma: no cover\n                raise ExpressionError(f\"The pseudo-element ::{pseudo_element} is unknown\")\n            xpath = method(xpath)\n        return xpath\n\n    @staticmethod\n    def xpath_attr_functional_pseudo_element(xpath: OriginalXPathExpr, function: FunctionalPseudoElement) -> XPathExpr:\n        \"\"\"Support selecting attribute values using ::attr() pseudo-element\"\"\"\n        if function.argument_types() not in ([\"STRING\"], [\"IDENT\"]):  # pragma: no cover\n            raise ExpressionError(f\"Expected a single string or ident for ::attr(), got {function.arguments!r}\")\n        return XPathExpr.from_xpath(xpath, attribute=function.arguments[0].value)\n\n    @staticmethod\n    def xpath_text_simple_pseudo_element(xpath: OriginalXPathExpr) -> XPathExpr:","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/D4Vinci/Scrapling/blob/5d213a2d4764002bfc4fed33c32fe09fa8b0bf7f/scrapling/core/translator.py#L81-L117","documentation":"Raised by `xpath_pseudo_element` in Scrapling's translator (scrapling/core/translator.py:99). When translating a CSS selector, each pseudo-element is dispatched to a handler method named `xpath_<name>_functional_pseudo_element`. Scrapling implements `::attr()`; any other functional pseudo-element (one written with parentheses, e.g. `::nth-child(2)` misused as pseudo-element, `::lang(en)`, `::dir(rtl)`) has no handler, so an `ExpressionError` (cssselect's exception, exported for compatibility) is raised.","triggerScenarios":"Calling `selector.css('p::lang(en)')`, `selector.css('div::dir(rtl)')`, or any `::<name>(...)` pseudo-element other than `::attr()` in Scrapling. Anything with parentheses that isn't `::attr(...)` hits this branch.","commonSituations":"Copy-pasting selectors written for Scrapy (which supports a similar set) or from browser DevTools; assuming all CSS pseudo-elements work because `::text` and `::attr()` do; typos like `::atrr(href)`.","solutions":["Restrict pseudo-elements in Scrapling selectors to the supported set: `::text`, `::attr(name)` (and standard structural pseudo-classes like `:nth-child()` written with one colon, which cssselect handles natively).","Replace unsupported functional pseudo-elements with equivalent XPath: `p::lang(en)` -> select then filter by `element.lang` attribute in Python.","If you need browser-grade CSS, evaluate the selector in the browser engine via the fetcher's `page_action`/JS instead of the translator.","Check the exact selector string for typos before assuming a missing feature."],"exampleFix":"# before\nlinks = page.css('a::attr(href)')\nlang_paras = page.css('p::lang(en)')  # ExpressionError\n\n# after\nlinks = page.css('a::attr(href)')\nlang_paras = [p for p in page.css('p') if p.attrib.get('lang', '').startswith('en')]","handlingStrategy":"validation","validationCode":"import re\n\nSUPPORTED_FUNCTIONAL = {'attr'}\n\ndef selector_uses_supported_pseudo_elements(css: str) -> bool:\n    found = re.findall(r'::([a-zA-Z-]+)\\(', css)\n    return all(name in SUPPORTED_FUNCTIONAL for name in found)","typeGuard":null,"tryCatchPattern":"from cssselect.parser import ExpressionError  # same class scrapling re-raises\ntry:\n    vals = page.css('p::lang(en)')\nexcept Exception as e:\n    if 'pseudo-element' in str(e):\n        vals = [p for p in page.css('p') if p.attrib.get('lang', '').startswith('en')]\n    else:\n        raise","preventionTips":["Restrict Scrapling pseudo-elements to ::text and ::attr(name).","Use structural pseudo-classes (:nth-child) with one colon, not pseudo-elements.","Keep a whitelist check on dynamically assembled selector strings."],"tags":["css-selectors","translator","expression-error","scrapling"],"backgroundTag":null,"analyzedSha":"5d213a2d4764002bfc4fed33c32fe09fa8b0bf7f","analyzedAt":"2026-08-14T22:23:09.440Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}