def _patch_case(self) -> None:
        self.classes = {}
        for clazz in self.api:
            members = {}
            self.classes[clazz["name"]] = clazz
            for member in clazz["members"]:
                if member["kind"] == "event":
                    continue
                method_name = member["name"]
                new_name = to_snake_case(method_name)
                alias = (member["langs"].get("aliases").get("python")
                         if member["langs"].get("aliases") else None)
                if alias:
                    new_name = alias
                members[new_name] = member
                member["name"] = new_name

                if "args" in member:
                    args = {}
                    for arg in member["args"]:
                        arg_name = arg["name"]
                        new_name = to_snake_case(arg_name)
                        expand_type = None
                        expand_as_optional = False
                        if arg_name == "options":
                            expand_type = arg["type"]
                            expand_as_optional = True
                        if (method_name == "setViewportSize"
                                and arg_name == "viewportSize"):
                            expand_type = arg["type"]
                        if arg_name == "frameSelector":
                            expand_type = arg["type"]["union"][1]

                        if expand_type:
                            for opt_property in expand_type["properties"]:
                                opt_name = opt_property["name"]
                                if opt_name == "recordHar" or opt_name == "recordVideo":
                                    for sub_property in opt_property["type"][
                                            "properties"]:
                                        sub_name = sub_property["name"]
                                        new_sub_name = to_snake_case(
                                            opt_name + sub_name[0:1].upper() +
                                            sub_name[1:])
                                        args[new_sub_name] = sub_property
                                        sub_property["name"] = new_sub_name
                                        sub_property["required"] = False
                                else:
                                    args[to_snake_case(
                                        opt_name)] = opt_property
                                    opt_property["name"] = to_snake_case(
                                        opt_name)
                                    if expand_as_optional:
                                        opt_property["required"] = False
                        else:
                            args[new_name] = arg
                            arg["name"] = new_name

                    member["args"] = args

            clazz["members"] = members
    def _patch_descriptions(self) -> None:
        map: Dict[str, str] = {}
        for class_name in self.api:
            clazz = self.api[class_name]
            js_class = ""
            if class_name.startswith("JS"):
                js_class = "jsHandle"
            if class_name.startswith("CDP"):
                js_class = "cdpSession"
            else:
                js_class = class_name[0:1].lower() + class_name[1:]
            for method_name in clazz["methods"]:
                camel_case = js_class + "." + method_name
                snake_case = (
                    to_snake_case(class_name) + "." + to_snake_case(method_name)
                )
                map[camel_case] = snake_case

        for [name, value] in map.items():
            for class_name in self.api:
                clazz = self.api[class_name]
                for method_name in clazz["methods"]:
                    method = clazz["methods"][method_name]
                    if "comment" in method:
                        method["comment"] = method["comment"].replace(name, value)
                    if "args" in method:
                        for _, arg in method["args"].items():
                            if "comment" in arg:
                                arg["comment"] = arg["comment"].replace(name, value)
Exemplo n.º 3
0
    def _patch_case(self) -> None:
        self.classes = {}
        for clazz in self.api:
            if not works_for_python(clazz):
                continue
            members = {}
            self.classes[clazz["name"]] = clazz
            events = []
            for member in clazz["members"]:
                if not works_for_python(member):
                    continue
                member_name = member["name"]
                new_name = name_or_alias(member)
                self._add_link(member["kind"], clazz["name"], member_name,
                               new_name)

                if member["kind"] == "event":
                    events.append(member)
                else:
                    new_name = to_snake_case(new_name)
                    member["name"] = new_name
                    members[new_name] = member
                apply_type_or_override(member)

                if "args" in member:
                    args = {}
                    for arg in member["args"]:
                        if not works_for_python(arg):
                            continue
                        if arg["name"] == "options":
                            for option in arg["type"]["properties"]:
                                if not works_for_python(option):
                                    continue
                                option = self_or_override(option)
                                option_name = to_snake_case(
                                    name_or_alias(option))
                                option["name"] = option_name
                                option["required"] = False
                                args[option_name] = option
                        else:
                            arg = self_or_override(arg)
                            arg_name = to_snake_case(name_or_alias(arg))
                            arg["name"] = arg_name
                            args[arg_name] = arg

                    member["args"] = args

            clazz["members"] = members
            clazz["events"] = events
 def _add_link(self, kind: str, clazz: str, member: str, alias: str) -> None:
     match = re.match(r"(JS|CDP|[A-Z])([^.]+)", clazz)
     if not match:
         raise Exception("Invalid class " + clazz)
     var_name = to_snake_case(f"{match.group(1).lower()}{match.group(2)}")
     new_name = to_snake_case(alias)
     if kind == "event":
         new_name = new_name.lower()
         self.links[
             f"[`event: {clazz}.{member}`]"
         ] = f"`{var_name}.on('{new_name}')`"
     elif kind == "property":
         self.links[f"[`property: {clazz}.{member}`]"] = f"`{var_name}.{new_name}`"
     else:
         self.links[f"[`method: {clazz}.{member}`]"] = f"`{var_name}.{new_name}()`"
