Files
2026-02-04 17:39:32 +08:00

320 lines
13 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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')