项目文件夹

文件
wehub-resource-sync 593b94c120
pytest / Unit Tests (push) Has been cancelled
pytest / Integration (integration_tests_a) (push) Has been cancelled
pytest / Integration (integration_tests_b) (push) Has been cancelled
pytest / Integration (integration_tests_c) (push) Has been cancelled
pytest / Integration (integration_tests_d) (push) Has been cancelled
pytest / Integration (integration_tests_e) (push) Has been cancelled
pytest / Integration (integration_tests_f) (push) Has been cancelled
pytest / Integration (integration_tests_g) (push) Has been cancelled
pytest / Integration (integration_tests_h) (push) Has been cancelled
pytest / Integration (integration_tests_i) (push) Has been cancelled
pytest / Integration (integration_tests_j) (push) Has been cancelled
pytest / Distributed (distributed_a) (push) Has been cancelled
pytest / Distributed (distributed_b) (push) Has been cancelled
pytest / Distributed (distributed_c) (push) Has been cancelled
pytest / Distributed (distributed_d) (push) Has been cancelled
pytest / Distributed (distributed_e) (push) Has been cancelled
pytest / Distributed (distributed_f) (push) Has been cancelled
pytest / Minimal Install (push) Has been cancelled
pytest / Event File (push) Has been cancelled
pytest (slow) / py-slow (push) Has been cancelled
Publish JSON Schema / publish-schema (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:49:20 +08:00

184 行
6.0 KiB
Python

def PublicAPI(*args, **kwargs):
"""Annotation for documenting public APIs. Public APIs are classes and methods exposed to end users of Ludwig.
If stability="stable", the APIs will remain backwards compatible across minor Ludwig releases
(e.g., Ludwig 0.6 -> Ludwig 0.7).
If stability="experimental", the APIs can be used by advanced users who are tolerant to and expect
breaking changes. This will likely be seen in the case of incremental new feature development.
Args:
stability: One of {"stable", "experimental"}
Examples:
>>> from api_annotations import PublicAPI
>>> @PublicAPI
... def func1(x):
... return x
>>> @PublicAPI(stability="experimental")
... def func2(y):
... return y
"""
if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
return PublicAPI(stability="stable")(args[0])
if "stability" in kwargs:
stability = kwargs["stability"]
if stability not in ("stable", "experimental"):
raise ValueError(
f"Unknown stability level '{stability}'.\n"
f"Expected one of: 'stable', 'experimental'.\n"
f"Fix: use @PublicAPI(stability='stable') or @PublicAPI(stability='experimental')."
)
elif kwargs:
raise ValueError(f"Unknown kwargs: {kwargs.keys()}")
else:
stability = "stable"
def wrap(obj):
if stability == "experimental":
message = f"PublicAPI ({stability}): This API is {stability} and may change before becoming stable."
else:
message = "PublicAPI: This API is stable across Ludwig releases."
_append_doc(obj, message=message)
_mark_annotated(obj)
return obj
return wrap
def DeveloperAPI(*args, **kwargs):
"""Annotation for documenting developer APIs. Developer APIs are lower-level methods explicitly exposed to
advanced Ludwig users and library developers. Their interfaces may change across minor Ludwig releases (for
e.g., Ludwig 0.6.1 and Ludwig 0.6.2).
Examples:
>>> from api_annotations import DeveloperAPI
>>> @DeveloperAPI
... def func(x):
... return x
"""
if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
return DeveloperAPI()(args[0])
def wrap(obj):
_append_doc(obj, message="DeveloperAPI: This API may change across minor Ludwig releases.")
_mark_annotated(obj)
return obj
return wrap
def Deprecated(*args, **kwargs):
"""Annotation for documenting a deprecated API. Deprecated APIs may be removed in future releases of Ludwig
(e.g., Ludwig 0.7 to Ludwig 0.8).
Args:
message: A message to help users understand the reason for the deprecation, and provide a migration path.
Examples:
>>> from api_annotations import Deprecated
>>> @Deprecated
... def func(x):
... return x
>>> @Deprecated(message="g() is deprecated because the API is error prone. Please call h() instead.")
... def g(y):
... return y
"""
if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
return Deprecated()(args[0])
message = "**DEPRECATED:** This API is deprecated and may be removed in a future Ludwig release."
if "message" in kwargs:
message += " " + kwargs["message"]
del kwargs["message"]
if kwargs:
raise ValueError(f"Unknown kwargs: {kwargs.keys()}")
def inner(obj):
_append_doc(obj, message=message, directive="warning")
_mark_annotated(obj)
return obj
return inner
def _append_doc(obj, message: str, directive: str | None = None) -> str:
"""
Args:
message: An additional message to append to the end of docstring for a class
or method that uses one of the API annotations
directive: A shorter message that provides contexts for the message and indents it.
For example, this could be something like 'warning' or 'info'.
"""
if not obj.__doc__:
obj.__doc__ = ""
obj.__doc__ = obj.__doc__.rstrip()
indent = _get_indent(obj.__doc__)
obj.__doc__ += "\n\n"
if directive is not None:
obj.__doc__ += f"{' ' * indent}.. {directive}::\n"
obj.__doc__ += f"{' ' * (indent + 4)}{message}"
else:
obj.__doc__ += f"{' ' * indent}{message}"
obj.__doc__ += f"\n{' ' * indent}"
def _mark_annotated(obj) -> None:
# Set magic token for check_api_annotations linter.
if hasattr(obj, "__name__"):
obj._annotated = obj.__name__
def _is_annotated(obj) -> bool:
# Check the magic token exists and applies to this class (not a subclass).
return hasattr(obj, "_annotated") and obj._annotated == obj.__name__
def _get_indent(docstring: str) -> int:
"""
Example:
>>> def f():
... '''Docstring summary.'''
>>> f.__doc__
'Docstring summary.'
>>> _get_indent(f.__doc__)
0
>>> def g(foo):
... '''Docstring summary.
...
... Args:
... foo: Does bar.
... '''
>>> g.__doc__
'Docstring summary.\\n\\n Args:\\n foo: Does bar.\\n '
>>> _get_indent(g.__doc__)
4
>>> class A:
... def h():
... '''Docstring summary.
...
... Returns:
... None.
... '''
>>> A.h.__doc__
'Docstring summary.\\n\\n Returns:\\n None.\\n '
>>> _get_indent(A.h.__doc__)
8
"""
if not docstring:
return 0
non_empty_lines = list(filter(bool, docstring.splitlines()))
if len(non_empty_lines) == 1:
# Docstring contains summary only.
return 0
# The docstring summary isn't indented, so check the indentation of the second non-empty line.
return len(non_empty_lines[1]) - len(non_empty_lines[1].lstrip())