singlestoredb 1.16.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. singlestoredb/__init__.py +75 -0
  2. singlestoredb/ai/__init__.py +2 -0
  3. singlestoredb/ai/chat.py +139 -0
  4. singlestoredb/ai/embeddings.py +128 -0
  5. singlestoredb/alchemy/__init__.py +90 -0
  6. singlestoredb/apps/__init__.py +3 -0
  7. singlestoredb/apps/_cloud_functions.py +90 -0
  8. singlestoredb/apps/_config.py +72 -0
  9. singlestoredb/apps/_connection_info.py +18 -0
  10. singlestoredb/apps/_dashboards.py +47 -0
  11. singlestoredb/apps/_process.py +32 -0
  12. singlestoredb/apps/_python_udfs.py +100 -0
  13. singlestoredb/apps/_stdout_supress.py +30 -0
  14. singlestoredb/apps/_uvicorn_util.py +36 -0
  15. singlestoredb/auth.py +245 -0
  16. singlestoredb/config.py +484 -0
  17. singlestoredb/connection.py +1487 -0
  18. singlestoredb/converters.py +950 -0
  19. singlestoredb/docstring/__init__.py +33 -0
  20. singlestoredb/docstring/attrdoc.py +126 -0
  21. singlestoredb/docstring/common.py +230 -0
  22. singlestoredb/docstring/epydoc.py +267 -0
  23. singlestoredb/docstring/google.py +412 -0
  24. singlestoredb/docstring/numpydoc.py +562 -0
  25. singlestoredb/docstring/parser.py +100 -0
  26. singlestoredb/docstring/py.typed +1 -0
  27. singlestoredb/docstring/rest.py +256 -0
  28. singlestoredb/docstring/tests/__init__.py +1 -0
  29. singlestoredb/docstring/tests/_pydoctor.py +21 -0
  30. singlestoredb/docstring/tests/test_epydoc.py +729 -0
  31. singlestoredb/docstring/tests/test_google.py +1007 -0
  32. singlestoredb/docstring/tests/test_numpydoc.py +1100 -0
  33. singlestoredb/docstring/tests/test_parse_from_object.py +109 -0
  34. singlestoredb/docstring/tests/test_parser.py +248 -0
  35. singlestoredb/docstring/tests/test_rest.py +547 -0
  36. singlestoredb/docstring/tests/test_util.py +70 -0
  37. singlestoredb/docstring/util.py +141 -0
  38. singlestoredb/exceptions.py +120 -0
  39. singlestoredb/functions/__init__.py +16 -0
  40. singlestoredb/functions/decorator.py +201 -0
  41. singlestoredb/functions/dtypes.py +1793 -0
  42. singlestoredb/functions/ext/__init__.py +1 -0
  43. singlestoredb/functions/ext/arrow.py +375 -0
  44. singlestoredb/functions/ext/asgi.py +2133 -0
  45. singlestoredb/functions/ext/json.py +420 -0
  46. singlestoredb/functions/ext/mmap.py +413 -0
  47. singlestoredb/functions/ext/rowdat_1.py +724 -0
  48. singlestoredb/functions/ext/timer.py +89 -0
  49. singlestoredb/functions/ext/utils.py +218 -0
  50. singlestoredb/functions/signature.py +1578 -0
  51. singlestoredb/functions/typing/__init__.py +41 -0
  52. singlestoredb/functions/typing/numpy.py +20 -0
  53. singlestoredb/functions/typing/pandas.py +2 -0
  54. singlestoredb/functions/typing/polars.py +2 -0
  55. singlestoredb/functions/typing/pyarrow.py +2 -0
  56. singlestoredb/functions/utils.py +421 -0
  57. singlestoredb/fusion/__init__.py +11 -0
  58. singlestoredb/fusion/graphql.py +213 -0
  59. singlestoredb/fusion/handler.py +916 -0
  60. singlestoredb/fusion/handlers/__init__.py +0 -0
  61. singlestoredb/fusion/handlers/export.py +525 -0
  62. singlestoredb/fusion/handlers/files.py +690 -0
  63. singlestoredb/fusion/handlers/job.py +660 -0
  64. singlestoredb/fusion/handlers/models.py +250 -0
  65. singlestoredb/fusion/handlers/stage.py +502 -0
  66. singlestoredb/fusion/handlers/utils.py +324 -0
  67. singlestoredb/fusion/handlers/workspace.py +956 -0
  68. singlestoredb/fusion/registry.py +249 -0
  69. singlestoredb/fusion/result.py +399 -0
  70. singlestoredb/http/__init__.py +27 -0
  71. singlestoredb/http/connection.py +1267 -0
  72. singlestoredb/magics/__init__.py +34 -0
  73. singlestoredb/magics/run_personal.py +137 -0
  74. singlestoredb/magics/run_shared.py +134 -0
  75. singlestoredb/management/__init__.py +9 -0
  76. singlestoredb/management/billing_usage.py +148 -0
  77. singlestoredb/management/cluster.py +462 -0
  78. singlestoredb/management/export.py +295 -0
  79. singlestoredb/management/files.py +1102 -0
  80. singlestoredb/management/inference_api.py +105 -0
  81. singlestoredb/management/job.py +887 -0
  82. singlestoredb/management/manager.py +373 -0
  83. singlestoredb/management/organization.py +226 -0
  84. singlestoredb/management/region.py +169 -0
  85. singlestoredb/management/utils.py +423 -0
  86. singlestoredb/management/workspace.py +1927 -0
  87. singlestoredb/mysql/__init__.py +177 -0
  88. singlestoredb/mysql/_auth.py +298 -0
  89. singlestoredb/mysql/charset.py +214 -0
  90. singlestoredb/mysql/connection.py +2032 -0
  91. singlestoredb/mysql/constants/CLIENT.py +38 -0
  92. singlestoredb/mysql/constants/COMMAND.py +32 -0
  93. singlestoredb/mysql/constants/CR.py +78 -0
  94. singlestoredb/mysql/constants/ER.py +474 -0
  95. singlestoredb/mysql/constants/EXTENDED_TYPE.py +3 -0
  96. singlestoredb/mysql/constants/FIELD_TYPE.py +48 -0
  97. singlestoredb/mysql/constants/FLAG.py +15 -0
  98. singlestoredb/mysql/constants/SERVER_STATUS.py +10 -0
  99. singlestoredb/mysql/constants/VECTOR_TYPE.py +6 -0
  100. singlestoredb/mysql/constants/__init__.py +0 -0
  101. singlestoredb/mysql/converters.py +271 -0
  102. singlestoredb/mysql/cursors.py +896 -0
  103. singlestoredb/mysql/err.py +92 -0
  104. singlestoredb/mysql/optionfile.py +20 -0
  105. singlestoredb/mysql/protocol.py +450 -0
  106. singlestoredb/mysql/tests/__init__.py +19 -0
  107. singlestoredb/mysql/tests/base.py +126 -0
  108. singlestoredb/mysql/tests/conftest.py +37 -0
  109. singlestoredb/mysql/tests/test_DictCursor.py +132 -0
  110. singlestoredb/mysql/tests/test_SSCursor.py +141 -0
  111. singlestoredb/mysql/tests/test_basic.py +452 -0
  112. singlestoredb/mysql/tests/test_connection.py +851 -0
  113. singlestoredb/mysql/tests/test_converters.py +58 -0
  114. singlestoredb/mysql/tests/test_cursor.py +141 -0
  115. singlestoredb/mysql/tests/test_err.py +16 -0
  116. singlestoredb/mysql/tests/test_issues.py +514 -0
  117. singlestoredb/mysql/tests/test_load_local.py +75 -0
  118. singlestoredb/mysql/tests/test_nextset.py +88 -0
  119. singlestoredb/mysql/tests/test_optionfile.py +27 -0
  120. singlestoredb/mysql/tests/thirdparty/__init__.py +6 -0
  121. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/__init__.py +9 -0
  122. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/capabilities.py +323 -0
  123. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/dbapi20.py +865 -0
  124. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_capabilities.py +110 -0
  125. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_dbapi20.py +224 -0
  126. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_nonstandard.py +101 -0
  127. singlestoredb/mysql/times.py +23 -0
  128. singlestoredb/notebook/__init__.py +16 -0
  129. singlestoredb/notebook/_objects.py +213 -0
  130. singlestoredb/notebook/_portal.py +352 -0
  131. singlestoredb/py.typed +0 -0
  132. singlestoredb/pytest.py +352 -0
  133. singlestoredb/server/__init__.py +0 -0
  134. singlestoredb/server/docker.py +452 -0
  135. singlestoredb/server/free_tier.py +267 -0
  136. singlestoredb/tests/__init__.py +0 -0
  137. singlestoredb/tests/alltypes.sql +307 -0
  138. singlestoredb/tests/alltypes_no_nulls.sql +208 -0
  139. singlestoredb/tests/empty.sql +0 -0
  140. singlestoredb/tests/ext_funcs/__init__.py +702 -0
  141. singlestoredb/tests/local_infile.csv +3 -0
  142. singlestoredb/tests/test.ipynb +18 -0
  143. singlestoredb/tests/test.sql +680 -0
  144. singlestoredb/tests/test2.ipynb +18 -0
  145. singlestoredb/tests/test2.sql +1 -0
  146. singlestoredb/tests/test_basics.py +1332 -0
  147. singlestoredb/tests/test_config.py +318 -0
  148. singlestoredb/tests/test_connection.py +3103 -0
  149. singlestoredb/tests/test_dbapi.py +27 -0
  150. singlestoredb/tests/test_exceptions.py +45 -0
  151. singlestoredb/tests/test_ext_func.py +1472 -0
  152. singlestoredb/tests/test_ext_func_data.py +1101 -0
  153. singlestoredb/tests/test_fusion.py +1527 -0
  154. singlestoredb/tests/test_http.py +288 -0
  155. singlestoredb/tests/test_management.py +1599 -0
  156. singlestoredb/tests/test_plugin.py +33 -0
  157. singlestoredb/tests/test_results.py +171 -0
  158. singlestoredb/tests/test_types.py +132 -0
  159. singlestoredb/tests/test_udf.py +737 -0
  160. singlestoredb/tests/test_udf_returns.py +459 -0
  161. singlestoredb/tests/test_vectorstore.py +51 -0
  162. singlestoredb/tests/test_xdict.py +333 -0
  163. singlestoredb/tests/utils.py +141 -0
  164. singlestoredb/types.py +373 -0
  165. singlestoredb/utils/__init__.py +0 -0
  166. singlestoredb/utils/config.py +950 -0
  167. singlestoredb/utils/convert_rows.py +69 -0
  168. singlestoredb/utils/debug.py +13 -0
  169. singlestoredb/utils/dtypes.py +205 -0
  170. singlestoredb/utils/events.py +65 -0
  171. singlestoredb/utils/mogrify.py +151 -0
  172. singlestoredb/utils/results.py +585 -0
  173. singlestoredb/utils/xdict.py +425 -0
  174. singlestoredb/vectorstore.py +192 -0
  175. singlestoredb/warnings.py +5 -0
  176. singlestoredb-1.16.1.dist-info/METADATA +165 -0
  177. singlestoredb-1.16.1.dist-info/RECORD +183 -0
  178. singlestoredb-1.16.1.dist-info/WHEEL +5 -0
  179. singlestoredb-1.16.1.dist-info/entry_points.txt +2 -0
  180. singlestoredb-1.16.1.dist-info/licenses/LICENSE +201 -0
  181. singlestoredb-1.16.1.dist-info/top_level.txt +3 -0
  182. sqlx/__init__.py +4 -0
  183. sqlx/magic.py +113 -0
