一、使用需求。
本程序面向使用标准格式书写函数声明和注释的程序,将其按照一般注释提取工具正常展示页面展示到浏览器。
因为我自己在对程序进行注释提取的时候发现:注释提取工具诸如pydoc,sphinx,都会默认将程序运行一遍,然后才能得到注释提取结果。然而,我写的streamlit程序并不能直接运行(否则会报错),而是要调用命令行才能运行,因此注释提取一直失败。由此我萌发了写一个不会默认运行程序的注释提取工具的想法。
二、使用说明。
需要引入的库:streamlit(运行引入streamlit的库的程序要调用浏览器展示,因此要对该程序使用命令行调用,浏览器进行本地展示注释提取结果。之后会展示详细过程)。
需要修改的参数:程序中注释提取的对象——目标python文件的路径。
目前实现的功能:对函数的声明及下用三个单引号或者三个双引号括起来的注释提取到一起。并分别展示。
能够被识别的注释格式:(例如:)
单引号,双引号都无所谓,但是要是三个引号引起的注释且写在函数声明正下方,否则视为没有写注释。
即使没有写注释也无所谓,会在注释提取的网页结果中呈现出来。
测试时我注释采用的是pycharm的默认格式(其他格式也行的),展示时会将冒号转为换行。
三、程序代码。(方面后面将步骤)
# coding=gbk
#一个程序注释提取工具,并将其展示为网页形式
# 2024-06-13
# Chen Jing
"""
提取逻辑如下:
初始为状态0,即正常状态。
1.如果获取到以def开头的行,进入函数定义提取状态,即状态“GET_FUNC”,状态1。
此时会继续遍历,并存储这个过程中从‘(’符号开始到‘):’符号结束的全部内容,每行剔除全部前后空格。
然后将获取到的内容内含的全部'\n'符号剔除。
2.如果获取到使用三个双号或三个单引号开头的行,进入提取注释阶段,即状态“GET_COMMENT”。
此时会继续遍历,并存储这个过程中从‘三个单引号’符号开始到‘三个双引号’符号结束的全部内容,每行剔除全部前后空格。
然后将获取到的内容内含的全部'\n'符号剔除。
"""
# 需修改的参数(需进行注释提取的文件路径)!!!!!!!!!!!!!!!!!!!!!!!!!!!!
filename= r'..\三阶段-整合\2024.03.25建框架.py'
# 需导入的库,以及运行时采用:在命令行,先定位到程序所在位置,然后键入‘streamlit run + 文件名’(引号不用加)的方式运行!!!
import streamlit as st
# 页面宽度拓宽
st.set_page_config(layout='wide',page_title='注释提取结果')
# 使用streamlit的方式初始化状态
if 'extra_status' not in st.session_state:
st.session_state['extra_status']=0 #注释提取状态。0为初始状态,1为函数定义提取状态,2为注释提取状态
if 'store_func_and_comment' not in st.session_state:
st.session_state['store_func_and_comment']= []
if 'func_info' not in st.session_state:
st.session_state['func_info'] = "" #存储函数声明信息
if 'comment_info' not in st.session_state:
st.session_state['comment_info'] = "" #存储注释信息
if 'func_and_comment' not in st.session_state:
st.session_state['func_and_comment'] = [] #存储函数名和注释
if 'sign_flag' not in st.session_state:
st.session_state['sign_flag'] = -1 #表示注释所使用的引号情况,当取值为-1,表示还没有找到引号,当取值为0时,表示使用单引号,当取值为1时,表示使用双引号
@st.cache_data #缓存数据,避免重复读取
def GetInfo(filename):
with open(filename,'r',encoding='utf-8') as f:
lines = f.readlines()
for line in lines:
# 剔除前后空格
line = line.strip()
if st.session_state['extra_status']==0:
#先判断是否为函数定义
if line.startswith('def'):
#进入函数定义提取状态
st.session_state['extra_status']=1
st.session_state['func_info'] = line.replace('\n', '') #初始化st.session_state['func_info']为声明信息的第一行,剔除换行符
if line.endswith('):'):
# 汇总函数声明信息,并存储
st.session_state['func_info'] = st.session_state['func_info'].replace('\n', '') # 剔除换行符
st.session_state['func_and_comment'].append(st.session_state['func_info'])
# 下一行就进入注释提取状态,如果下一行不是注释,则进入正常状态
st.session_state['extra_status'] = 2
st.session_state['func_info'] = "" # 清空st.session_state['func_info'],方便下次复用变量
elif st.session_state['extra_status']==1:
line=line.replace('\n', '')
st.session_state['func_info'] += line
#判断是否是声明信息的最后一行
if line.endswith('):'):
#汇总函数声明信息,并存储
st.session_state['func_info'] = st.session_state['func_info'].replace('\n', '') #剔除换行符
st.session_state['func_and_comment'].append(st.session_state['func_info'])
#下一行就进入注释提取状态,如果下一行不是注释,则进入正常状态
st.session_state['extra_status']=2
st.session_state['func_info']="" #清空st.session_state['func_info'],方便下次复用变量
elif st.session_state['extra_status']==2:
#判断开头是否为三个单引号或三个双引号,要分别进行处理。而且要处理该函仅有引号的情况。
if st.session_state['sign_flag']==-1 and line.startswith("'''"):
st.session_state['sign_flag']=0
elif st.session_state['sign_flag']==-1 and line.startswith('"""'):
st.session_state['sign_flag']=1
#当不在注释行,且未进入注释提取状态时,状态为:没有给函数写注释。
elif st.session_state['sign_flag']==-1:
#没有按照标准写函数的注释,将空信息存储,并返回正常状态。
st.session_state['func_and_comment'].append("") #存储一个空字符串,表示该函数没有注释
st.session_state['store_func_and_comment'].append(st.session_state['func_and_comment'])
st.session_state['extra_status']=0
st.session_state['sign_flag']=-1
st.session_state['comment_info'] = "" # 清空st.session_state['comment_info'],方便下次复用变量
st.session_state['func_and_comment'] = [] # 清空st.session_state['func_and_comment'],方便下次复用变量
#当进入注释行时。(三个单引号)
if st.session_state['sign_flag']==0:
#如果遇到结尾为三个单引号,存储信息,并返回正常状态
st.session_state['comment_info'] += line #可能出现提取结果从中,结尾多了三个引号的情况!!!
if st.session_state['comment_info']!="'''" and line.endswith("'''"):
print(st.session_state['comment_info'])
st.session_state['func_and_comment'].append(st.session_state['comment_info'][3:-3]) # 存储一个空字符串,表示该函数没有注释
st.session_state['store_func_and_comment'].append(st.session_state['func_and_comment'])
st.session_state['extra_status'] = 0
st.session_state['sign_flag'] = -1
st.session_state['comment_info'] = "" #清空st.session_state['comment_info'],方便下次复用变量
st.session_state['func_and_comment'] = [] #清空st.session_state['func_and_comment'],方便下次复用变量
# 当进入注释行时。(三个双引号)
elif st.session_state['sign_flag']==1:
#如果遇到结尾为三个双引号,存储信息,并返回正常状态
st.session_state['comment_info'] += line
if st.session_state['comment_info']!='"""' and line.endswith('"""'):
st.session_state['func_and_comment'].append(st.session_state['comment_info'][3:-3]) # 存储一个空字符串,表示该函数没有注释
st.session_state['store_func_and_comment'].append(st.session_state['func_and_comment'])
st.session_state['extra_status'] = 0
st.session_state['sign_flag'] = -1
st.session_state['comment_info'] = "" # 清空st.session_state['comment_info'],方便下次复用变量
st.session_state['func_and_comment'] = [] # 清空st.session_state['func_and_comment'],方便下次复用变量
return st.session_state['store_func_and_comment']
#在本地网页展示注释提取信息
if 'info' not in st.session_state:
st.session_state['info'] = GetInfo(filename)
st.title('注释提取结果:')
for func,comment in st.session_state['info']:
st.markdown(f'''## {func}''')
if comment=="":
with st.expander('''**函数注释**''',expanded=True):
st.write('该函数没有注释')
else:
comment=comment.replace(':', ' \n') # 替换换行符成为markdown的换行符
with st.expander('''**函数注释**''',expanded=True):
st.markdown(f'''###### {comment}''')
#st.write(st.session_state['info'])
四、程序使用步骤。
前置参数修改:将程序中的目标程序地址filename变量,r后面的部分修改为目标程序(需要被注释提取的对象)的路径。(绝对路径和相对路径都行。)
1.将程序放到任意你想放的位置。然后打开命令行(使用pycharm的命令行,或者电脑本身的cmd的命令行都行)。
这个是pycharm的命令行,圈中的是打开位置。
在可以输入的命令行处,将指向的地址修改为该程序放置的文件夹下。可以采用下面的方法:
比如,如果原本它指向的是C盘某处,但是你的程序放在D盘,可以用下面指令:
D:
将其指向的地址先修改为D盘。
然后再用 cd +该程序所在文件夹的路径 ,指向该程序所在文件夹。如:
cd D:\python练习\pythonProject3\MidiHall\source1
2.使用指令:streamlit run + 该程序名字 + .py ,并敲击回车执行。如下:
streamlit run thisExample.py
然后它就会自动运行并自动打开浏览器展示本地网页,效果如下:
其中展示的网页地址就是本地浏览器展示的页面位置链接。它会自动调用浏览器打开的,也可以手动点开。
点开后呈现的浏览器页面效果如下:
包含了每一个函数声明和其注释信息。
3.将注释提取结果的网页保存成pdf。
我使用的是edge浏览器,即使它保存成html文件也是没办法打开的。因此选择保存成pdf。
edge浏览器,在网页上右键,会有一个“打印”选择:
点击后出现一个窗口:
选择另存为PDF。
然后确认就能将网页保存成PDF文件了。
五、局限性。
由于只花了大概2个小时写这个程序,因此,程序仅仅实现了函数声明及其注释的提取,没有办法对类进行提取,但是一般类的生命和定义的文件也不会用到streamlit这种无法直接运行的库吧。
然后就是注释提取的网页没法直接保存成html文件并能正确打开,原因可能是呈现该网页需要js文件等等。光靠html文件不行。或许有大佬可以试试将整个网页对象(html+css+js)保存起来到本地。
六、总结。
一次兴趣使然的编码经历。虽然临近期末,但是比起游戏,果然还是做这种事情更有成就感啊。