查找Python包的依赖包(语句)

匿名 (未验证) 提交于 2019-12-02 22:54:36

Window 10家庭中文版,Python 3.6.4,

于是,就有了一段200来行的程序和本文了。

功能名称:

查找Python包的依赖包(语句)

功能介绍:

找到Python包(包括子目录)中所有Python语句中的from、import语句,from语句获取import前面的部分,import语句获取整行。

使用方法:

使用包的绝对路径建立类ModuleYilai(模块依赖)的实例,然后调用实例方法yilais就可以获得Python包的依赖包呢,以列表形式返回。

程序介绍:

class ModuleYilai

查找包依赖类;

def ispackage(dirpath)

检查文件夹是否是Python包,判断是否含有__init__.py文件;

def get_all_dirs(dirpath, level=True)

获取给定dirpath目录及其子目录的绝对路径的列表,level为True时包含目录本身;用到了递归,内部使用时,level为False;

def get_all_pyfiles(dirpath)

获取目录下的所有Python文件(*.py);

def get_pyfile_yilais(pyfile)

获取Python文件中所有的from子句、import子句;

需要说明的是,在建立正则表达时是,会忽略了from子句、import子句位于文件开头的情况,故程序中对from子句、import子句分别使用了两次正则匹配;

当然,from子句、import子句的规则还是挺多了,目前程序未能完美(100%)匹配,因此,尚有改进空间,很大;

代码如下(就不添加行号了,方便大家复制):

