如何解决阻止 Sphinx 执行缓存的类方法属性
from abc import ABC,ABCMeta,abstractmethod
from functools import cache
from pathlib import Path
from warnings import warn
class DatasetMetaClass(ABCMeta):
r"""Meta Class for Datasets"""
@property
@cache
def Metaclass_property(cls):
r"""Compute an expensive property (for example: dataset statistics)."""
warn("Caching Metaclass property...")
return "result"
# def __dir__(cls):
# return list(super().__dir__()) + ['Metaclass_property']
class DatasetBaseClass(Metaclass=DatasetMetaClass):
r"""Base Class for datasets that all datasets must subclass"""
@classmethod
@property
@cache
def baseclass_property(cls):
r"""Compute an expensive property (for example: dataset statistics)."""
warn("Caching baseclass property...")
return "result"
class DatasetExampleClass(DatasetBaseClass,Metaclass=DatasetMetaClass):
r"""Some Dataset Example."""
现在,问题是在 make html
期间,sphinx 实际上执行了 baseclass_property
,这是一个非常昂贵的操作。 (其中包括:检查本地是否存在数据集,如果不存在,则下载它,对其进行预处理,计算数据集统计信息,修剪草坪并取出垃圾。)
我注意到,如果我将其设为 MetaClass 属性,则不会发生这种情况,因为 meta-class property does not appear in the classes __dir__
call 可能是也可能不是错误。通过取消注释这两行将其手动添加到 __dir__
会导致 sphinx 也处理元类属性。
问题:
- 这是 Sphinx 中的错误吗?鉴于
@properties
通常处理得很好,它似乎无意中为@classmethod@property
中断。 - 目前避免此问题的最佳选择是什么?我能以某种方式告诉 Sphinx 不要解析这个函数吗?我希望有可能通过类似于
# noqa
、# type: ignore
、# pylint disable=
等的注释或通过某种@nodoc
装饰器来禁用函数的 sphinx。
解决方法
一切正常,Sphinx 和 ABC 机器中都没有“错误”,语言中更是如此。
Sphinx 使用语言自省功能来检索类的成员,然后自省方法。当你结合 @classmethod 和 @property 时会发生什么,除了它有点作为一个很好的惊喜实际工作之外,当这样创建的类成员被 Sphynx 访问时,它必须在搜索文档字符串时执行,代码被触发并运行。
如果实际上不能组合使用 property 和 classmethod 实际上就不那么令人惊讶了,因为 property
和 classmethod
装饰器都使用描述符协议来创建具有适当方法的新对象他们实施。
我认为不那么令人惊讶的事情是在您的“类方法属性缓存”函数中放置一些显式保护,以便在 sphinx 处理文件时不运行。由于 sphinx 本身没有此功能,因此您可以为此使用环境变量,例如 GENERATING_DOCS
。 (这不存在,它可以是任何名称),然后在你的方法中设置一个守卫,例如:
...
def baseclass_property(self):
if os.environ.get("GENERATING_DOCS",False):
return
然后您要么在运行脚本之前手动设置此变量,要么将其设置在 Sphinx 的 conf.py
文件本身中。
如果你有几个这样的方法,并且不想在所有这些方法中编写保护代码,你可以做一个装饰器,同时,只需使用相同的装饰器一次应用其他 3 个装饰器:
from functools import cache,wraps
import os
def cachedclassproperty(func):
@wraps(func)
def wrapper(*args,**kwargs):
if os.environ.get("GENERATING_DOCS",False):
return
return func(*args,**kwargs)
return classmethod(property(cache(wrapper)))
现在,至于在元类上使用属性:我建议不要这样做。元类用于当您确实需要自定义类创建过程时,元类上的 property
也几乎是偶然地用作类属性。在这种情况下发生的所有事情,正如您所调查的那样,该属性将对类“dir
其他目的,如果你只是像我建议的那样添加一个守卫,甚至可能不会阻止 sphinx 正确记录类属性,如果它有一个文档字符串。如果你对 Sphinx 隐藏它,它显然不会被记录。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。