Changeset View
Standalone View
swh/web/api/apidoc.py
Show First 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | def __init__(self, document, urls, data): | ||||
self.args_set = set() | self.args_set = set() | ||||
self.params_set = set() | self.params_set = set() | ||||
self.inputs_set = set() | self.inputs_set = set() | ||||
self.returns_set = set() | self.returns_set = set() | ||||
self.status_codes_set = set() | self.status_codes_set = set() | ||||
self.reqheaders_set = set() | self.reqheaders_set = set() | ||||
self.resheaders_set = set() | self.resheaders_set = set() | ||||
self.field_list_visited = False | self.field_list_visited = False | ||||
self.current_json_obj = None | |||||
def process_paragraph(self, par): | def process_paragraph(self, par): | ||||
""" | """ | ||||
Process extracted paragraph text before display. | Process extracted paragraph text before display. | ||||
Cleanup document model markups and transform the | Cleanup document model markups and transform the | ||||
paragraph into a valid raw rst string (as the apidoc | paragraph into a valid raw rst string (as the apidoc | ||||
documentation transform rst to html when rendering). | documentation transform rst to html when rendering). | ||||
""" | """ | ||||
par = par.replace('\n', ' ') | par = par.replace('\n', ' ') | ||||
# keep emphasized, strong and literal text | # keep emphasized, strong and literal text | ||||
par = par.replace('<emphasis>', '*') | par = par.replace('<emphasis>', '*') | ||||
par = par.replace('</emphasis>', '*') | par = par.replace('</emphasis>', '*') | ||||
par = par.replace('<strong>', '**') | par = par.replace('<strong>', '**') | ||||
par = par.replace('</strong>', '**') | par = par.replace('</strong>', '**') | ||||
par = par.replace('<literal>', '``') | par = par.replace('<literal>', '``') | ||||
par = par.replace('</literal>', '``') | par = par.replace('</literal>', '``') | ||||
# remove parsed document markups | # remove parsed document markups | ||||
par = re.sub('<[^<]+?>', '', par) | par = re.sub('<[^<]+?>', '', par) | ||||
# api urls cleanup to generate valid links afterwards | # api urls cleanup to generate valid links afterwards | ||||
par = re.sub(r'\(\w+\)', '', par) | subs_made = 1 | ||||
par = re.sub(r'\[.*\]', '', par) | while subs_made: | ||||
(par, subs_made) = re.subn(r'(:http:.*)(\(\w+\))', r'\1', par) | |||||
subs_made = 1 | |||||
while subs_made: | |||||
(par, subs_made) = re.subn(r'(:http:.*)(\[.*\])', r'\1', par) | |||||
par = par.replace('//', '/') | par = par.replace('//', '/') | ||||
vlorentz: Use subn instead of search+sub:
```
replacements_made = 1
while replacements_made:
(par… | |||||
Done Inline Actions
Ack, thanks.
Because previously I was replacing every words between parenthesis but some can be part of the documentation without being url arguments. :>json object <swh_pid>: an object whose keys are input persistent identifiers and values objects with the following keys: * **known (bool)**: whether the object was found The (bool) string was replaced by an empty one previously. anlambert: > Use subn instead of search+sub:
Ack, thanks.
> And why do URLs need to be replaced multiple… | |||||
Not Done Inline ActionsThat explains the change in regexp, but why the while loop? vlorentz: That explains the change in regexp, but why the `while` loop? | |||||
Done Inline ActionsBecause some url description can have multiple parameters between parenthesis or brackets . For instance /api/1/content/[(hash_type):](hash)/filetype/ and we want to obtain /api/1/content/filetype/ to reference the endpoint. Previously, a global replacement was made but now we restrict the replacement to strings starting with :http: and use group capture. So we need to do replacements while a group is captured in the input string. anlambert: Because some url description can have multiple parameters between parenthesis or brackets . | |||||
Not Done Inline ActionsThis would probably deserve a comment explaining that, eg. with that example vlorentz: This would probably deserve a comment explaining that, eg. with that example | |||||
# transform references to api endpoints into valid rst links | # transform references to api endpoints into valid rst links | ||||
par = re.sub(':http:get:`([^,]*)`', r'`<\1>`_', par) | par = re.sub(':http:get:`([^,]*)`', r'`<\1>`_', par) | ||||
# transform references to some elements into bold text | # transform references to some elements into bold text | ||||
par = re.sub(':http:header:`(.*)`', r'**\1**', par) | par = re.sub(':http:header:`(.*)`', r'**\1**', par) | ||||
par = re.sub(':func:`(.*)`', r'**\1**', par) | par = re.sub(':func:`(.*)`', r'**\1**', par) | ||||
return par | return par | ||||
def visit_field_list(self, node): | def visit_field_list(self, node): | ||||
Show All 34 Lines | def visit_field_list(self, node): | ||||
else: | else: | ||||
self.data['input_type'] = 'object' | self.data['input_type'] = 'object' | ||||
# input object field | # input object field | ||||
if field_data[2] not in self.inputs_set: | if field_data[2] not in self.inputs_set: | ||||
self.data['inputs'].append({'name': field_data[2], | self.data['inputs'].append({'name': field_data[2], | ||||
'type': field_data[1], | 'type': field_data[1], | ||||
'doc': text}) | 'doc': text}) | ||||
self.inputs_set.add(field_data[2]) | self.inputs_set.add(field_data[2]) | ||||
self.current_json_obj = self.data['inputs'][-1] | |||||
# Response type | # Response type | ||||
if (field_data[0] in self.response_json_array_roles or | if (field_data[0] in self.response_json_array_roles or | ||||
field_data[0] in self.response_json_object_roles): | field_data[0] in self.response_json_object_roles): | ||||
# array | # array | ||||
if field_data[0] in self.response_json_array_roles: | if field_data[0] in self.response_json_array_roles: | ||||
self.data['return_type'] = 'array' | self.data['return_type'] = 'array' | ||||
# object | # object | ||||
else: | else: | ||||
self.data['return_type'] = 'object' | self.data['return_type'] = 'object' | ||||
# returned object field | # returned object field | ||||
if field_data[2] not in self.returns_set: | if field_data[2] not in self.returns_set: | ||||
self.data['returns'].append({'name': field_data[2], | self.data['returns'].append({'name': field_data[2], | ||||
'type': field_data[1], | 'type': field_data[1], | ||||
'doc': text}) | 'doc': text}) | ||||
self.returns_set.add(field_data[2]) | self.returns_set.add(field_data[2]) | ||||
self.current_json_obj = self.data['returns'][-1] | |||||
# Status Codes | # Status Codes | ||||
if field_data[0] in self.status_code_roles: | if field_data[0] in self.status_code_roles: | ||||
if field_data[1] not in self.status_codes_set: | if field_data[1] not in self.status_codes_set: | ||||
self.data['status_codes'].append({'code': field_data[1], # noqa | self.data['status_codes'].append({'code': field_data[1], # noqa | ||||
'doc': text}) | 'doc': text}) | ||||
self.status_codes_set.add(field_data[1]) | self.status_codes_set.add(field_data[1]) | ||||
# Request Headers | # Request Headers | ||||
if field_data[0] in self.request_header_roles: | if field_data[0] in self.request_header_roles: | ||||
▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | class _HTTPDomainDocVisitor(docutils.nodes.NodeVisitor): | ||||
def visit_bullet_list(self, node): | def visit_bullet_list(self, node): | ||||
# bullet list in endpoint description | # bullet list in endpoint description | ||||
if not self.field_list_visited: | if not self.field_list_visited: | ||||
self.data['description'] += '\n\n' | self.data['description'] += '\n\n' | ||||
for child in node.traverse(): | for child in node.traverse(): | ||||
# process list item | # process list item | ||||
if isinstance(child, docutils.nodes.paragraph): | if isinstance(child, docutils.nodes.paragraph): | ||||
line_text = self.process_paragraph(str(child)) | line_text = self.process_paragraph(str(child)) | ||||
self.data['description'] += '\t* %s\n' % line_text | self.data['description'] += '\t* %s\n' % line_text | ||||
elif self.current_json_obj: | |||||
self.current_json_obj['doc'] += '\n\n' | |||||
for child in node.traverse(): | |||||
# process list item | |||||
if isinstance(child, docutils.nodes.paragraph): | |||||
line_text = self.process_paragraph(str(child)) | |||||
self.current_json_obj['doc'] += '\t\t* %s\n' % line_text | |||||
self.current_json_obj = None | |||||
def visit_warning(self, node): | def visit_warning(self, node): | ||||
text = self.process_paragraph(str(node)) | text = self.process_paragraph(str(node)) | ||||
rst_warning = '\n\n.. warning::\n%s\n' % textwrap.indent(text, '\t') | rst_warning = '\n\n.. warning::\n%s\n' % textwrap.indent(text, '\t') | ||||
if rst_warning not in self.data['description']: | if rst_warning not in self.data['description']: | ||||
Not Done Inline Actionswhat is this about? vlorentz: what is this about? | |||||
Done Inline ActionsI am not proud of that s**t but I could not find any other way to do it. Basically, I just want to concatenate a bullet list to the input data / response data documentation. anlambert: I am not proud of that s**t but I could not find any other way to do it.
Basically, I just… | |||||
Done Inline ActionsI think I found a better solution, diff update incoming anlambert: I think I found a better solution, diff update incoming | |||||
self.data['description'] += rst_warning | self.data['description'] += rst_warning | ||||
def unknown_visit(self, node): | def unknown_visit(self, node): | ||||
pass | pass | ||||
def depart_document(self, node): | def depart_document(self, node): | ||||
""" | """ | ||||
End of parsing extra processing | End of parsing extra processing | ||||
▲ Show 20 Lines • Show All 205 Lines • Show Last 20 Lines |
Use subn instead of search+sub:
And why do URLs need to be replaced multiple times now?