320 lines
13 KiB
Python
320 lines
13 KiB
Python
#!/usr/bin/python
|
||
# -*- coding: UTF-8
|
||
|
||
from __future__ import print_function
|
||
|
||
# sphinx事件回调函数
|
||
#如果copy到conf.py文件中,只copy下面的代码即可。
|
||
|
||
import os
|
||
import os.path as osp
|
||
import sys
|
||
import importlib
|
||
import traceback
|
||
import datetime as dt
|
||
import locale
|
||
|
||
#如果文档编译失败,请检查下面默认的路径是否正确,请根据实际路径进行修改
|
||
|
||
if osp.exists(osp.join(osp.dirname(__file__), './parsejson.py')):
|
||
sys.path.append(osp.join(osp.dirname(__file__), '.'))
|
||
else:
|
||
sys.path.append(osp.join(osp.dirname(__file__), '../'))
|
||
|
||
cusdirect = None
|
||
extpathisexist = False # ./_ext/customdirective.py是否存在的标志,用于设置指令扩展
|
||
|
||
extpath = osp.join(osp.dirname(__file__), './_ext')
|
||
if osp.exists(extpath):
|
||
# 自定义指令所在路径,加到搜索目录,否则无法使用自定义指令
|
||
sys.path.append(extpath)
|
||
if osp.exists(osp.join(osp.dirname(__file__),r'./_ext/customdirective.py')):
|
||
extpathisexist = True
|
||
cusdirect = importlib.import_module('customdirective')
|
||
else:
|
||
extpath = osp.join(osp.dirname(__file__), '../_ext')
|
||
sys.path.append(extpath)
|
||
if osp.exists(osp.join(osp.dirname(__file__),r'../_ext/customdirective.py')):
|
||
extpathisexist = True
|
||
cusdirect = importlib.import_module('customdirective')
|
||
|
||
import sphinx
|
||
import sphinx.errors as sperror
|
||
import parsejson
|
||
|
||
try:
|
||
import breathe # 仅为了判断版本号,根据breathe版本号添加不同配置
|
||
breathe_version = breathe.__version__[:3]
|
||
except Exception as e:
|
||
breathe_version = ""
|
||
|
||
warnfile = '' # 告警文件,不包含路径
|
||
warnfilepath = '' # 保存告警日志文件,包含完整的路径名
|
||
|
||
months=['January','February','March','April','May','June','July',
|
||
'August','September','October','Novmber','December']
|
||
|
||
gmaketitle = r'''
|
||
\pagenumbering{Roman}
|
||
\begin{titlepage}
|
||
\centering
|
||
\vspace*{40mm}
|
||
\textbf{\Huge {%(titlename)s}}
|
||
|
||
\vspace{10mm}
|
||
\textbf{\Large{%(release)s}}
|
||
|
||
\vfill
|
||
\textbf{\large{%(today)s}}
|
||
\end{titlepage}
|
||
'''
|
||
|
||
def __ModifyMakeTitle(elementskeys,config):
|
||
'''
|
||
如果有maketitle变量则修改maketitle的内容
|
||
:param config: 配置变量
|
||
:return:
|
||
'''
|
||
#得到封面名称
|
||
titlename = config.latex_documents[0][2]
|
||
|
||
if 'releasename' in elementskeys:
|
||
releasename = config.latex_elements['releasename']
|
||
else:
|
||
releasename = ''
|
||
|
||
if hasattr(config,'version'):
|
||
version = config.version
|
||
else:
|
||
version = ''
|
||
release = releasename +' ' + version
|
||
if hasattr(config,'today') and len(config.today) > 0:
|
||
today = config.today
|
||
else:
|
||
todayft = dt.date.today()
|
||
if hasattr(config,'language') and config.language=='zh_CN':
|
||
today = todayft.strftime('%Y 年 %m 月 %d 日')
|
||
else:
|
||
#除中文外,一律用英文时间格式
|
||
#得到英文月的全称。
|
||
month = months[int(todayft.strftime('%m'))-1]
|
||
today = month + todayft.strftime(" %d, %Y")
|
||
|
||
if len(config.latex_elements['maketitle'].strip())==0:
|
||
maketitle = gmaketitle
|
||
else:
|
||
maketitle = config.latex_elements['maketitle']
|
||
#必须转一下,否则赋值不成功
|
||
config.latex_elements['maketitle'] = maketitle % {
|
||
'titlename': titlename,
|
||
'release': release,
|
||
'today': today
|
||
}
|
||
|
||
def config_inited_handler(app, config):
|
||
# 检查年份是否正确,并修改。需配合最新parsejson.py文件使用,否则不支持该接口。
|
||
# 通用配置的修改放在该位置,否则对配置的修改不生效
|
||
try:
|
||
# 该函数的第二个参数可以传入版权声明文件相对于conf.py的相对路径,自动修改版权声明的年份
|
||
# 比如:parsejson.CheckCurrentYearAndModify(app,'../source/copyright/cnperf_conpyright.rst')
|
||
# 默认路径为“./copyright/copyright.rst”和“./copyright/copyright_zh.rst”或者“./copyright/copyright_en.rst”
|
||
# 以上三个路径无需传入
|
||
parsejson.CheckCurrentYearAndModify(app)
|
||
except Exception as e:
|
||
print('------------------')
|
||
print(e)
|
||
# traceback.print_stack()
|
||
traceback.print_exc()
|
||
print('------------------')
|
||
|
||
try:
|
||
# print(sphinx.__version__)
|
||
# app.require_sphinx('3.5')
|
||
keys = config.latex_elements.keys()
|
||
if 'sphinxsetup' not in keys:
|
||
config.latex_elements['sphinxsetup'] = ''
|
||
if "3.5" <= sphinx.__display_version__[:3]:
|
||
config.latex_elements['sphinxsetup'] = 'verbatimforcewraps=true,verbatimmaxunderfull=2,' + \
|
||
config.latex_elements['sphinxsetup']
|
||
if "5.3" <= sphinx.__display_version__[:3]:
|
||
config.latex_table_style = ['standard', 'nocolorrows']
|
||
config.latex_elements['sphinxsetup'] += 'pre_border-radius=0pt,'
|
||
if len(breathe_version) > 0 and "4.3" <= breathe_version:
|
||
config.latex_elements['preamble'] += '''
|
||
\\renewenvironment{description}
|
||
{\\list{}{\\labelwidth=0pt
|
||
\\let\\makelabel\\descriptionlabel}}
|
||
{\\endlist}
|
||
'''
|
||
#如果有自定义maketitle,则修改maketitle的内容
|
||
if 'maketitle' in keys:
|
||
__ModifyMakeTitle(keys,config)
|
||
|
||
# sphinxsetup = config.latex_elements['sphinxsetup']
|
||
# print('sphinxversion:%s;sphinxsetup:%s' % (sphinx.__version__,sphinxsetup))
|
||
except Exception as e:
|
||
# print('sphinxversion:%s %s' % (sphinx.__version__,sphinx.__display_version__[:3]))
|
||
pass
|
||
|
||
|
||
def build_finished_handler(app, exception):
|
||
if exception != None:
|
||
# print(exception)
|
||
return
|
||
|
||
# 判断告警文件是否存在,只有无告警或者告警全是忽略告警才允许继续后续的编译
|
||
if warnfilepath != '' and osp.exists(warnfilepath):
|
||
# 判断告警文件中是否全是忽略告警
|
||
iswarn = parsejson.warn_main(warnfilepath, app)
|
||
if iswarn:
|
||
# 如果为True则说明有不可忽略的告警,报sphinxerror异常,停止继续编译
|
||
raise sperror.SphinxError('There are alarms, please check the file of %s for details' % warnfile)
|
||
return
|
||
|
||
try:
|
||
if app.builder.name == "latex":
|
||
selffnlst = app.config.latex_documents
|
||
parsejson.Modifylatex_main(app.outdir, selffnlst, app)
|
||
except Exception as e:
|
||
print('------------------')
|
||
print(e)
|
||
traceback.print_exc()
|
||
|
||
# 检查html标题一致性。该接口最好放在该位置,主索引文件的标题已经解析出来。
|
||
# if app.builder.name == "html":
|
||
# result = parsejson.CheckProjectNameIsConsistent(app)
|
||
# if result != "":
|
||
# raise sperror.SphinxError(result) #带raise的异常如果用except捕捉,则在终端打印告警将不能显示为红色字体。
|
||
|
||
|
||
def build_inited_handler(app):
|
||
global warnfile
|
||
global warnfilepath
|
||
|
||
print(sys.argv)
|
||
args = sys.argv[1:] # 0为sphinx-build,需忽略掉
|
||
if '-w' in args:
|
||
pos = args.index('-w') # 找到-w所在的索引位置
|
||
warnfile = args[pos + 1] # 得到告警保存的文件名
|
||
# print('warnfile=' + warnfile)
|
||
|
||
# 根据工作路径,得到文件名的绝对路径
|
||
# 当前在build阶段,因此工作路径为Makefile所在的目录,-w后面的文件保存在基于Makefile的相对路径下
|
||
filepath = osp.join(os.getcwd(), warnfile)
|
||
warnfilepath = osp.abspath(filepath)
|
||
# print('warnfilepath = ' + warnfilepath)
|
||
|
||
# try:
|
||
# # 检查是否有rst_prolog或者rst_epilog替换内容,有的话去掉前后空格
|
||
# # 仅适用于pdf文件和中文文档,英文文档和html不启作用
|
||
# checkDocobj = parsejson.clsCheckDocCopyrightYear(app, "")
|
||
# if app.builder.name == 'latex' or app.builder.name == 'latexpdf':
|
||
# checkDocobj.CheckReplaceContent()
|
||
# except Exception as e:
|
||
# print('------------------')
|
||
# print(e)
|
||
# traceback.print_exc()
|
||
# print('------------------')
|
||
|
||
# try:
|
||
# # 检查html配置是否符合规范。必须放在这个位置,否则app没有builder对象。
|
||
# if app.builder.name == "html":
|
||
# error = parsejson.CheckHtmlConfigIsCorrect(app)
|
||
# if error != "":
|
||
# raise sperror.ConfigError(error)
|
||
# except Exception as e:
|
||
# print('------------------')
|
||
# print(e)
|
||
# traceback.print_stack()
|
||
# traceback.print_exc()
|
||
# print('------------------')
|
||
|
||
|
||
def source_read_handler(app, docname, source):
|
||
if cusdirect is not None:
|
||
cnonlyobj = cusdirect.CNOnlyPro(app, docname, source[0])
|
||
source[0] = cnonlyobj.parsecnonlycontent(source[0])
|
||
|
||
# try:
|
||
# 自动添加更新历史代码,默认添加“无内容更新”
|
||
# source[0] = parsejson.CheckUpdateHistory(app, docname, source[0])
|
||
# except Exception as e:
|
||
# print('------------------')
|
||
# print(e)
|
||
# traceback.print_exc()
|
||
# print('------------------')
|
||
|
||
return source
|
||
|
||
|
||
def doctree_resolved_handle(app, doctree, docname):
|
||
"""
|
||
删除不需要文件的内容,避免编译html的时候还进行编译,导致内容泄漏
|
||
"""
|
||
if not hasattr(app.config, 'enable_exclude_html') or not app.config.enable_exclude_html:
|
||
# 默认按sphinx方式编译所有rst文件到html
|
||
return
|
||
|
||
indexname = app.config.master_doc
|
||
if docname == indexname:
|
||
return
|
||
indexdocnames = app.env.toctree_includes[indexname]
|
||
isexist = False
|
||
# 判断文档是否在索引文件里
|
||
if docname not in indexdocnames:
|
||
newtoctree = app.env.toctree_includes.copy()
|
||
del newtoctree[indexname] # 删除主索引文件,因为无需比较
|
||
for key in newtoctree.keys():
|
||
values = newtoctree[key]
|
||
if docname in values:
|
||
isexist = True
|
||
# 判断该key是否在主索引文件里面
|
||
if key not in indexdocnames:
|
||
# 如果不在整个的索引文件里面,删除该文件内容
|
||
# doctree.attributes['source']=''
|
||
while len(doctree.children) > 0:
|
||
doctree.remove(doctree.children[0])
|
||
# 删除标题,否则html页面还有标题,还需要维护标题
|
||
for node in app.env.titles[docname].children:
|
||
app.env.titles[docname].remove(node)
|
||
if not isexist:
|
||
# 文档不在toctree字典里,直接删除内容
|
||
while len(doctree.children) > 0:
|
||
doctree.remove(doctree.children[0])
|
||
# 删除标题,否则html页面还有标题,还需要维护标题
|
||
for node in app.env.titles[docname].children:
|
||
app.env.titles[docname].remove(node)
|
||
|
||
|
||
def setup(app):
|
||
"""
|
||
该函数中的路径都是相对于工程builder环境的路径而不是相对于conf.py所在的路径,
|
||
该函数外面的相对路径是相对于conf.py的路径没有问题。
|
||
比如./_ext相对路径是相对于工程的路径,因此如果使用abspath得到绝对路径会得到~/m0_docs/_ext/,
|
||
而不是~ / mo_docs / source / _ext。
|
||
因此该路径下对路径的判断最好转化为绝对路径判断,否则可能会判断失败。
|
||
:param app:
|
||
:return:
|
||
"""
|
||
app.connect('config-inited', config_inited_handler)
|
||
app.connect('build-finished', build_finished_handler)
|
||
app.connect('builder-inited', build_inited_handler)
|
||
app.connect('source-read', source_read_handler)
|
||
app.connect('doctree-resolved', doctree_resolved_handle)
|
||
|
||
app.add_config_value('chapterbkpaper_image', '', 'env', [str])
|
||
# 是否排除未添加到index的html文件。在编译html时,sphinx默认会将所有rst文件都生成html,因此可能会造成部分内容的泄露。
|
||
# 为了删除这部分内容,设置该变量。如果置为True的话,将删除不在index.rst里面的html的内容。
|
||
# 默认为false,不做处理,以增加编译效率。
|
||
# 建议还是将不参与编译的rst文件,放到exclude_patterns变量内,放到该变量后,rst不参与编译,能提高编译效率。
|
||
app.add_config_value('enable_exclude_html', False, 'env', [bool])
|
||
#配置版权声明文件完整路径,实现版权声明文件年份的自动修改。
|
||
#parsejson.CheckCurrentYearAndModify如果该函数中传入了版权声明的全路径,则以该函数中传入的为准
|
||
#在该处又增加了一个配置值,是为了配置和代码解偶,为了兼容性,保留了原来的配置方式
|
||
app.add_config_value('copyfile_path', '', 'env', [str])
|
||
#在conf.py中配置的conf.json路径,为了实现conf.json配置的灵活处理
|
||
app.add_config_value('confjson_path', '', 'env', [str])
|
||
|
||
if extpathisexist:
|
||
app.setup_extension('customdirective')
|