@@ -0,0 +1,33 @@
1
+ """Parse docstrings as per Sphinx notation."""
2
+ from .common import Docstring
3
+ from .common import DocstringDeprecated
4
+ from .common import DocstringMeta
5
+ from .common import DocstringParam
6
+ from .common import DocstringRaises
7
+ from .common import DocstringReturns
8
+ from .common import DocstringStyle
9
+ from .common import ParseError
10
+ from .common import RenderingStyle
11
+ from .parser import compose
12
+ from .parser import parse
13
+ from .parser import parse_from_object
14
+ from .util import combine_docstrings
15
+
16
+ Style = DocstringStyle # backwards compatibility
17
+
18
+ __all__ = [
19
+ 'parse',
20
+ 'parse_from_object',
21
+ 'combine_docstrings',
22
+ 'compose',
23
+ 'ParseError',
24
+ 'Docstring',
25
+ 'DocstringMeta',
26
+ 'DocstringParam',
27
+ 'DocstringRaises',
28
+ 'DocstringReturns',
29
+ 'DocstringDeprecated',
30
+ 'DocstringStyle',
31
+ 'RenderingStyle',
32
+ 'Style',
33
+ ]
@@ -0,0 +1,126 @@
1
+ """Attribute docstrings parsing.
2
+
3
+ .. seealso:: https://peps.python.org/pep-0257/#what-is-a-docstring
4
+ """
5
+ import ast
6
+ import inspect
7
+ import textwrap
8
+ import typing as T
9
+ from types import ModuleType
10
+
11
+ from .common import Docstring
12
+ from .common import DocstringParam
13
+
14
+
15
+ def ast_get_constant_value(node: ast.AST) -> T.Any:
16
+ """Return the constant's value if the given node is a constant."""
17
+ return getattr(node, 'value')
18
+
19
+
20
+ def ast_unparse(node: ast.AST) -> T.Optional[str]:
21
+ """Convert the AST node to source code as a string."""
22
+ if hasattr(ast, 'unparse'):
23
+ return ast.unparse(node)
24
+ # Support simple cases in Python < 3.9
25
+ if isinstance(node, ast.Constant):
26
+ return str(ast_get_constant_value(node))
27
+ if isinstance(node, ast.Name):
28
+ return node.id
29
+ return None
30
+
31
+
32
+ def ast_is_literal_str(node: ast.AST) -> bool:
33
+ """Return True if the given node is a literal string."""
34
+ return (
35
+ isinstance(node, ast.Expr)
36
+ and isinstance(node.value, ast.Constant)
37
+ and isinstance(ast_get_constant_value(node.value), str)
38
+ )
39
+
40
+
41
+ def ast_get_attribute(
42
+ node: ast.AST,
43
+ ) -> T.Optional[T.Tuple[str, T.Optional[str], T.Optional[str]]]:
44
+ """Return name, type and default if the given node is an attribute."""
45
+ if isinstance(node, (ast.Assign, ast.AnnAssign)):
46
+ target = (
47
+ node.targets[0] if isinstance(node, ast.Assign) else node.target
48
+ )
49
+ if isinstance(target, ast.Name):
50
+ type_str = None
51
+ if isinstance(node, ast.AnnAssign):
52
+ type_str = ast_unparse(node.annotation)
53
+ default = None
54
+ if node.value:
55
+ default = ast_unparse(node.value)
56
+ return target.id, type_str, default
57
+ return None
58
+
59
+
60
+ class AttributeDocstrings(ast.NodeVisitor):
61
+ """An ast.NodeVisitor that collects attribute docstrings."""
62
+
63
+ attr_docs: T.Dict[str, T.Tuple[str, T.Optional[str], T.Optional[str]]] = {}
64
+ prev_attr = None
65
+
66
+ def visit(self, node: T.Any) -> None:
67
+ if self.prev_attr and ast_is_literal_str(node):
68
+ attr_name, attr_type, attr_default = self.prev_attr
69
+ self.attr_docs[attr_name] = (
70
+ ast_get_constant_value(node.value),
71
+ attr_type,
72
+ attr_default,
73
+ )
74
+ self.prev_attr = ast_get_attribute(node)
75
+ if isinstance(node, (ast.ClassDef, ast.Module)):
76
+ self.generic_visit(node)
77
+
78
+ def get_attr_docs(
79
+ self, component: T.Any,
80
+ ) -> T.Dict[str, T.Tuple[str, T.Optional[str], T.Optional[str]]]:
81
+ """Get attribute docstrings from the given component.
82
+
83
+ :param component: component to process (class or module)
84
+ :returns: for each attribute docstring, a tuple with (description,
85
+ type, default)
86
+ """
87
+ self.attr_docs = {}
88
+ self.prev_attr = None
89
+ try:
90
+ source = textwrap.dedent(inspect.getsource(component))
91
+ except OSError:
92
+ pass
93
+ else:
94
+ tree = ast.parse(source)
95
+ if inspect.ismodule(component):
96
+ self.visit(tree)
97
+ elif isinstance(tree, ast.Module) and isinstance(
98
+ tree.body[0], ast.ClassDef,
99
+ ):
100
+ self.visit(tree.body[0])
101
+ return self.attr_docs
102
+
103
+
104
+ def add_attribute_docstrings(
105
+ obj: T.Union[type, ModuleType], docstring: Docstring,
106
+ ) -> None:
107
+ """Add attribute docstrings found in the object's source code.
108
+
109
+ :param obj: object from which to parse attribute docstrings
110
+ :param docstring: Docstring object where found attributes are added
111
+ :returns: list with names of added attributes
112
+ """
113
+ params = set(p.arg_name for p in docstring.params)
114
+ for arg_name, (description, type_name, default) in (
115
+ AttributeDocstrings().get_attr_docs(obj).items()
116
+ ):
117
+ if arg_name not in params:
118
+ param = DocstringParam(
119
+ args=['attribute', arg_name],
120
+ description=description,
121
+ arg_name=arg_name,
122
+ type_name=type_name,
123
+ is_optional=default is not None,
124
+ default=default,
125
+ )
126
+ docstring.meta.append(param)
@@ -0,0 +1,230 @@
1
+ """Common methods for parsing."""
2
+ import enum
3
+ import typing as T
4
+
5
+ PARAM_KEYWORDS = {
6
+ 'param',
7
+ 'parameter',
8
+ 'arg',
9
+ 'argument',
10
+ 'attribute',
11
+ 'key',
12
+ 'keyword',
13
+ }
14
+ RAISES_KEYWORDS = {'raises', 'raise', 'except', 'exception'}
15
+ DEPRECATION_KEYWORDS = {'deprecation', 'deprecated'}
16
+ RETURNS_KEYWORDS = {'return', 'returns'}
17
+ YIELDS_KEYWORDS = {'yield', 'yields'}
18
+ EXAMPLES_KEYWORDS = {'example', 'examples'}
19
+
20
+
21
+ class ParseError(RuntimeError):
22
+ """Base class for all parsing related errors."""
23
+
24
+
25
+ class DocstringStyle(enum.Enum):
26
+ """Docstring style."""
27
+
28
+ REST = 1
29
+ GOOGLE = 2
30
+ NUMPYDOC = 3
31
+ EPYDOC = 4
32
+ AUTO = 255
33
+
34
+
35
+ class RenderingStyle(enum.Enum):
36
+ """Rendering style when unparsing parsed docstrings."""
37
+
38
+ COMPACT = 1
39
+ CLEAN = 2
40
+ EXPANDED = 3
41
+
42
+
43
+ class DocstringMeta:
44
+ """Docstring meta information.
45
+
46
+ Symbolizes lines in form of
47
+
48
+ :param arg: description
49
+ :raises ValueError: if something happens
50
+ """
51
+
52
+ def __init__(
53
+ self, args: T.List[str], description: T.Optional[str],
54
+ ) -> None:
55
+ """Initialize self.
56
+
57
+ :param args: list of arguments. The exact content of this variable is
58
+ dependent on the kind of docstring; it's used to distinguish
59
+ between custom docstring meta information items.
60
+ :param description: associated docstring description.
61
+ """
62
+ self.args = args
63
+ self.description = description
64
+
65
+
66
+ class DocstringParam(DocstringMeta):
67
+ """DocstringMeta symbolizing :param metadata."""
68
+
69
+ def __init__(
70
+ self,
71
+ args: T.List[str],
72
+ description: T.Optional[str],
73
+ arg_name: str,
74
+ type_name: T.Optional[str],
75
+ is_optional: T.Optional[bool],
76
+ default: T.Optional[str],
77
+ ) -> None:
78
+ """Initialize self."""
79
+ super().__init__(args, description)
80
+ self.arg_name = arg_name
81
+ self.type_name = type_name
82
+ self.is_optional = is_optional
83
+ self.default = default
84
+
85
+
86
+ class DocstringReturns(DocstringMeta):
87
+ """DocstringMeta symbolizing :returns or :yields metadata."""
88
+
89
+ def __init__(
90
+ self,
91
+ args: T.List[str],
92
+ description: T.Optional[str],
93
+ type_name: T.Optional[str],
94
+ is_generator: bool,
95
+ return_name: T.Optional[str] = None,
96
+ ) -> None:
97
+ """Initialize self."""
98
+ super().__init__(args, description)
99
+ self.type_name = type_name
100
+ self.is_generator = is_generator
101
+ self.return_name = return_name
102
+
103
+
104
+ class DocstringRaises(DocstringMeta):
105
+ """DocstringMeta symbolizing :raises metadata."""
106
+
107
+ def __init__(
108
+ self,
109
+ args: T.List[str],
110
+ description: T.Optional[str],
111
+ type_name: T.Optional[str],
112
+ ) -> None:
113
+ """Initialize self."""
114
+ super().__init__(args, description)
115
+ self.type_name = type_name
116
+ self.description = description
117
+
118
+
119
+ class DocstringDeprecated(DocstringMeta):
120
+ """DocstringMeta symbolizing deprecation metadata."""
121
+
122
+ def __init__(
123
+ self,
124
+ args: T.List[str],
125
+ description: T.Optional[str],
126
+ version: T.Optional[str],
127
+ ) -> None:
128
+ """Initialize self."""
129
+ super().__init__(args, description)
130
+ self.version = version
131
+ self.description = description
132
+
133
+
134
+ class DocstringExample(DocstringMeta):
135
+ """DocstringMeta symbolizing example metadata."""
136
+
137
+ def __init__(
138
+ self,
139
+ args: T.List[str],
140
+ snippet: T.Optional[str],
141
+ description: T.Optional[str],
142
+ post_snippet: T.Optional[str] = None,
143
+ ) -> None:
144
+ """Initialize self."""
145
+ super().__init__(args, description)
146
+ self.snippet = snippet
147
+ self.description = description
148
+ self.post_snippet = post_snippet
149
+
150
+
151
+ class Docstring:
152
+ """Docstring object representation."""
153
+
154
+ def __init__(
155
+ self,
156
+ style=None, # type: T.Optional[DocstringStyle]
157
+ ) -> None:
158
+ """Initialize self."""
159
+ self.short_description = None # type: T.Optional[str]
160
+ self.long_description = None # type: T.Optional[str]
161
+ self.blank_after_short_description = False
162
+ self.blank_after_long_description = False
163
+ self.meta = [] # type: T.List[DocstringMeta]
164
+ self.style = style # type: T.Optional[DocstringStyle]
165
+
166
+ @property
167
+ def description(self) -> T.Optional[str]:
168
+ """Return the full description of the function
169
+
170
+ Returns None if the docstring did not include any description
171
+ """
172
+ ret = []
173
+ if self.short_description:
174
+ ret.append(self.short_description)
175
+ if self.blank_after_short_description:
176
+ ret.append('')
177
+ if self.long_description:
178
+ ret.append(self.long_description)
179
+
180
+ if not ret:
181
+ return None
182
+
183
+ return '\n'.join(ret)
184
+
185
+ @property
186
+ def params(self) -> T.List[DocstringParam]:
187
+ """Return a list of information on function params."""
188
+ return [item for item in self.meta if isinstance(item, DocstringParam)]
189
+
190
+ @property
191
+ def raises(self) -> T.List[DocstringRaises]:
192
+ """Return a list of information on the exceptions that the function
193
+ may raise.
194
+ """
195
+ return [
196
+ item for item in self.meta if isinstance(item, DocstringRaises)
197
+ ]
198
+
199
+ @property
200
+ def returns(self) -> T.Optional[DocstringReturns]:
201
+ """Return a single information on function return.
202
+
203
+ Takes the first return information.
204
+ """
205
+ for item in self.meta:
206
+ if isinstance(item, DocstringReturns):
207
+ return item
208
+ return None
209
+
210
+ @property
211
+ def many_returns(self) -> T.List[DocstringReturns]:
212
+ """Return a list of information on function return."""
213
+ return [
214
+ item for item in self.meta if isinstance(item, DocstringReturns)
215
+ ]
216
+
217
+ @property
218
+ def deprecation(self) -> T.Optional[DocstringDeprecated]:
219
+ """Return a single information on function deprecation notes."""
220
+ for item in self.meta:
221
+ if isinstance(item, DocstringDeprecated):
222
+ return item
223
+ return None
224
+
225
+ @property
226
+ def examples(self) -> T.List[DocstringExample]:
227
+ """Return a list of information on function examples."""
228
+ return [
229
+ item for item in self.meta if isinstance(item, DocstringExample)
230
+ ]
@@ -0,0 +1,267 @@
1
+ """Epyoc-style docstring parsing.
2
+
3
+ .. seealso:: http://epydoc.sourceforge.net/manual-fields.html
4
+ """
5
+ import inspect
6
+ import re
7
+ import typing as T
8
+
9
+ from .common import Docstring
10
+ from .common import DocstringMeta
11
+ from .common import DocstringParam
12
+ from .common import DocstringRaises
13
+ from .common import DocstringReturns
14
+ from .common import DocstringStyle
15
+ from .common import ParseError
16
+ from .common import RenderingStyle
17
+
18
+
19
+ def _clean_str(string: str) -> T.Optional[str]:
20
+ string = string.strip()
21
+ if len(string) > 0:
22
+ return string
23
+ return None
24
+
25
+
26
+ def parse(text: T.Optional[str]) -> Docstring:
27
+ """Parse the epydoc-style docstring into its components.
28
+
29
+ :returns: parsed docstring
30
+ """
31
+ ret = Docstring(style=DocstringStyle.EPYDOC)
32
+ if not text:
33
+ return ret
34
+
35
+ text = inspect.cleandoc(text)
36
+ match = re.search('^@', text, flags=re.M)
37
+ if match:
38
+ desc_chunk = text[:match.start()]
39
+ meta_chunk = text[match.start():]
40
+ else:
41
+ desc_chunk = text
42
+ meta_chunk = ''
43
+
44
+ parts = desc_chunk.split('\n', 1)
45
+ ret.short_description = parts[0] or None
46
+ if len(parts) > 1:
47
+ long_desc_chunk = parts[1] or ''
48
+ ret.blank_after_short_description = long_desc_chunk.startswith('\n')
49
+ ret.blank_after_long_description = long_desc_chunk.endswith('\n\n')
50
+ ret.long_description = long_desc_chunk.strip() or None
51
+
52
+ param_pattern = re.compile(
53
+ r'(param|keyword|type)(\s+[_A-z][_A-z0-9]*\??):',
54
+ )
55
+ raise_pattern = re.compile(r'(raise)(\s+[_A-z][_A-z0-9]*\??)?:')
56
+ return_pattern = re.compile(r'(return|rtype|yield|ytype):')
57
+ meta_pattern = re.compile(
58
+ r'([_A-z][_A-z0-9]+)((\s+[_A-z][_A-z0-9]*\??)*):',
59
+ )
60
+
61
+ # tokenize
62
+ stream: T.List[T.Tuple[str, str, T.List[str], str]] = []
63
+ for match in re.finditer(
64
+ r'(^@.*?)(?=^@|\Z)', meta_chunk, flags=re.S | re.M,
65
+ ):
66
+ chunk = match.group(0)
67
+ if not chunk:
68
+ continue
69
+
70
+ param_match = re.search(param_pattern, chunk)
71
+ raise_match = re.search(raise_pattern, chunk)
72
+ return_match = re.search(return_pattern, chunk)
73
+ meta_match = re.search(meta_pattern, chunk)
74
+
75
+ match = param_match or raise_match or return_match or meta_match
76
+ if not match:
77
+ raise ParseError(f'Error parsing meta information near "{chunk}".')
78
+
79
+ desc_chunk = chunk[match.end():]
80
+ if param_match:
81
+ base = 'param'
82
+ key = match.group(1)
83
+ args = [match.group(2).strip()]
84
+ elif raise_match:
85
+ base = 'raise'
86
+ key = match.group(1)
87
+ args = [] if match.group(2) is None else [match.group(2).strip()]
88
+ elif return_match:
89
+ base = 'return'
90
+ key = match.group(1)
91
+ args = []
92
+ else:
93
+ base = 'meta'
94
+ key = match.group(1)
95
+ token = _clean_str(match.group(2).strip())
96
+ args = [] if token is None else re.split(r'\s+', token)
97
+
98
+ # Make sure we didn't match some existing keyword in an incorrect
99
+ # way here:
100
+ if key in [
101
+ 'param',
102
+ 'keyword',
103
+ 'type',
104
+ 'return',
105
+ 'rtype',
106
+ 'yield',
107
+ 'ytype',
108
+ ]:
109
+ raise ParseError(
110
+ f'Error parsing meta information near "{chunk}".',
111
+ )
112
+
113
+ desc = desc_chunk.strip()
114
+ if '\n' in desc:
115
+ first_line, rest = desc.split('\n', 1)
116
+ desc = first_line + '\n' + inspect.cleandoc(rest)
117
+ stream.append((base, key, args, desc))
118
+
119
+ # Combine type_name, arg_name, and description information
120
+ params: T.Dict[str, T.Dict[str, T.Any]] = {}
121
+ for base, key, args, desc in stream:
122
+ if base not in ['param', 'return']:
123
+ continue # nothing to do
124
+
125
+ (arg_name,) = args or ('return',)
126
+ info = params.setdefault(arg_name, {})
127
+ info_key = 'type_name' if 'type' in key else 'description'
128
+ info[info_key] = desc
129
+
130
+ if base == 'return':
131
+ is_generator = key in {'ytype', 'yield'}
132
+ if info.setdefault('is_generator', is_generator) != is_generator:
133
+ raise ParseError(
134
+ f'Error parsing meta information for "{arg_name}".',
135
+ )
136
+
137
+ meta_item: T.Union[DocstringParam, DocstringReturns, DocstringRaises, DocstringMeta]
138
+ is_done: T.Dict[str, bool] = {}
139
+ for base, key, args, desc in stream:
140
+ if base == 'param' and not is_done.get(args[0], False):
141
+ (arg_name,) = args
142
+ info = params[arg_name]
143
+ type_name = info.get('type_name')
144
+
145
+ if type_name and type_name.endswith('?'):
146
+ is_optional = True
147
+ type_name = type_name[:-1]
148
+ else:
149
+ is_optional = False
150
+
151
+ match = re.match(r'.*defaults to (.+)', desc, flags=re.DOTALL)
152
+ default = match.group(1).rstrip('.') if match else None
153
+
154
+ meta_item = DocstringParam(
155
+ args=[key, arg_name],
156
+ description=info.get('description'),
157
+ arg_name=arg_name,
158
+ type_name=type_name,
159
+ is_optional=is_optional,
160
+ default=default,
161
+ )
162
+ is_done[arg_name] = True
163
+ elif base == 'return' and not is_done.get('return', False):
164
+ info = params['return']
165
+ meta_item = DocstringReturns(
166
+ args=[key],
167
+ description=info.get('description'),
168
+ type_name=info.get('type_name'),
169
+ is_generator=info.get('is_generator', False),
170
+ )
171
+ is_done['return'] = True
172
+ elif base == 'raise':
173
+ (type_name,) = args or (None,)
174
+ meta_item = DocstringRaises(
175
+ args=[key] + args,
176
+ description=desc,
177
+ type_name=type_name,
178
+ )
179
+ elif base == 'meta':
180
+ meta_item = DocstringMeta(
181
+ args=[key] + args,
182
+ description=desc,
183
+ )
184
+ else:
185
+ (key, *_) = args or ('return',)
186
+ assert is_done.get(key, False)
187
+ continue # don't append
188
+
189
+ ret.meta.append(meta_item)
190
+
191
+ return ret
192
+
193
+
194
+ def compose(
195
+ docstring: Docstring,
196
+ rendering_style: RenderingStyle = RenderingStyle.COMPACT,
197
+ indent: str = ' ',
198
+ ) -> str:
199
+ """Render a parsed docstring into docstring text.
200
+
201
+ :param docstring: parsed docstring representation
202
+ :param rendering_style: the style to render docstrings
203
+ :param indent: the characters used as indentation in the docstring string
204
+ :returns: docstring text
205
+ """
206
+
207
+ def process_desc(desc: T.Optional[str], is_type: bool) -> str:
208
+ if not desc:
209
+ return ''
210
+
211
+ if rendering_style == RenderingStyle.EXPANDED or (
212
+ rendering_style == RenderingStyle.CLEAN and not is_type
213
+ ):
214
+ (first, *rest) = desc.splitlines()
215
+ return '\n'.join(
216
+ ['\n' + indent + first] + [indent + line for line in rest],
217
+ )
218
+
219
+ (first, *rest) = desc.splitlines()
220
+ return '\n'.join([' ' + first] + [indent + line for line in rest])
221
+
222
+ parts: T.List[str] = []
223
+ if docstring.short_description:
224
+ parts.append(docstring.short_description)
225
+ if docstring.blank_after_short_description:
226
+ parts.append('')
227
+ if docstring.long_description:
228
+ parts.append(docstring.long_description)
229
+ if docstring.blank_after_long_description:
230
+ parts.append('')
231
+
232
+ for meta in docstring.meta:
233
+ if isinstance(meta, DocstringParam):
234
+ if meta.type_name:
235
+ type_name = (
236
+ f'{meta.type_name}?'
237
+ if meta.is_optional
238
+ else meta.type_name
239
+ )
240
+ text = f'@type {meta.arg_name}:'
241
+ text += process_desc(type_name, True)
242
+ parts.append(text)
243
+ text = f'@param {meta.arg_name}:' + process_desc(
244
+ meta.description, False,
245
+ )
246
+ parts.append(text)
247
+ elif isinstance(meta, DocstringReturns):
248
+ (arg_key, type_key) = (
249
+ ('yield', 'ytype')
250
+ if meta.is_generator
251
+ else ('return', 'rtype')
252
+ )
253
+ if meta.type_name:
254
+ text = f'@{type_key}:' + process_desc(meta.type_name, True)
255
+ parts.append(text)
256
+ if meta.description:
257
+ text = f'@{arg_key}:' + process_desc(meta.description, False)
258
+ parts.append(text)
259
+ elif isinstance(meta, DocstringRaises):
260
+ text = f'@raise {meta.type_name}:' if meta.type_name else '@raise:'
261
+ text += process_desc(meta.description, False)
262
+ parts.append(text)
263
+ else:
264
+ text = f'@{" ".join(meta.args)}:'
265
+ text += process_desc(meta.description, False)
266
+ parts.append(text)
267
+ return '\n'.join(parts)