‘‘‘ 获取一个Python包的依赖包(语句列表)  2018-06-24 1625:第一版,并不完善,短时间内也不准备完善了 ‘‘‘ import os import logging import re  zllog = logging.getLogger("zl.yilai")  class ModuleYilai:     ‘‘‘     找到模块依赖的模块:          ‘‘‘     __module_path = ‘‘     __module_name = ‘‘     __yilai_modules = set()          def __init__(self, module_path):         ‘‘‘         输入模块的安装路径:绝对路径,不能为根目路“/”         ‘‘‘         zllog.debug(ModuleYilai.__init__)                  # 检查module_path是否符合要求         if not isinstance(module_path, str):             print(moule_path (%s) is not str % type(module_path))             return                  if len(module_path) == 1:             print(the length of module_path (%s) is 1 % module_path)             return                  if not os.path.isabs(module_path):             print(module_path (%s) is not an absolute path % module_path)             return                  if not os.path.isdir(module_path):             print(module_path (%s) is not a directory % module_path)             return                  if not os.path.exists(module_path):             print(module path (%s) does not exist % module_path)             return                  if module_path.endswith(//) or module_path.endswith(\\\\):             print(too many forward slashes or back slashes in the end of the module_path (%s) % module_path)             return                  # 目录下是否有__init__.py文件         # 存在此文件,那么,这是一个package         dl = os.listdir(module_path)         try:             dl.index(__init__.py)         except:             print(module_path (%s) is not a package: there is no __init__.py % module_path)             return                  # 检查完毕后,设置内部_module_path         self.__module_path = module_path                  # 找出模块名称         temp_path = module_path         if temp_path.endswith(/) or temp_path.endswith(\\):             print(module_path processing...)             temp_path = temp_path[:len(temp_path) - 1]                  last_slash_index = temp_path.rfind(/)         if last_slash_index < 0:             last_slash_index = temp_path.rfind(\\)                  self.__module_name = temp_path[last_slash_index + 1:]                  # 寻找模块依赖,并将找打的依赖模块存放到_yilai_modules中         self._search_yilais()          # 寻找模块依赖     def _search_yilais(self):         if self.__module_path == ‘‘:             return                  # 1.找到模块下每一个目录(包括目录本身)         dirlist = get_all_dirs(self.__module_path)         zllog.debug(length of dirlist: , len(dirlist))                  # 2.找到模块下每一个模块文件(*.py),将其绝对路径存入列表中         pyfiles = []         for item in dirlist:             pyfiles.extend(get_all_pyfiles(item))         zllog.debug(length of pyfiles: , len(pyfiles))                  # 3.找到每一个模块文件的依赖模块         fileyilais = []         for item in pyfiles:             fileyilais.extend(get_pyfile_yilais(item))         zllog.debug(length of fileyilais: , len(fileyilais))                  # 4.将fileyilais转换为set并将其存入实例的_yilai_modules中         self.__yilai_modules = set(fileyilais)         zllog.debug(length of self.__yilai_modules: , len(self.__yilai_modules))          # 获取模块名称     def mod_name(self):         return self.__module_name          # 获取依赖的包的列表     def yilais(self):         return list(self.__yilai_modules)  # 判断一个文件夹是否是Python包 def ispackage(dirpath):     try:         dl = os.listdir(dirpath)         dl.index(__init__.py)         return True     except:         return False  # 找到dirpath下所有目录(包括目录本身),以列表形式返回 # 递归算法 # level为True时,添加目录本身,否则,不添加(查找子目录下的目录时不添加) def get_all_dirs(dirpath, level=True):     # 统一使用UNIX样式路径分隔符(/)     # 替换后,Windows下也可以运行     dirpath = dirpath.replace(\\, /)          dirlist = []          # 添加目录自身     if level:         dirlist.append(dirpath)          dl = os.listdir(dirpath)          # 排除其中的__pycache__和test文件夹     try:         dl.remove(__pycache__)         dl.remove(test)     except:         pass          for item in dl:         itempath = dirpath + / + item         if os.path.isdir(itempath):             # 将目录添加到返回列表中             dirlist.append(itempath)                          # 执行get_all_dirs获取其下的目录并添加到dirlist中!             dirlist.extend(get_all_dirs(itempath, level=False))          return dirlist  # 找到diapath下所有Python模块(*.py文件),以列表形式返回 # dirpath为绝对路径 def get_all_pyfiles(dirpath):     # 统一使用UNIX样式路径分隔符(/)     # 替换后,Windows下也可以运行     dirpath = dirpath.replace(\\, /)          rs = []          if not os.path.isdir(dirpath):         return          dl = os.listdir(dirpath)     for item in dl:         itempath = dirpath + / + item         # 检查是否是文件,是否要是py文件         if os.path.isfile(itempath) and item.endswith(.py):             rs.append(itempath)          return rs  # 获取一个Python模块(.py文件)导入的包 # 结果以列表形式返回 #  # 可能的形式: # 1.import sys # 2.from __future__ import absolute_import # 3.from socket import error as SocketError, timeout as SocketTimeout # 4. # from .connection import ( #     port_by_scheme, #     DummyConnection, #     HTTPConnection, HTTPSConnection, VerifiedHTTPSConnection, #     HTTPException, BaseSSLError, # ) # 5. # if six.PY2: #     # Queue is imported for side effects on MS Windows #     import Queue as _unused_module_Queue  # noqa: F401 # 6.import mod1, mod2, mod3 # 7.... def get_pyfile_yilais(pyfile):     ‘‘‘     格式很多,尚未完善!!     ‘‘‘     rs = []          if not os.path.isfile(pyfile):         print([get_pyfile_yilais] pyfile (%s) is not a file. % pyfile)         return rs          with open(pyfile, r, encoding=utf-8) as f:         content = f.read()                  #rs1 = re.findall(‘\n(from\s+.+)\s‘, content)         # from可以在文件的开头,或者一行的开头,或者注释中,需要前面两种         rs1 = re.findall(^(from\s+[\_\.0-9a-zA-Z]+)\s, content)         rs2 = re.findall(\n(from\s+[\_\.0-9a-zA-Z]+)\s, content)         #print(‘rs1 = ‘, rs1)         #print(‘rs2 = ‘, rs2)                  rs3 = re.findall(^(import\s+.+), content)         rs4 = re.findall(\n(import\s+.+), content)         #print(‘rs3 = ‘, rs3)         #print(‘rs4 = ‘, rs4)         rs = rs1 + rs2 + rs3 + rs4          return rs  if __name__ == __main__:     # 一些测试     #m1 = ModuleYilai(‘C:\\Python36\\Lib\\sqlite3‘)     #m2 = ModuleYilai(‘C:\\Python36\\Lib\\sqlite3\\‘)     #m3 = ModuleYilai(‘C:\\Python36\\Lib‘)     #m4 = ModuleYilai(‘C:\\Python36\\Lib\\‘)     #m5 = ModuleYilai(‘C:/Python36/Lib/sqlite3‘)     #m6 = ModuleYilai(‘C:/Python36/Lib/sqlite3/‘)     #m7 = ModuleYilai(‘C:/Python36/Lib/sqlite3//‘)     #m8 = ModuleYilai(‘C‘)     #m9 = ModuleYilai(‘/‘)     #m10 = ModuleYilai(‘\\‘)          # 测试get_pyfile_yilais     #get_pyfile_yilais(‘C:\\Python36\\Lib\\sqlite3\\dbapi2.py‘)     #print()     #get_pyfile_yilais(‘C:\\Python36\\Lib\\site-packages\\urllib3\\connectionpool.py‘)          # 测试get_all_dirs     #retlist = get_all_dirs(‘C:\\Python36\\Lib\\site-packages\\urllib3‘)     #for item in retlist:     #    print(item)          # Test urllib3 module     m1 = ModuleYilai(C:\\Python36\\Lib\\site-packages\\urllib3)     print(module name: , m1.mod_name())     print(length: , len(m1.yilais()))     print(m1.yilais())     print()          # Test of requests module     m2 = ModuleYilai(C:\\Python36\\Lib\\site-packages\\requests)     print(module name: , m2.mod_name())     print(length: , len(m2.yilais()))     print(m2.yilais())     print()          # Test of D:\\Users\\log     m3 = ModuleYilai(D:\\Users\\log)     print(module name: , m3.mod_name())     print(length: , len(m3.yilais()))     print(m3.yilais())
mdeps.py

测试结果:

改进方式:

本想在程序中使用logging记录日志,发现它和print混用时在Console输出的消息是混乱的;

使用setLevel函数设置了日志优先级,可是,没能输出想要的调试信息,还需继续研究;

程序使用自己的杂乱的方法进行了测试,更高质量的测试工具unittest、pytest没能用到;

对于from子句、import的抓取,目前只使用了两个简单的规则,其它的尚需进一步完善,但孤可能不会继续了;

对模块以一个点、两个点开头的方式没有做处理;

代码中的def ispackage(dirpath)函数没有用到,本想检测目录是否是Python包的,可自行添加;

本次收获:

写了一个Python类了,还用到了两个下划线开头的私有变量;

用了一些list、set、str的方法呢;

练习了正则表达式的使用,不过,还是不很熟练,需提高;

轻车熟路地使用了with语句;

今天添加了几个小时的认真时间;

原文:https://www.cnblogs.com/luo630/p/9221141.html

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!