Exemplo n.º 5
0
    def inner_serialize_doc_type(self, type: Any) -> str:
        if type["name"] == "Promise":
            type = type["templates"][0]

        if "union" in type:
            ll = [self.serialize_doc_type(t) for t in type["union"]]
            ll.sort(key=lambda item: "}" if item == "NoneType" else item)
            return f"Union[{', '.join(ll)}]"

        type_name = type["name"]
        if type_name == "path":
            return "Union[pathlib.Path, str]"

        if type_name == "function" and "args" not in type:
            return "Callable"

        if type_name == "function":
            return_type = "Any"
            if type.get("returnType"):
                return_type = self.serialize_doc_type(type["returnType"])
            return f"Callable[[{', '.join(self.serialize_doc_type(t) for t in type['args'])}], {return_type}]"

        if "templates" in type:
            base = type_name
            if type_name == "Array":
                base = "List"
            if type_name == "Object" or type_name == "Map":
                base = "Dict"
            return f"{base}[{', '.join(self.serialize_doc_type(t) for t in type['templates'])}]"

        if type_name == "Object" and "properties" in type:
            items = []
            for p in type["properties"]:
                items.append(
                    to_snake_case(p["name"]) + ": " +
                    (self.serialize_doc_type(p["type"]) if p["required"] else
                     self.make_optional(self.serialize_doc_type(p["type"]))))
            return f"{{{', '.join(items)}}}"

        if type_name == "boolean":
            return "bool"
        if type_name == "string":
            return "str"
        if type_name == "Object" or type_name == "Serializable":
            return "Any"
        if type_name == "Buffer":
            return "bytes"
        if type_name == "URL":
            return "str"
        if type_name == "RegExp":
            return "Pattern"
        if type_name == "null":
            return "NoneType"
        if type_name == "EvaluationArgument":
            return "Dict"
        return type["name"]
Exemplo n.º 6
0
    def _patch_case(self) -> None:
        self.classes = {}
        for clazz in self.api:
            members = {}
            self.classes[clazz["name"]] = clazz
            for member in clazz["members"]:
                member_name = member["name"]
                alias = (member["langs"].get("aliases").get("python")
                         if member["langs"].get("aliases") else None)
                new_name = member_name
                if alias:
                    new_name = alias
                self._add_link(member["kind"], clazz["name"], member_name,
                               new_name)

                if member["kind"] == "event":
                    continue

                new_name = to_snake_case(new_name)
                member["name"] = new_name
                members[new_name] = member
                if member["langs"].get(
                        "types") and member["langs"]["types"].get("python"):
                    member["type"] = member["langs"]["types"]["python"]

                if "args" in member:
                    args = {}
                    for arg in member["args"]:
                        arg_name = arg["name"]
                        new_name = to_snake_case(arg_name)
                        if arg_name == "options":
                            for opt_property in arg["type"]["properties"]:
                                opt_name = opt_property["name"]
                                args[to_snake_case(opt_name)] = opt_property
                                opt_property["name"] = to_snake_case(opt_name)
                                opt_property["required"] = False
                        else:
                            args[new_name] = arg
                            arg["name"] = new_name

                    member["args"] = args

            clazz["members"] = members
