Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/smart_open/doctools.py: 73%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# -*- coding: utf-8 -*-
2#
3# Copyright (C) 2019 Radim Rehurek <me@radimrehurek.com>
4#
5# This code is distributed under the terms and conditions
6# from the MIT License (MIT).
7#
9"""Common functions for working with docstrings.
11For internal use only.
12"""
14import contextlib
15import inspect
16import io
17import os.path
18import re
19import sys
21from . import compression
22from . import transport
24#
25# Python 3.13+ automatically trims docstrings (like inspect.cleandoc),
26# so we need to adjust the placeholder and indentation accordingly.
27#
28if sys.version_info >= (3, 13):
29 PLACEHOLDER = 'smart_open/doctools.py magic goes here'
30 LPAD = ''
31else:
32 PLACEHOLDER = ' smart_open/doctools.py magic goes here'
33 LPAD = ' '
36def extract_kwargs(docstring):
37 """Extract keyword argument documentation from a function's docstring.
39 Parameters
40 ----------
41 docstring: str
42 The docstring to extract keyword arguments from.
44 Returns
45 -------
46 list of (str, str, list str)
48 str
49 The name of the keyword argument.
50 str
51 Its type.
52 str
53 Its documentation as a list of lines.
55 Notes
56 -----
57 The implementation is rather fragile. It expects the following:
59 1. The parameters are under an underlined Parameters section
60 2. Keyword parameters have the literal ", optional" after the type
61 3. Names and types are not indented
62 4. Descriptions are indented with 4 spaces
63 5. The Parameters section ends with an empty line.
65 Examples
66 --------
68 >>> docstring = '''The foo function.
69 ... Parameters
70 ... ----------
71 ... bar: str, optional
72 ... This parameter is the bar.
73 ... baz: int, optional
74 ... This parameter is the baz.
75 ...
76 ... '''
77 >>> kwargs = extract_kwargs(docstring)
78 >>> kwargs[0]
79 ('bar', 'str, optional', ['This parameter is the bar.'])
81 """
82 if not docstring:
83 return []
85 lines = inspect.cleandoc(docstring).split('\n')
86 kwargs = []
88 #
89 # 1. Find the underlined 'Parameters' section
90 # 2. Once there, continue parsing parameters until we hit an empty line
91 #
92 while lines and lines[0] != 'Parameters':
93 lines.pop(0)
95 if not lines:
96 return []
98 lines.pop(0)
99 lines.pop(0)
101 for line in lines:
102 if not line.strip(): # stop at the first empty line encountered
103 break
104 is_arg_line = not line.startswith(' ')
105 if is_arg_line:
106 name, type_ = line.split(':', 1)
107 name, type_, description = name.strip(), type_.strip(), []
108 kwargs.append([name, type_, description])
109 continue
110 is_description_line = line.startswith(' ')
111 if is_description_line:
112 kwargs[-1][-1].append(line.strip())
114 return kwargs
117def to_docstring(kwargs, lpad=''):
118 """Reconstruct a docstring from keyword argument info.
120 Basically reverses :func:`extract_kwargs`.
122 Parameters
123 ----------
124 kwargs: list
125 Output from the extract_kwargs function
126 lpad: str, optional
127 Padding string (from the left).
129 Returns
130 -------
131 str
132 The docstring snippet documenting the keyword arguments.
134 Examples
135 --------
137 >>> kwargs = [
138 ... ('bar', 'str, optional', ['This parameter is the bar.']),
139 ... ('baz', 'int, optional', ['This parameter is the baz.']),
140 ... ]
141 >>> print(to_docstring(kwargs), end='')
142 bar: str, optional
143 This parameter is the bar.
144 baz: int, optional
145 This parameter is the baz.
147 """
148 buf = io.StringIO()
149 for name, type_, description in kwargs:
150 buf.write('%s%s: %s\n' % (lpad, name, type_))
151 for line in description:
152 buf.write('%s %s\n' % (lpad, line))
153 return buf.getvalue()
156def extract_examples_from_readme_rst(indent=None):
157 """Extract examples from this project's README.rst file.
159 Parameters
160 ----------
161 indent: str
162 Prepend each line with this string. Should contain some number of spaces.
164 Returns
165 -------
166 str
167 The examples.
169 Notes
170 -----
171 Quite fragile, depends on named labels inside the README.rst file.
172 """
173 if indent is None:
174 indent = LPAD
175 curr_dir = os.path.dirname(os.path.abspath(__file__))
176 readme_path = os.path.join(curr_dir, '..', 'README.rst')
177 try:
178 with open(readme_path) as fin:
179 lines = list(fin)
180 start = lines.index('.. _doctools_before_examples:\n')
181 end = lines.index(".. _doctools_after_examples:\n")
182 lines = lines[start+4:end-2]
183 return ''.join([indent + re.sub('^ ', '', line) for line in lines])
184 except Exception:
185 return indent + 'See README.rst'
188def tweak_open_docstring(f):
189 buf = io.StringIO()
190 seen = set()
192 root_path = os.path.dirname(os.path.dirname(__file__))
194 with contextlib.redirect_stdout(buf):
195 print(f'{LPAD}smart_open supports the following transport mechanisms:')
196 print()
197 for scheme, submodule in sorted(transport._REGISTRY.items()):
198 if scheme == transport.NO_SCHEME or submodule in seen:
199 continue
200 seen.add(submodule)
202 try:
203 schemes = submodule.SCHEMES
204 except AttributeError:
205 schemes = [scheme]
207 relpath = os.path.relpath(submodule.__file__, start=root_path)
208 heading = '%s (%s)' % ("/".join(schemes), relpath)
209 print(f'{LPAD}{heading}')
210 print(f'{LPAD}{"~" * len(heading)}')
211 print(f'{LPAD}{submodule.__doc__.split(chr(10))[0]}')
212 print()
214 kwargs = extract_kwargs(submodule.open.__doc__)
215 if kwargs:
216 print(to_docstring(kwargs, lpad=LPAD))
218 print(f'{LPAD}Examples')
219 print(f'{LPAD}--------')
220 print()
221 print(extract_examples_from_readme_rst(indent=LPAD))
223 print(f'{LPAD}This function also supports transparent compression and decompression ')
224 print(f'{LPAD}using the following codecs:')
225 print()
226 for extension in compression.get_supported_extensions():
227 print(f'{LPAD}* {extension}')
228 print()
229 print(f'{LPAD}The function depends on the file extension to determine the appropriate codec.')
231 #
232 # The docstring can be None if -OO was passed to the interpreter.
233 #
234 if f.__doc__:
235 f.__doc__ = f.__doc__.replace(PLACEHOLDER, buf.getvalue())
238def tweak_parse_uri_docstring(f):
239 buf = io.StringIO()
240 seen = set()
241 schemes = []
242 examples = []
244 for scheme, submodule in sorted(transport._REGISTRY.items()):
245 if scheme == transport.NO_SCHEME or submodule in seen:
246 continue
248 seen.add(submodule)
250 try:
251 examples.extend(submodule.URI_EXAMPLES)
252 except AttributeError:
253 pass
255 try:
256 schemes.extend(submodule.SCHEMES)
257 except AttributeError:
258 schemes.append(scheme)
260 with contextlib.redirect_stdout(buf):
261 print(f'{LPAD}Supported URI schemes are:')
262 print()
263 for scheme in schemes:
264 print(f'{LPAD}* {scheme}')
265 print()
266 print(f'{LPAD}Valid URI examples::')
267 print()
268 for example in examples:
269 print(f'{LPAD}* {example}')
271 if f.__doc__:
272 f.__doc__ = f.__doc__.replace(PLACEHOLDER, buf.getvalue())