测试任意变量/对象/模块是否存在之方法

2024-09-30 21:10:09 浏览数 (1)

代码语言:javascript复制
def tryok(any_var_str = '^Tryok', output_tips = -1, version = 'ver-1.21'):
    # === 【★★★】=== 该方法属于-任意变量/对象/模块-询值-正逻辑-测试函数-在当前或外部文件均可调用---存在返回-真-否则返回-假
    # === 【正逻辑】测试-对任何变量、对象、模块检查其是否【存在】是-则为真-否则为假,相应的名称串需要用单或双引号括起来
    # === 若在变量名首位增加 ^ 符号,则表明是按【负逻辑】测试返回结果,即:不存在为真,存在为假,该功能等同于方法:tryerr()
    # === any_var_str:欲测试的任意全局变量-对象-模块名,output_tips:是否输出加载过程的提示信息,默认=-1,不提示,否则输出提示
    # === 对于由点号组成的 any_var_str 属于-对象.属性名参数形式:其中的对象必须是全局型,属性名串应符合合规范
    # === 其中的 version :参数-无意义,因为需要复制使用,所以要知道谁是最新的-以此方式作标记
    any_var_str = any_var_str.replace(' ', '')
    ON_OFF_PRINT = '0 0' if output_tips >= 0 else '00'
    back_logic = True if any_var_str[0] == '^' else False
    any_var_str = any_var_str[1:] if back_logic else any_var_str
    sure = False
    try:
        if '.' in any_var_str:
            one, two = any_var_str.split('.')
            isok = hasattr(globals()[one], two)
            sure = True if isok or 'NoneType' not in repr(isok) else False
            if ON_OFF_PRINT == '0 0': print(here(0), F"① 查询-对象【{any_var_str}】属性-{'【存在】' if sure==True else '【不存在】'}")
        else:
            try:
                var_val = sys._getframe(1).f_locals[any_var_str]
                the = '模块' if '<module' in str(var_val)  else '变量'
                sure = True
                if ON_OFF_PRINT == '0 0': print(here(0), F'② 查询-{the}【{any_var_str}】=值={var_val}')
            except:
                if ON_OFF_PRINT == '0 0': print(here(0), F'③ 查询-变量【{any_var_str}】-【不存在】')
    except:pass
    if back_logic:
        sure = False if sure else True
        if ON_OFF_PRINT == '0 0' : print(here(0), F"{any_var_str}-【负逻辑】测试【结论】-{sure}")
    else:
        if ON_OFF_PRINT == '0 0' : print(here(0), F"{any_var_str}-【正逻辑】测试【结论】-{sure}")
    return sure