def generate(t: Any) -> None:
    print("")
    class_name = short_name(t)
    base_class = t.__bases__[0].__name__
    base_sync_class = ("SyncBase" if base_class == "ChannelOwner"
                       or base_class == "object" else base_class)
    print(f"class {class_name}({base_sync_class}):")
    print("")
    print(f"    def __init__(self, obj: {class_name}Impl):")
    print("        super().__init__(obj)")
    for [name, type] in get_type_hints(t, api_globals).items():
        print("")
        print("    @property")
        print(f"    def {to_snake_case(name)}(self) -> {process_type(type)}:")
        documentation_provider.print_entry(class_name, to_snake_case(name),
                                           {"return": type})
        [prefix, suffix] = return_value(type)
        prefix = "        return " + prefix + f"self._impl_obj.{name}"
        print(f"{prefix}{suffix}")
    for [name, value] in t.__dict__.items():
        if name.startswith("_"):
            continue
        if not name.startswith("_") and str(value).startswith("<property"):
            value = value.fget
            print("")
            print("    @property")
            print(
                f"    def {to_snake_case(name)}({signature(value, len(name) + 9)}) -> {return_type(value)}:"
            )
            documentation_provider.print_entry(
                class_name, to_snake_case(name),
                get_type_hints(value, api_globals))
            [prefix, suffix
             ] = return_value(get_type_hints(value, api_globals)["return"])
            prefix = "        return " + prefix + f"self._impl_obj.{name}"
            print(f"{prefix}{arguments(value, len(prefix))}{suffix}")
    for [name, value] in t.__dict__.items():
        if name in ["expect_dialog"]:
            continue
        if (not name.startswith("_") and isinstance(value, FunctionType)
                and "expect_" not in name and "remove_listener" != name):
            is_async = inspect.iscoroutinefunction(value)
            print("")
            print(
                f"    def {to_snake_case(name)}({signature(value, len(name) + 9)}) -> {return_type(value)}:"
            )
            documentation_provider.print_entry(
                class_name, to_snake_case(name),
                get_type_hints(value, api_globals))
            [prefix, suffix
             ] = return_value(get_type_hints(value, api_globals)["return"])
            if is_async:
                prefix = prefix + f"self._sync(self._impl_obj.{name}("
                suffix = "))" + suffix
            else:
                prefix = prefix + f"self._impl_obj.{name}("
                suffix = ")" + suffix

            print(f"""
        try:
            log_api("=> {to_snake_case(class_name)}.{to_snake_case(name)} started")
            result = {prefix}{arguments(value, len(prefix))}{suffix}
            log_api("<= {to_snake_case(class_name)}.{to_snake_case(name)} succeded")
            return result
        except Exception as e:
            log_api("<= {to_snake_case(class_name)}.{to_snake_case(name)} failed")
            raise e""")
        if "expect_" in name:
            print("")
            return_type_value = return_type(value)
            return_type_value = re.sub(r"\"([^\"]+)Impl\"", r"\1",
                                       return_type_value)
            event_name = re.sub(r"expect_(.*)", r"\1", name)
            event_name = re.sub(r"_", "", event_name)
            event_name = re.sub(r"consolemessage", "console", event_name)

            print(
                f"""    def {name}({signature(value, len(name) + 9)}) -> {return_type_value}:
        \"\"\"{class_name}.{name}

        Returns context manager that waits for ``event`` to fire upon exit. It passes event's value
        into the ``predicate`` function and waits for the predicate to return a truthy value. Will throw
        an error if the page is closed before the ``event`` is fired.

        with page.expect_{event_name}() as event_info:
            page.click("button")
        value = event_info.value

        Parameters
        ----------
        predicate : Optional[typing.Callable[[Any], bool]]
            Predicate receiving event data.
        timeout : Optional[int]
            Maximum wait time in milliseconds, defaults to 30 seconds, pass `0` to disable the timeout.
            The default value can be changed by using the browserContext.setDefaultTimeout(timeout) or
            page.setDefaultTimeout(timeout) methods.
        \"\"\"""")

            wait_for_method = "waitForEvent(event, predicate, timeout)"
            if event_name == "request":
                wait_for_method = "waitForRequest(url_or_predicate, timeout)"
            elif event_name == "response":
                wait_for_method = "waitForResponse(url_or_predicate, timeout)"
            elif event_name == "loadstate":
                wait_for_method = "waitForLoadState(state, timeout)"
            elif event_name == "navigation":
                wait_for_method = "waitForNavigation(url, wait_until, timeout)"
            elif event_name != "event":
                print(f'        event = "{event_name}"')

            print(
                f"        return EventContextManager(self, self._impl_obj.{wait_for_method})"
            )

    print("")
    print(f"mapping.register({class_name}Impl, {class_name})")
