方法加入装饰器后,调用不显示参数,且无法联想到后续方法

问题描述:

方法加入 自定义装饰器 后,调用时不显示参数,并且无法联想到后续的方法( click, sendkeys 等)

会报错,但用例正常运行

相关代码

装饰器 handle_black.py

import logging

from appium.webdriver.common.mobileby import MobileBy


def handle_black(func):
    def wrapper(*args, **kwargs):
        logging.basicConfig(level=logging.INFO)
        _black_list = [
            (MobileBy.ID, "mIvClose")
        ]
        _max_err_num = 3
        _error_num = 0
        from Oxygen_UI.page.base_page import BasePage
        instance: BasePage = args[0]
        try:
            logging.info("run " + func.__name__ + "\n args:" + repr(args[1:]) + "\n" + repr(kwargs))
            element = func(*args, **kwargs)
            _error_num = 0
            instance.set_implicitly_wait(5)
            return element

        except Exception as e:
            if _error_num > _max_err_num:
                _error_num = 0
                raise e
            _error_num += 1
            for ele in _black_list:
                eles = instance.finds(ele)
                if len(eles) > 0:
                    eles[0].click()
                    return wrapper(*args, **kwargs)
            raise ValueError("元素不在黑名单中")

    return wrapper

base_page.py

 import json

import yaml
from appium.webdriver.webdriver import WebDriver
from selenium.webdriver.remote.webelement import WebElement
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait

from Oxygen_UI.page.handle_black import handle_black


class BasePage:

    driver: WebDriver
    _params = {}

    def __init__(self, driver: WebDriver = None):
        self.driver = driver

    # 查找元素
    @handle_black
    def find(self, by, locator=None) -> WebElement:
        if locator is None:
            result = self.driver.find_element(*by)
        else:
            result = self.driver.find_element(by, locator)
        return result

    # 查找多个元素
    def finds(self, by, locator=None):
        if locator is None:
            return self.driver.find_elements(*by)
        else:
            return self.driver.find_elements(by, locator)

    # 查找元素并点击
    def find_and_click(self, locator):
        self.find(locator).click()

    # 查找输入
    def find_and_sendkeys(self, locator, value):
        self.find(locator).send_keys(value)

    # 获取属性
    def get_attribute(self, locator, value):
        result = self.find(locator).get_attribute(value)
        return result

    # 获取文本
    # def get_text(self, locator):
    #     result = self.find(locator).text()
    #     return result

    # 设置隐式等待时间
    def set_implicitly_wait(self, second):
        self.driver.implicitly_wait(second)

    # 设置显示等待
    def set_webdriver_wait(self, locator, timeout=10):
        element = WebDriverWait(self.driver, timeout).until(expected_conditions.element_to_be_clickable(locator))
        return element

    # 解析 yaml数据
    def action_steps(self, steps_path, name):
        # 打开yaml文件
        with open(steps_path, encoding='utf-8') as f:
            steps = yaml.safe_load(f)[name]

        raw = json.dumps(steps)
        # yaml内参数化处理
        for key, value in self._params.items():
            raw = raw.replace('${' + key + '}', value)
        steps = json.loads(raw)
        # 判断需要执行的步骤
        for step in steps:
            if 'action' in step.keys():
                action = step['action']
                # 执行点击
                if 'click' == action:
                    self.find_and_click((step['by'], step['locator']))
                # 执行传入文字
                if 'send' == action:
                    self.find_and_sendkeys((step['by'], step['locator']), step['value'])
                # 执行滑动并查找
                if 'swipe_find' == action:
                    self.swipe_find((step['by'], step['locator']))
                # 执行查找
                if 'find' == action:
                    result = self.find(step['by'], step['locator'])
                    return result
                # 执行查找多个元素
                if 'finds' == action:
                    result = self.finds(step['by'], step['locator'])
                    return result
                # 执行获取属性
                if 'get_attribute' == action:
                    self.get_attribute((step['by'], step['locator']), step['value'])

    # 返回
    def back(self, num=1):
        for i in range(num):
            self.driver.back()

    # 读取测试数据
    def load_data(self, data_path):
        with open(data_path, encoding='utf-8') as f:
            result = yaml.safe_load(f)
        return result

    # 截图功能
    def screenshot(self, path):
        self.driver.save_screenshot(path)

    # 滑动查找
    def swipe_find(self, locator):
        self.driver.implicitly_wait(1)
        num = 5
        for i in range(0, num):
            if i == num - 1:
                raise NoSuchElementException(f"找了{i}次,未找到")
            try:
                # element = self.driver.find_element(
                #     MobileBy.XPATH, f"//*[@text='{text}']"
                # )
                element = self.find(locator)
                self.driver.implicitly_wait(5)
                return element
            except NoSuchElementException:
                print("未找到,滑动")
                size = self.driver.get_window_size()
                width = size["width"]
                height = size["height"]
                start_x = width / 2
                start_y = height * 0.8
                end_x = start_x
                end_y = height * 0.3
                duration = 2000
                self.driver.swipe(start_x, start_y, end_x, end_y, duration)

    # 滑动登录
    def swipe_login(self):
        size = self.driver.get_window_size()
        width = size["width"]
        height = size["height"]
        start_x = width * 0.17
        start_y = height * 0.57
        end_x = width * 0.94
        end_y = start_y
        duration = 2000
        self.driver.swipe(start_x, start_y, end_x, end_y, duration)

报错信息:

E       AttributeError: 'NoneType' object has no attribute 'click'

原因以及解决方式(没有可以写无):

无法联想是因为你的装饰器执行之后的返回值类型不唯一,所以pycharm不知道根据哪个类型的返回值来提示,你可以看一下你的装饰器内执行返回值,应该是包含有None等情况

尝试下

@handle_black
def find(self, by, locator=None) :
    if locator is None:
        result = self.driver.find_element(*by)
    else:
        result = self.driver.find_element(by, locator)
    return result # type: WebElement

麻烦天马老师能讲解详细点吗?

我打印出的返回值是这个,好像不对?

<function handle_black.<locals>.wrapper at 0x7feb68215a60>

谢谢小田的解答,这样做好像没用,问题应该还是在装饰器里

贴上完整代码吧

已贴上

这句定义里面加一个返回值的声明

def wrapper(*args, **kwargs) -> WebElement:

再试试看

加了,还是一样,这样导入没问题吧?

mport allure
from appium.webdriver.common.mobileby import MobileBy
from selenium.webdriver.remote.webelement import WebElement


def handle_black(fun):
    def wrapper(*args, **kwargs) -> WebElement:

image
这不是可以吗

抱歉了,刚刚在老师们的帮助下确认了是 Pycharm 的问题,最新版本的bug

谢谢 小田同学