def here(s=['', 0, 1 , -1, 2, 3, 5, 35, 53][0], sayit = 1) :
    # === 功能:获取并返回-调用本方法之当前行的行号以及其所在当前文件名-含路径、所在函数体之函数名称串与函数名对象引用
    # === 参数sayit:是否语音播报行号。默认播报=1,=0或者非1值则不语音播报
    # === 使用说明:当 s 缺省为空串时-返回-调用本方法here()当前行的行号数值
    # === 使用说明:当 s =int= 0 时-返回-调用本方法here(0)当前行的行号标记文本,形如:F"当前位于【{lineno}】行"
    # === 使用说明:当 s =int= 1 时-返回-本方法here(1)所在[路径各段,,,当前文件名,路径\]之列表项,here(1)[-1]=路径path
    # === 使用说明:当 s =int=-1 时-返回-【顶层】调用本方法here(5),here(3)之组合-即列表[函数对象引用, 函数名称串]
    # === 使用说明:当 s =int= 2 时-返回-【最近】一层调用本方法here(2)之[堆栈层级数, 行代码串]
    # === 使用说明:当 s =int= 3 时-返回-【最近】一层调用本方法here(3)之函数名称串
    # === 使用说明:当 s =int= 5 时-返回-【最近】一层调用本方法here(5)之函数名称的对象引用
    # === 使用说明:当 s =int= 35/53 时-返回-【最近】一层调用本方法here(5),here(3)之组合-即列表[函数对象引用, 堆栈层级数, 函数名称串]
    # === 使用说明:当 s =str= 字符串 时-返回-本方法here('xxx')当前执行位置的装饰标记性显示格式
    # =================================================================
    Q = str(s)
    ql = len(Q.strip())
    lineno = sys._getframe(1).f_lineno
    if ql == 0 : return lineno
    is_int = 'int' in str(type(s))
    s_int = (True if Q in '-102535' else False) if is_int else False
    if s_int and s == 0: return F"当前位于【{lineno}】行"
    # =================================================================
    if s_int:
        rowmsgs = frames()
        rowmsg = rowmsgs[-1]
        lineno = rowmsg.uprow
        filename = rowmsg.upfile
        # =========================path =  os.path.split(xpath)[0]  filename = os.path.split(xpath)[1]
        if s == 1: return filename.split(chr(92))   [filename[: filename.rfind(chr(92), 1)]   chr(92)]
        if s==-1 : return [rowmsg.topcall, rowmsg.topname]
        if s==2 : return [rowmsg.upidx,rowmsg.upcode]
        if s==3 : return rowmsg.upname
        if s==5 : return rowmsg.upcall
        if s==35 or s==53 : return [rowmsg.upcall, rowmsg.upidx, rowmsg.upname]
    elif is_int:return lineno
    # =======================================================================
    # === 以下是处理标记-本方法here('xxx')当前执行位置的装饰标记性显示格式
    if sayit == 1 : Say(F"我在{lineno}行")
    # =======================================================================
    t = 'F"'   "{'   ' * SP}{'☀' * W32} ● TS -当前代码-运行至此:【 "   str(lineno)   " 】行 ● {'☀' * W32}{' ' * SP}"   '"'
    m , g = 38 , 8
    n , k , TS = len(Q) , m   5 ,'本页面文件'
    if n > 0:
        TS = Q if n < 21 else Q[:21]
        k = m   len(TS)
    px = 120 - 24 - k - g
    px = 12 if px < 12 else px
    pk = 4 if px < 12 else 8
    W32 = str(px // 2)
    SP = str(pk // 2)
    t = t.replace('SP', SP).replace('W32', W32).replace('TS', TS)
    return eval(t)
    
def frames(nList_mTuple = -2):
    # === 功能:查询并获取当前各层级堆栈信息列表。实际返回-已扣除了-frames()-本级调用,即总层级数应减 1
    # === 参数nList_mTuple:是一个行列代号组成的1~2位正整数数字码,n 取自列表中的序号码-nList,m 取自其中各层帧结构-元组中的序号码-mTuple
    # === 由此可直接返回所需要的元素值(若值=-1即返回【对象引用】,<-1的负值无效),而非返回整个列表和对象引用。比如参数值=23,即返回:第 2 层堆栈帧中-调用函数所在行之代码串。
    # === 【特别说明】参数nList_mTuple若为字符串-则是作为检索某个有特征关键字的帧序号耐用-此时函数返回对象引用。检索结果赋于self.upidx属性
    # === 返回列表结构-列表-(nList的代号 n=):[0-当前调用堆栈总层级数, (1-层-最近的-调用堆栈信息), (2-层-上一层-调用堆栈信息), (3-层...),,, (-2 = n-顶层-调用堆栈信息), -1 = self-对象属性引用]
    # === 各层信息结构-元组-(mTuple-代号 m=):(0-本层堆栈级数, 1-调用函数名称串, 2-调用函数所在行号, 3-调用函数所在行之代码串, -2=4-调用函数引用对象, -1=5=调用函数所在之文件名)
    # === 返回【列表 对象引用】双结果:从列表-到-对象取用:self=返回列表项[-1]=frames()[-1]
    # --- self.maxdepth:最大堆栈层级数-其中包含了本级-即 0 层=frames(),属于页面层级
    # --- self.toplevel:这个是从1级调用开始的实际堆栈层级数-排除了:frames() 这一级-0层
    # --- self.upidx        # 上一层-外部文件中-最近一次调用之堆栈层级帧数
    # --- self.upname       # 上一层-外部文件中-最近一次调用函数之名称串
    # --- self.uprow        # 上一层-外部文件中-最近一次调用函数所在之行号
    # --- self.upcode       # 上一层-外部文件中-最近一次调用函数所在行之行代码串
    # --- self.upcall       # 上一层-外部文件中-最近一次调用函数之对象引用
    # --- self.upeqvar      # 上一层-外部文件中-最近一次调用函数所在行代码中等号左侧的赋值变量名
    # --- self.LEFTVAR      # 同上---只是为了使用时的一个特征标记属性
    # --- self.upfile       # 上一层-外部文件中-最近一次调用函数所在之全路径文件名
    # --- self.topidx       # 在外部文件中-顶层调用之堆栈层级帧数
    # --- self.topname      # 在外部文件中-顶层调用之函数的名称串
    # --- self.toprow       # 在外部文件中-顶层调用之函数所在之行号
    # --- self.topcode      # 在外部文件中-顶层调用之函数所在行之行代码串
    # --- self.topcall      # 在外部文件中-顶层调用之函数的对象引用
    # --- self.topeqvar     # 在外部文件中-顶层调用函数所在行代码中等号左侧的赋值变量名
    # --- self.topfile      # 在外部文件中-顶层调用之函数所在之全路径文件名
    # ●∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞
    call_bydef_ref, get_1_locals, get_2_locals, get_1_frame, Self_Name = None, None, None, None, None
    call_bydef_row, call_bydef_name, callby_code, callby_file = -1, '', '', ''
    frames_depth, frames_detail = 0, []
    InOutCall = InThisFileCall()    # 检测-frames()函数-是在本文件中调用-True,还是在外部文件调用-False
    # ●∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞
    nLIST_mTUPLE = nList_mTuple if what(nList_mTuple, 'int') else -1  # 返回对象引用
    findkey = nList_mTuple if what(nList_mTuple, 'str') else None
    idxFound = None
    # ●∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞
    while True:
        if frames_depth > 0 and call_bydef_name != '<module>':
            frames_detail.append((frames_depth, call_bydef_name, call_bydef_row, callby_code, call_bydef_ref,callby_file))
        get_f_code = sys._getframe(frames_depth).f_code
        call_bydef_name = get_f_code.co_name
        if frames_depth == 0: Self_Name = call_bydef_name   "("
        try:
            get_1_frame = sys._getframe(frames_depth   1)
            get_1_locals = get_1_frame.f_back.f_locals
            get_2_locals = sys._getframe(frames_depth 2).f_back.f_locals
        except:get_2_locals = get_1_locals
        call_bydef_row = call_bydef_row if call_bydef_name == '<module>' else get_1_frame.f_lineno
        callby_file = get_1_frame.f_code.co_filename
        callby_code = RAM_Cache.getline(callby_file, call_bydef_row).strip()
        pos = callby_code.rfind('#', 1)
        callby_code = callby_code[:pos].strip() if pos > 0 else callby_code
        if findkey != None and idxFound is None:
            idxFound = frames_depth   1 if (findkey   '(') in callby_code and Self_Name not in callby_code else idxFound

        fx_at_locals = F"<function {call_bydef_name} at"
        try:
            if frames_depth == 0:
                call_bydef_ref = globals().get(call_bydef_name)
            elif fx_at_locals in repr(get_1_locals):
                call_bydef_ref = get_1_locals[call_bydef_name]
            elif call_bydef_name == '<module>' or fx_at_locals in repr(get_2_locals):
                call_bydef_ref = get_2_locals[call_bydef_name]
        except:pass
        if call_bydef_name == '<module>':break
        frames_depth  = 1
    # ●∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞
    self = globals().get(frames_detail[0][1])  # 当前函数体内获取本函数隐式对象的引用
    frames_detail = [frames_depth]   frames_detail   [self]
    self.toplevel = frames_depth
    self.maxdepth = self.toplevel   1
    # ●∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞
    upcall_depth = idxFound if idxFound != None else 2 if InOutCall and frames_depth >= 2 else frames_depth
    self.upidx = frames_detail[upcall_depth][0]
    self.upname = frames_detail[upcall_depth][1]
    self.uprow = frames_detail[upcall_depth][2]
    self.upcall = frames_detail[upcall_depth][-2]
    self.upfile = frames_detail[upcall_depth][-1]
    # ==================================================================
    upco = frames_detail[upcall_depth][3]
    self.upcode = upco
    eqh, zkh = upco.find('='), upco.find('(')
    self.upeqvar = None if eqh < 0 or eqh > zkh else upco.split('=')[0].strip()
    self.LEFTVAR = self.upeqvar
    # ●∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞
    self.topidx = frames_detail[-2][0]
    self.topname = frames_detail[-2][1]
    self.toprow = frames_detail[-2][2]
    self.topcall = frames_detail[-2][-2]
    self.topfile = frames_detail[-2][-1]
    # ==================================================================
    upco = frames_detail[-2][3]
    self.topcode = upco
    eqh, zkh = upco.find('='), upco.find('(')
    self.topeqvar = None if eqh < 0 or eqh > zkh else upco.split('=')[0].strip()
    RAM_Cache.clearcache()
    # ●∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞
    try:
        eleIdx = nLIST_mTUPLE
    except:eleIdx = -1
    if eleIdx == -1:
        return self
    elif eleIdx < -1:
        return frames_detail   # 返回列表-到对象引用取用:self=frames_detail[-1]
    else:
        eleIdx = int(eleIdx)
        idxStr = str(eleIdx).strip()
        idxStr = idxStr[0] if eleIdx < 10 else idxStr[:2]    # 最多取用 2 位
        # 即最大处理 10 级调用帧结构堆栈-太多也无意义
        if eleIdx < 10 and eleIdx < len(frames_detail):return frames_detail[eleIdx]
        n, m = int(idxStr[0]), int(idxStr[1])
        m = 0 if m > len(frames_detail[1]) else m
        return frames_detail[n][m]
        
def tryerr(any_var_str = 'TryError', output_tips = -1, version = 'ver-1.21'):
    # === 【★★★】=== 该方法属于-任意变量/对象/模块-询值-负逻辑-测试函数-在当前或外部文件均可调用---存在返回-假-否则返回-真
    # === 【负逻辑】测试-对任何变量、对象、模块检查其是否【存在】是-则为假-否则为真,相应的名称串需要用单或双引号括起来
    # === 该功能等同于方法:tryok(any_var_str = '^Tryok') 即首位加 ^ 符号之逻辑
    # === any_var_str:欲测试的任意全局变量-对象-模块名,output_tips:是否输出加载过程的提示信息,默认=-1,不提示,否则输出提示
    # === 对于由点号组成的 any_var_str 属于-对象.属性名参数形式:其中的对象必须是全局型,属性名串应符合合规范
    # === 其中的 version :参数-无意义,因为需要复制使用,所以要知道谁是最新的-以此方式作标记
    any_var_str = any_var_str.replace(' ', '')
    ON_OFF_PRINT = '0 0' if output_tips >= 0 else '00'
    fail = True
    try:
        if '.' in any_var_str:
            one,two = any_var_str.split('.')
            isok = hasattr(globals()[one], two)
            fail = False if isok or 'NoneType' not in repr(isok) else True
            if ON_OFF_PRINT == '0 0': print(here(0), F"① 查询-对象【{any_var_str}】属性-{'【存在】' if fail==False else '【不存在】'}")
        else:
            try:
                var_val = sys._getframe(1).f_locals[any_var_str]
                the = '模块' if '<module' in str(var_val) else '变量'
                fail = False
                if ON_OFF_PRINT == '0 0': print(here(0), F'② 查询-{the}【{any_var_str}】=值={var_val}')
            except:
                if ON_OFF_PRINT == '0 0': print(here(0), F'③ 查询-变量【{any_var_str}】-【不存在】')
    except:pass
    if ON_OFF_PRINT == '0 0': print(here(0), F"{any_var_str}-【负逻辑】测试【结论】-{fail}")
    return fail
    
def tryload(load_module_name = None, output_tips = -1, version = 'ver-1.19'):
    # === 【★★★】=== 该方法属于-模块-动态加载方法-在当前或外部文件均可调用---多次调用只会首次加载-后续调用只是引用而已
    # === 测试符合:import XXX 这一格式的模块加载表达式的模块-是否-未加载-是-则为真,模块名需要用单或双引号括起来
    # === load_module_name:欲加载的模块名,output_tips:是否输出加载过程的提示信息,默认=-1,不提示,否则输出提示
    # === 其中的 version :参数-无意义,只是为了标记其最新版本而已
    # === 若相关模块未加载-则-加载它-并返回该模块对象,它有三种应用格式:
    # [格式 1] XXX = tryload(),即缺省参数时,其等式左侧的 XXX 必须是合法的且已安装有效的模块名称字符串(以下有关 XXX 的定义相同)
    # [格式 2] XXX = tryload('XXX'),即参数值与其等式左侧的 XXX 同名---同名加载,格式1为缺省加载,格式3为别名加载
    # [格式 3] module_alias_name = tryload('XXX'),即参数值与其等式左侧的 XXX 不同,则module_alias_name即为 XXX 模块的别名
    # === 本方法-方式 1-调用方式示例:win32 = tryload('win32gui'),方式2:win32gui = tryload()
    calls = FRAMES('tryload')
    call_name = calls.LEFTVAR  # 获取直接调用tryload()方法之代码行赋值等号左侧的变量名即模块名称或别名

    ON_OFF_PRINT = '0 0' if output_tips >= 0 else '00'
    if load_module_name == None or len(load_module_name.strip()) == 0:
        load_module_name = call_name
    load_module_name, load_module = load_module_name.replace(' ', ''), None
    if call_name == load_module_name:
        alias, the = load_module_name, '同名'
    else:
        alias, the = call_name, '别名'
    try:
        load_module = globals()[alias]       # load_module = eval(load_module_name)
        if ON_OFF_PRINT == '0 0': print(here(0),F'[{the}]-该模块-已存在')
    except:
        try:
            code = F"import {load_module_name} as {alias}"
            exec(code, globals())
            load_module = globals()[alias]   # load_module = eval(load_module_name)
            if ON_OFF_PRINT == '0 0': print(here(0), F'【模块】{load_module_name}--[{the}]--加载')
        except:Say('该模块加载失败')
    the = '【失败】' if load_module is None else '【成功】'
    if ON_OFF_PRINT == '0 0': print(here(0), F"【模块】{load_module_name}-【加载】-{the}")
    return load_module

0 人点赞