Exemplo n.º 8
0
    def print_entry(
        self,
        class_name: str,
        method_name: str,
        signature: Dict[str, Any] = None,
        is_property: bool = False,
    ) -> None:
        if class_name in ["BindingCall"] or method_name in [
                "pid",
                "_add_event_handler",
                "remove_listener",
        ]:
            return
        original_method_name = method_name
        self.printed_entries.append(f"{class_name}.{method_name}")
        clazz = self.classes[class_name]
        method = clazz["members"].get(method_name)
        if not method and "extends" in clazz:
            superclass = self.classes.get(clazz["extends"])
            if superclass:
                method = superclass["members"].get(method_name)
        fqname = f"{class_name}.{method_name}"

        if not method:
            self.errors.add(f"Method not documented: {fqname}")
            return

        doc_is_property = (not method.get("async") and not len(method["args"])
                           and "type" in method)
        if method["name"].startswith("is_") or method["name"].startswith(
                "as_"):
            doc_is_property = False
        if doc_is_property != is_property:
            self.errors.add(f"Method vs property mismatch: {fqname}")
            return

        indent = " " * 8
        print(f'{indent}"""{class_name}.{to_snake_case(original_method_name)}')
        if method.get("comment"):
            print(
                f"{indent}{self.beautify_method_comment(method['comment'], indent)}"
            )
        signature_no_return = {**signature} if signature else None
        if signature_no_return and "return" in signature_no_return:
            del signature_no_return["return"]

        # Collect a list of all names, flatten options.
        args = method["args"]
        if signature and signature_no_return:
            print("")
            print("        Parameters")
            print("        ----------")
            for [name, value] in signature.items():
                name = to_snake_case(name)
                if name == "return":
                    continue
                original_name = name
                doc_value = args.get(name)
                if name in args:
                    del args[name]
                if not doc_value:
                    self.errors.add(
                        f"Parameter not documented: {fqname}({name}=)")
                else:
                    code_type = self.serialize_python_type(value)

                    print(
                        f"{indent}{to_snake_case(original_name)} : {code_type}"
                    )
                    if doc_value.get("comment"):
                        print(
                            f"{indent}    {self.indent_paragraph(self.render_links(doc_value['comment']), f'{indent}    ')}"
                        )
                    self.compare_types(code_type, doc_value,
                                       f"{fqname}({name}=)", "in")
        if (signature and "return" in signature
                and str(signature["return"]) != "<class 'NoneType'>"):
            value = signature["return"]
            doc_value = method
            self.compare_types(value, doc_value, f"{fqname}(return=)", "out")
            print("")
            print("        Returns")
            print("        -------")
            print(f"        {self.serialize_python_type(value)}")
        print(f'{indent}"""')

        for name in args:
            if args[name].get("deprecated"):
                continue
            self.errors.add(
                f"Parameter not implemented: {class_name}.{method_name}({name}=)"
            )
