chore: 添加虚拟环境到仓库
- 添加 backend_service/venv 虚拟环境 - 包含所有Python依赖包 - 注意:虚拟环境约393MB,包含12655个文件
This commit is contained in:
@@ -0,0 +1,259 @@
|
||||
"""ReST-style docstring parsing."""
|
||||
|
||||
import inspect
|
||||
import re
|
||||
import typing as T
|
||||
|
||||
from .common import (
|
||||
DEPRECATION_KEYWORDS,
|
||||
PARAM_KEYWORDS,
|
||||
RAISES_KEYWORDS,
|
||||
RETURNS_KEYWORDS,
|
||||
YIELDS_KEYWORDS,
|
||||
Docstring,
|
||||
DocstringDeprecated,
|
||||
DocstringMeta,
|
||||
DocstringParam,
|
||||
DocstringRaises,
|
||||
DocstringReturns,
|
||||
DocstringStyle,
|
||||
ParseError,
|
||||
RenderingStyle,
|
||||
)
|
||||
|
||||
|
||||
def _build_meta(args: T.List[str], desc: str) -> DocstringMeta:
|
||||
key = args[0]
|
||||
|
||||
if key in PARAM_KEYWORDS:
|
||||
if len(args) == 3:
|
||||
key, type_name, arg_name = args
|
||||
if type_name.endswith("?"):
|
||||
is_optional = True
|
||||
type_name = type_name[:-1]
|
||||
else:
|
||||
is_optional = False
|
||||
elif len(args) == 2:
|
||||
key, arg_name = args
|
||||
type_name = None
|
||||
is_optional = None
|
||||
else:
|
||||
raise ParseError(
|
||||
f"Expected one or two arguments for a {key} keyword."
|
||||
)
|
||||
|
||||
match = re.match(r".*defaults to (.+)", desc, flags=re.DOTALL)
|
||||
default = match.group(1).rstrip(".") if match else None
|
||||
|
||||
return DocstringParam(
|
||||
args=args,
|
||||
description=desc,
|
||||
arg_name=arg_name,
|
||||
type_name=type_name,
|
||||
is_optional=is_optional,
|
||||
default=default,
|
||||
)
|
||||
|
||||
if key in RETURNS_KEYWORDS | YIELDS_KEYWORDS:
|
||||
if len(args) == 2:
|
||||
type_name = args[1]
|
||||
elif len(args) == 1:
|
||||
type_name = None
|
||||
else:
|
||||
raise ParseError(
|
||||
f"Expected one or no arguments for a {key} keyword."
|
||||
)
|
||||
|
||||
return DocstringReturns(
|
||||
args=args,
|
||||
description=desc,
|
||||
type_name=type_name,
|
||||
is_generator=key in YIELDS_KEYWORDS,
|
||||
)
|
||||
|
||||
if key in DEPRECATION_KEYWORDS:
|
||||
match = re.search(
|
||||
r"^(?P<version>v?((?:\d+)(?:\.[0-9a-z\.]+))) (?P<desc>.+)",
|
||||
desc,
|
||||
flags=re.I,
|
||||
)
|
||||
return DocstringDeprecated(
|
||||
args=args,
|
||||
version=match.group("version") if match else None,
|
||||
description=match.group("desc") if match else desc,
|
||||
)
|
||||
|
||||
if key in RAISES_KEYWORDS:
|
||||
if len(args) == 2:
|
||||
type_name = args[1]
|
||||
elif len(args) == 1:
|
||||
type_name = None
|
||||
else:
|
||||
raise ParseError(
|
||||
f"Expected one or no arguments for a {key} keyword."
|
||||
)
|
||||
return DocstringRaises(
|
||||
args=args, description=desc, type_name=type_name
|
||||
)
|
||||
|
||||
return DocstringMeta(args=args, description=desc)
|
||||
|
||||
|
||||
def parse(text: str) -> Docstring:
|
||||
"""Parse the ReST-style docstring into its components.
|
||||
|
||||
:returns: parsed docstring
|
||||
"""
|
||||
ret = Docstring(style=DocstringStyle.REST)
|
||||
if not text:
|
||||
return ret
|
||||
|
||||
text = inspect.cleandoc(text)
|
||||
match = re.search("^:", text, flags=re.M)
|
||||
if match:
|
||||
desc_chunk = text[: match.start()]
|
||||
meta_chunk = text[match.start() :]
|
||||
else:
|
||||
desc_chunk = text
|
||||
meta_chunk = ""
|
||||
|
||||
parts = desc_chunk.split("\n", 1)
|
||||
ret.short_description = parts[0] or None
|
||||
if len(parts) > 1:
|
||||
long_desc_chunk = parts[1] or ""
|
||||
ret.blank_after_short_description = long_desc_chunk.startswith("\n")
|
||||
ret.blank_after_long_description = long_desc_chunk.endswith("\n\n")
|
||||
ret.long_description = long_desc_chunk.strip() or None
|
||||
|
||||
types = {}
|
||||
rtypes = {}
|
||||
for match in re.finditer(
|
||||
r"(^:.*?)(?=^:|\Z)", meta_chunk, flags=re.S | re.M
|
||||
):
|
||||
chunk = match.group(0)
|
||||
if not chunk:
|
||||
continue
|
||||
try:
|
||||
args_chunk, desc_chunk = chunk.lstrip(":").split(":", 1)
|
||||
except ValueError as ex:
|
||||
raise ParseError(
|
||||
f'Error parsing meta information near "{chunk}".'
|
||||
) from ex
|
||||
args = args_chunk.split()
|
||||
desc = desc_chunk.strip()
|
||||
|
||||
if "\n" in desc:
|
||||
first_line, rest = desc.split("\n", 1)
|
||||
desc = first_line + "\n" + inspect.cleandoc(rest)
|
||||
|
||||
# Add special handling for :type a: typename
|
||||
if len(args) == 2 and args[0] == "type":
|
||||
types[args[1]] = desc
|
||||
elif len(args) in [1, 2] and args[0] == "rtype":
|
||||
rtypes[None if len(args) == 1 else args[1]] = desc
|
||||
else:
|
||||
ret.meta.append(_build_meta(args, desc))
|
||||
|
||||
for meta in ret.meta:
|
||||
if isinstance(meta, DocstringParam):
|
||||
meta.type_name = meta.type_name or types.get(meta.arg_name)
|
||||
elif isinstance(meta, DocstringReturns):
|
||||
meta.type_name = meta.type_name or rtypes.get(meta.return_name)
|
||||
|
||||
if not any(isinstance(m, DocstringReturns) for m in ret.meta) and rtypes:
|
||||
for return_name, type_name in rtypes.items():
|
||||
ret.meta.append(
|
||||
DocstringReturns(
|
||||
args=[],
|
||||
type_name=type_name,
|
||||
description=None,
|
||||
is_generator=False,
|
||||
return_name=return_name,
|
||||
)
|
||||
)
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def compose(
|
||||
docstring: Docstring,
|
||||
rendering_style: RenderingStyle = RenderingStyle.COMPACT,
|
||||
indent: str = " ",
|
||||
) -> str:
|
||||
"""Render a parsed docstring into docstring text.
|
||||
|
||||
:param docstring: parsed docstring representation
|
||||
:param rendering_style: the style to render docstrings
|
||||
:param indent: the characters used as indentation in the docstring string
|
||||
:returns: docstring text
|
||||
"""
|
||||
|
||||
def process_desc(desc: T.Optional[str]) -> str:
|
||||
if not desc:
|
||||
return ""
|
||||
|
||||
if rendering_style == RenderingStyle.CLEAN:
|
||||
(first, *rest) = desc.splitlines()
|
||||
return "\n".join([" " + first] + [indent + line for line in rest])
|
||||
|
||||
if rendering_style == RenderingStyle.EXPANDED:
|
||||
(first, *rest) = desc.splitlines()
|
||||
return "\n".join(
|
||||
["\n" + indent + first] + [indent + line for line in rest]
|
||||
)
|
||||
|
||||
return " " + desc
|
||||
|
||||
parts: T.List[str] = []
|
||||
if docstring.short_description:
|
||||
parts.append(docstring.short_description)
|
||||
if docstring.blank_after_short_description:
|
||||
parts.append("")
|
||||
if docstring.long_description:
|
||||
parts.append(docstring.long_description)
|
||||
if docstring.blank_after_long_description:
|
||||
parts.append("")
|
||||
|
||||
for meta in docstring.meta:
|
||||
if isinstance(meta, DocstringParam):
|
||||
if meta.type_name:
|
||||
type_text = (
|
||||
f" {meta.type_name}? "
|
||||
if meta.is_optional
|
||||
else f" {meta.type_name} "
|
||||
)
|
||||
else:
|
||||
type_text = " "
|
||||
if rendering_style == RenderingStyle.EXPANDED:
|
||||
text = f":param {meta.arg_name}:"
|
||||
text += process_desc(meta.description)
|
||||
parts.append(text)
|
||||
if type_text[:-1]:
|
||||
parts.append(f":type {meta.arg_name}:{type_text[:-1]}")
|
||||
else:
|
||||
text = f":param{type_text}{meta.arg_name}:"
|
||||
text += process_desc(meta.description)
|
||||
parts.append(text)
|
||||
elif isinstance(meta, DocstringReturns):
|
||||
type_text = f" {meta.type_name}" if meta.type_name else ""
|
||||
key = "yields" if meta.is_generator else "returns"
|
||||
|
||||
if rendering_style == RenderingStyle.EXPANDED:
|
||||
if meta.description:
|
||||
text = f":{key}:"
|
||||
text += process_desc(meta.description)
|
||||
parts.append(text)
|
||||
if type_text:
|
||||
parts.append(f":rtype:{type_text}")
|
||||
else:
|
||||
text = f":{key}{type_text}:"
|
||||
text += process_desc(meta.description)
|
||||
parts.append(text)
|
||||
elif isinstance(meta, DocstringRaises):
|
||||
type_text = f" {meta.type_name} " if meta.type_name else ""
|
||||
text = f":raises{type_text}:" + process_desc(meta.description)
|
||||
parts.append(text)
|
||||
else:
|
||||
text = f':{" ".join(meta.args)}:' + process_desc(meta.description)
|
||||
parts.append(text)
|
||||
return "\n".join(parts)
|
||||
Reference in New Issue
Block a user