forked from bot/app
⬇️ 更新文档样式
This commit is contained in:
10
litedoc/docstring/__init__.py
Normal file
10
litedoc/docstring/__init__.py
Normal file
@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
|
||||
|
||||
@Time : 2024/8/28 下午1:46
|
||||
@Author : snowykami
|
||||
@Email : snowykami@outlook.com
|
||||
@File : __init__.py.py
|
||||
@Software: PyCharm
|
||||
"""
|
157
litedoc/docstring/docstring.py
Normal file
157
litedoc/docstring/docstring.py
Normal file
@ -0,0 +1,157 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
|
||||
|
||||
@Time : 2024/8/28 下午1:46
|
||||
@Author : snowykami
|
||||
@Email : snowykami@outlook.com
|
||||
@File : docstring.py
|
||||
@Software: PyCharm
|
||||
"""
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from litedoc.i18n import get_text
|
||||
|
||||
|
||||
class Attr(BaseModel):
|
||||
name: str
|
||||
type: str = ""
|
||||
desc: str = ""
|
||||
|
||||
|
||||
class Args(BaseModel):
|
||||
name: str
|
||||
type: str = ""
|
||||
desc: str = ""
|
||||
|
||||
|
||||
class Return(BaseModel):
|
||||
desc: str = ""
|
||||
|
||||
|
||||
class Exception_(BaseModel):
|
||||
name: str
|
||||
desc: str = ""
|
||||
|
||||
|
||||
class Raise(BaseModel):
|
||||
exceptions: list[Exception_] = []
|
||||
|
||||
|
||||
class Example(BaseModel):
|
||||
desc: str = ""
|
||||
input: str = ""
|
||||
output: str = ""
|
||||
|
||||
|
||||
class Docstring(BaseModel):
|
||||
desc: str = ""
|
||||
args: list[Args] = []
|
||||
attrs: list[Attr] = []
|
||||
return_: Optional[Return] = None
|
||||
raise_: list[Exception_] = []
|
||||
example: list[Example] = []
|
||||
|
||||
def add_desc(self, desc: str):
|
||||
if self.desc == "":
|
||||
self.desc = desc
|
||||
else:
|
||||
self.desc += "\n" + desc
|
||||
|
||||
def add_arg(self, name: str, type_: str = "", desc: str = ""):
|
||||
self.args.append(Args(name=name, type=type_, desc=desc))
|
||||
|
||||
def add_attrs(self, name: str, type_: str = "", desc: str = ""):
|
||||
self.attrs.append(Attr(name=name, type=type_, desc=desc))
|
||||
|
||||
def add_return(self, desc: str = ""):
|
||||
self.return_ = Return(desc=desc)
|
||||
|
||||
def add_raise(self, name: str, desc: str = ""):
|
||||
self.raise_.append(Exception_(name=name, desc=desc))
|
||||
|
||||
def add_example(self, desc: str = "", input_: str = "", output: str = ""):
|
||||
self.example.append(Example(desc=desc, input=input_, output=output))
|
||||
|
||||
def reduction(self) -> str:
|
||||
"""
|
||||
通过解析结果还原docstring
|
||||
Args:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
ret = ""
|
||||
ret += self.desc + "\n"
|
||||
if self.args:
|
||||
ret += "Args:\n"
|
||||
for arg in self.args:
|
||||
ret += f" {arg.name}: {arg.type}\n {arg.desc}\n"
|
||||
if self.attrs:
|
||||
ret += "Attributes:\n"
|
||||
for attr in self.attrs:
|
||||
ret += f" {attr.name}: {attr.type}\n {attr.desc}\n"
|
||||
if self.return_:
|
||||
ret += "Returns:\n"
|
||||
ret += f" {self.return_.desc}\n"
|
||||
|
||||
if self.raise_:
|
||||
ret += "Raises:\n"
|
||||
for exception in self.raise_:
|
||||
ret += f" {exception.name}\n {exception.desc}\n"
|
||||
|
||||
if self.example:
|
||||
ret += "Examples:\n"
|
||||
for example in self.example:
|
||||
ret += f" {example.desc}\n Input: {example.input}\n Output: {example.output}\n"
|
||||
|
||||
return ret
|
||||
|
||||
def markdown(self, lang: str, indent: int = 4, is_classmethod: bool = False) -> str:
|
||||
"""
|
||||
生成markdown文档
|
||||
Args:
|
||||
is_classmethod:
|
||||
lang:
|
||||
indent:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
PREFIX = "" * indent
|
||||
ret = ""
|
||||
# ret += self.desc + "\n\n"
|
||||
# print(self.reduction())
|
||||
# print(self.desc, self.return_)
|
||||
# 单数属性
|
||||
if self.desc:
|
||||
ret += PREFIX + f"\n**{get_text(lang, 'desc')}**: {self.desc}\n"
|
||||
|
||||
# 复数属性
|
||||
if self.args:
|
||||
ret += PREFIX + f"\n**{get_text(lang, 'docstring.args')}**:\n"
|
||||
for arg in self.args:
|
||||
ret += PREFIX + f"> - {arg.name}: {arg.type} {arg.desc}\n"
|
||||
if self.attrs:
|
||||
ret += PREFIX + f"\n**{get_text(lang, 'docstring.attrs')}**:\n"
|
||||
for attr in self.attrs:
|
||||
ret += PREFIX + f"> - {attr.name}: {attr.type} {attr.desc}\n"
|
||||
|
||||
# 单数属性
|
||||
if self.return_ is not None:
|
||||
ret += PREFIX + f"\n**{get_text(lang, 'docstring.return')}**: {self.return_.desc}\n"
|
||||
# 复数属性
|
||||
if self.raise_:
|
||||
ret += PREFIX + f"\n**{get_text(lang, 'docstring.raises')}**:\n"
|
||||
for exception in self.raise_:
|
||||
ret += PREFIX + f"> - {exception.name} {exception.desc}\n"
|
||||
if self.example:
|
||||
ret += PREFIX + f"\n**{get_text(lang, 'docstring.example')}**:\n"
|
||||
for example in self.example:
|
||||
ret += PREFIX + f" - {example.desc}\n> **{get_text(lang, 'docs.input')}**: {example.input}\n> **{get_text(lang, 'docs.output')}**: {example.output}\n"
|
||||
return ret
|
||||
|
||||
def __str__(self):
|
||||
return self.desc
|
192
litedoc/docstring/parser.py
Normal file
192
litedoc/docstring/parser.py
Normal file
@ -0,0 +1,192 @@
|
||||
"""
|
||||
Google docstring parser for Python.
|
||||
"""
|
||||
from typing import Optional
|
||||
|
||||
from litedoc.docstring.docstring import Docstring
|
||||
|
||||
|
||||
class Parser:
|
||||
...
|
||||
|
||||
|
||||
class GoogleDocstringParser(Parser):
|
||||
_tokens = {
|
||||
"Args" : "args",
|
||||
"Arguments" : "args",
|
||||
"参数" : "args",
|
||||
|
||||
"Return" : "return",
|
||||
"Returns" : "return",
|
||||
"返回" : "return",
|
||||
|
||||
"Attribute" : "attribute",
|
||||
"Attributes" : "attribute",
|
||||
"属性" : "attribute",
|
||||
|
||||
"Raises" : "raises",
|
||||
"Raise" : "raises",
|
||||
"引发" : "raises",
|
||||
|
||||
"Example" : "example",
|
||||
"Examples" : "example",
|
||||
"示例" : "example",
|
||||
|
||||
"Yields" : "yields",
|
||||
"Yield" : "yields",
|
||||
"产出" : "yields",
|
||||
|
||||
"Requires" : "requires",
|
||||
"Require" : "requires",
|
||||
"需要" : "requires",
|
||||
|
||||
"FrontMatter": "front_matter",
|
||||
"前言" : "front_matter",
|
||||
}
|
||||
|
||||
def __init__(self, docstring: str, indent: int = 4):
|
||||
self.lines = docstring.splitlines()
|
||||
self.indent = indent
|
||||
self.lineno = 0 # Current line number
|
||||
self.char = 0 # Current character position
|
||||
|
||||
self.docstring = Docstring()
|
||||
|
||||
def read_line(self, move: bool = True) -> str:
|
||||
"""
|
||||
每次读取一行
|
||||
Args:
|
||||
move: 是否移动指针
|
||||
Returns:
|
||||
"""
|
||||
if self.lineno >= len(self.lines):
|
||||
return ""
|
||||
line = self.lines[self.lineno]
|
||||
if move:
|
||||
self.lineno += 1
|
||||
return line
|
||||
|
||||
def match_token(self) -> Optional[str]:
|
||||
"""
|
||||
解析下一行的token
|
||||
Returns:
|
||||
|
||||
"""
|
||||
for token in self._tokens:
|
||||
line = self.read_line(move=False)
|
||||
if line.strip().startswith(token):
|
||||
self.lineno += 1
|
||||
return self._tokens[token]
|
||||
return None
|
||||
|
||||
def parse_args(self):
|
||||
"""
|
||||
依次解析后面的参数行,直到缩进小于等于当前行的缩进
|
||||
"""
|
||||
while line := self.match_next_line():
|
||||
if ":" in line:
|
||||
name, desc = line.split(":", 1)
|
||||
self.docstring.add_arg(name.strip(), desc.strip())
|
||||
else:
|
||||
self.docstring.add_arg(line.strip())
|
||||
|
||||
def parse_return(self):
|
||||
"""
|
||||
解析返回值行
|
||||
"""
|
||||
if line := self.match_next_line():
|
||||
self.docstring.add_return(line.strip())
|
||||
|
||||
def parse_raises(self):
|
||||
"""
|
||||
解析异常行
|
||||
"""
|
||||
while line := self.match_next_line():
|
||||
if ":" in line:
|
||||
name, desc = line.split(":", 1)
|
||||
self.docstring.add_raise(name.strip(), desc.strip())
|
||||
else:
|
||||
self.docstring.add_raise(line.strip())
|
||||
|
||||
def parse_example(self):
|
||||
"""
|
||||
解析示例行
|
||||
"""
|
||||
while line := self.match_next_line():
|
||||
if ":" in line:
|
||||
name, desc = line.split(":", 1)
|
||||
self.docstring.add_example(name.strip(), desc.strip())
|
||||
else:
|
||||
self.docstring.add_example(line.strip())
|
||||
|
||||
def parse_attrs(self):
|
||||
"""
|
||||
解析属性行
|
||||
"""
|
||||
while line := self.match_next_line():
|
||||
if ":" in line:
|
||||
name, desc = line.split(":", 1)
|
||||
self.docstring.add_attrs(name.strip(), desc.strip())
|
||||
else:
|
||||
self.docstring.add_attrs(line.strip())
|
||||
|
||||
def match_next_line(self) -> Optional[str]:
|
||||
"""
|
||||
在一个子解析器中,解析下一行,直到缩进小于等于当前行的缩进
|
||||
Returns:
|
||||
"""
|
||||
line = self.read_line(move=False)
|
||||
if line.startswith(" " * self.indent):
|
||||
self.lineno += 1
|
||||
return line[self.indent:]
|
||||
else:
|
||||
return None
|
||||
|
||||
def parse(self) -> Docstring:
|
||||
"""
|
||||
逐行解析,直到遇到EOS
|
||||
|
||||
最开始未解析到的内容全部加入desc
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
add_desc = True
|
||||
while self.lineno < len(self.lines):
|
||||
token = self.match_token()
|
||||
if token is None and add_desc:
|
||||
self.docstring.add_desc(self.lines[self.lineno].strip())
|
||||
|
||||
if token is not None:
|
||||
add_desc = False
|
||||
|
||||
match token:
|
||||
case "args":
|
||||
self.parse_args()
|
||||
case "return":
|
||||
self.parse_return()
|
||||
case "attribute":
|
||||
self.parse_attrs()
|
||||
case "raises":
|
||||
self.parse_raises()
|
||||
case "example":
|
||||
self.parse_example()
|
||||
case _:
|
||||
self.lineno += 1
|
||||
|
||||
return self.docstring
|
||||
|
||||
|
||||
class NumpyDocstringParser(Parser):
|
||||
...
|
||||
|
||||
|
||||
class ReStructuredParser(Parser):
|
||||
...
|
||||
|
||||
|
||||
def parse(docstring: str, parser: str = "google", indent: int = 4) -> Docstring:
|
||||
if parser == "google":
|
||||
return GoogleDocstringParser(docstring, indent).parse()
|
||||
else:
|
||||
raise ValueError(f"Unknown parser: {parser}")
|
Reference in New Issue
Block a user