Exemplo n.º 9
0
    def _patch_case(self) -> None:
        self.classes = {}
        for clazz in self.api:
            members = {}
            self.classes[clazz["name"]] = clazz
            for member in clazz["members"]:
                if member["kind"] == "event":
                    continue
                method_name = member["name"]
                new_name = to_snake_case(method_name)
                if method_name == "continue":
                    new_name = "continue_"
                if method_name == "$eval":
                    new_name = "eval_on_selector"
                if method_name == "$$eval":
                    new_name = "eval_on_selector_all"
                if method_name == "$":
                    new_name = "query_selector"
                if method_name == "$$":
                    new_name = "query_selector_all"
                members[new_name] = member
                member["name"] = new_name

                if "args" in member:
                    args = {}
                    for arg in member["args"]:
                        arg_name = arg["name"]
                        new_name = to_snake_case(arg_name)
                        if arg_name == "pageFunction":
                            new_name = "expression"

                        expand_type = None
                        expand_as_optional = False
                        if arg_name == "options":
                            expand_type = arg["type"]
                            expand_as_optional = True
                        if arg_name == "optionsOrPredicate":
                            expand_type = arg["type"]["union"][1]
                            expand_as_optional = True
                        if arg_name == "params" and "properties" in arg["type"]:
                            expand_type = arg["type"]
                        if method_name == "emulateMedia" and arg_name == "params":
                            expand_type = arg["type"]
                        if method_name == "fulfill" and arg_name == "response":
                            expand_type = arg["type"]
                        if method_name == "continue" and arg_name == "overrides":
                            expand_type = arg["type"]
                        if (method_name == "setViewportSize"
                                and arg_name == "viewportSize"):
                            expand_type = arg["type"]
                        if arg_name == "geolocation":
                            expand_type = arg["type"]["union"][1]
                        if arg_name == "frameSelector":
                            expand_type = arg["type"]["union"][1]

                        if expand_type:
                            for opt_property in expand_type["properties"]:
                                opt_name = opt_property["name"]
                                if opt_name == "recordHar" or opt_name == "recordVideo":
                                    for sub_property in opt_property["type"][
                                            "properties"]:
                                        sub_name = sub_property["name"]
                                        new_sub_name = to_snake_case(
                                            opt_name + sub_name[0:1].upper() +
                                            sub_name[1:])
                                        args[new_sub_name] = sub_property
                                        sub_property["name"] = new_sub_name
                                        sub_property["required"] = False
                                else:
                                    args[to_snake_case(
                                        opt_name)] = opt_property
                                    opt_property["name"] = to_snake_case(
                                        opt_name)
                                    if expand_as_optional:
                                        opt_property["required"] = False
                        else:
                            args[new_name] = arg
                            arg["name"] = new_name

                    member["args"] = args

            clazz["members"] = members
Exemplo n.º 10
0
    def print_entry(self,
                    class_name: str,
                    method_name: str,
                    signature: Dict[str, Any] = None) -> None:
        if class_name in ["BindingCall"] or method_name in [
                "pid",
                "_add_event_handler",
                "remove_listener",
        ]:
            return
        original_method_name = method_name
        self.printed_entries.append(f"{class_name}.{method_name}")
        if class_name == "JSHandle":
            self.printed_entries.append(f"ElementHandle.{method_name}")
        clazz = self.classes[class_name]
        method = clazz["members"].get(method_name)
        if not method and "extends" in clazz:
            superclass = self.classes.get(clazz["extends"])
            if superclass:
                method = superclass["members"].get(method_name)
        fqname = f"{class_name}.{method_name}"

        if not method:
            self.errors.add(f"Method not documented: {fqname}")
            return

        indent = " " * 8
        print(f'{indent}"""{class_name}.{to_snake_case(original_method_name)}')
        if method.get("comment"):
            print(
                f"{indent}{self.beautify_method_comment(method['comment'], indent)}"
            )
        signature_no_return = {**signature} if signature else None
        if signature_no_return and "return" in signature_no_return:
            del signature_no_return["return"]

        # Collect a list of all names, flatten options.
        args = method["args"]
        if signature and signature_no_return:
            print("")
            print("        Parameters")
            print("        ----------")
            for [name, value] in signature.items():
                name = to_snake_case(name)
                if name == "return":
                    continue
                if name == "force_expr":
                    continue
                original_name = name
                doc_value = args.get(name)
                if name in args:
                    del args[name]
                if not doc_value:
                    self.errors.add(
                        f"Parameter not documented: {fqname}({name}=)")
                else:
                    code_type = self.serialize_python_type(value)

                    print(
                        f"{indent}{to_snake_case(original_name)} : {code_type}"
                    )
                    if doc_value.get("comment"):
                        print(
                            f"{indent}    {self.indent_paragraph(self.render_links(doc_value['comment']), f'{indent}    ')}"
                        )
                    if original_name == "expression":
                        print(f"{indent}force_expr : bool")
                        print(
                            f"{indent}    Whether to treat given expression as JavaScript evaluate expression, even though it looks like an arrow function"
                        )
                    self.compare_types(code_type, doc_value,
                                       f"{fqname}({name}=)")
        if (signature and "return" in signature
                and str(signature["return"]) != "<class 'NoneType'>"):
            value = signature["return"]
            doc_value = method
            self.compare_types(value, doc_value, f"{fqname}(return=)")
            print("")
            print("        Returns")
            print("        -------")
            print(f"        {self.serialize_python_type(value)}")
        print(f'{indent}"""')

        for name in args:
            self.errors.add(
                f"Parameter not implemented: {class_name}.{method_name}({name}=)"
            )