diff options
| author | Victor Feyens <vfe@odoo.com> | 2021-04-30 13:24:57 +0000 |
|---|---|---|
| committer | Antoine Vandevenne (anv) <anv@odoo.com> | 2021-05-04 08:09:20 +0000 |
| commit | 27bad8c3e32049ec69a588df0558e57a0b928309 (patch) | |
| tree | b60e430163d486c8c1fd052738c9854aeb8300f5 /doc/_extensions/pyjsdoc/__init__.py | |
| parent | 75a122bbae3a32bfa79da3506179fc63a8bced5d (diff) | |
[MOV] doc: move documentation to a dedicated repository
In odoo/documentation-user#945, the developer documentation is merged
with the user documentation in a single repository and build config.
This commit then removes source files for the developer documentation
from the odoo/odoo repository.
DOC PR: https://github.com/odoo/documentation-user/pull/945
task-2351938
task-2352371
task-2205684
task-2352544
Co-authored-by: Victor Feyens <vfe@odoo.com>
Co-authored-by: Antoine Vandevenne <anv@odoo.com>
Diffstat (limited to 'doc/_extensions/pyjsdoc/__init__.py')
| -rw-r--r-- | doc/_extensions/pyjsdoc/__init__.py | 408 |
1 files changed, 0 insertions, 408 deletions
diff --git a/doc/_extensions/pyjsdoc/__init__.py b/doc/_extensions/pyjsdoc/__init__.py deleted file mode 100644 index 8368f73083e..00000000000 --- a/doc/_extensions/pyjsdoc/__init__.py +++ /dev/null @@ -1,408 +0,0 @@ -from __future__ import division -import re, json - -##### Parsing utilities ##### - -def split_delimited(delimiters, split_by, text): - """ - Generator that walks the ``text`` and splits it into an array on - ``split_by``, being careful not to break inside a delimiter pair. - ``delimiters`` should be an even-length string with each pair of matching - delimiters listed together, open first. - - - >>> list(split_delimited('{}[]', ',', '')) - [''] - >>> list(split_delimited('', ',', 'foo,bar')) - ['foo', 'bar'] - >>> list(split_delimited('[]', ',', 'foo,[bar, baz]')) - ['foo', '[bar, baz]'] - >>> list(split_delimited('{}', ' ', '{Type Name} name Desc')) - ['{Type Name}', 'name', 'Desc'] - >>> list(split_delimited('[]{}', ',', '[{foo,[bar, baz]}]')) - ['[{foo,[bar, baz]}]'] - - Two adjacent delimiters result in a zero-length string between them: - - >>> list(split_delimited('{}', ' ', '{Type Name} Desc')) - ['{Type Name}', '', 'Desc'] - - ``split_by`` may be a predicate function instead of a string, in which - case it should return true on a character to split. - - >>> list(split_delimited('', lambda c: c in '[]{}, ', '[{foo,[bar, baz]}]')) - ['', '', 'foo', '', 'bar', '', 'baz', '', '', ''] - - """ - delims = [0] * (len(delimiters) // 2) - actions = {} - for i in range(0, len(delimiters), 2): - actions[delimiters[i]] = (i // 2, 1) - actions[delimiters[i + 1]] = (i // 2, -1) - - if isinstance(split_by, str): - def split_fn(c): - return c == split_by - else: - split_fn = split_by - last = 0 - - for i in range(len(text)): - c = text[i] - if split_fn(c) and not any(delims): - yield text[last:i] - last = i + 1 - try: - which, dir = actions[c] - delims[which] = delims[which] + dir - except KeyError: - pass # Normal character - yield text[last:] - - -def strip_stars(doc_comment): - r""" - Strip leading stars from a doc comment. - - >>> strip_stars('/** This is a comment. */') - 'This is a comment.' - >>> strip_stars('/**\n * This is a\n * multiline comment. */') - 'This is a\n multiline comment.' - >>> strip_stars('/** \n\t * This is a\n\t * multiline comment. \n*/') - 'This is a\n multiline comment.' - - """ - return re.sub('\n\s*?\*\s*?', '\n', doc_comment[3:-2]).strip() - - -def split_tag(section): - """ - Split the JSDoc tag text (everything following the @) at the first - whitespace. Returns a tuple of (tagname, body). - """ - splitval = re.split('\s+', section, 1) - tag, body = len(splitval) > 1 and splitval or (splitval[0], '') - return tag.strip(), body.strip() - - -FUNCTION_REGEXPS = [ - 'function (\w+)', - '(\w+):\sfunction', - '\.(\w+)\s*=\s*function', -] - - -def guess_function_name(next_line, regexps=FUNCTION_REGEXPS): - """ - Attempt to determine the function name from the first code line - following the comment. The patterns recognized are described by - `regexps`, which defaults to FUNCTION_REGEXPS. If a match is successful, - returns the function name. Otherwise, returns None. - """ - for regexp in regexps: - match = re.search(regexp, next_line) - if match: - return match.group(1) - return None - - -def guess_parameters(next_line): - """ - Attempt to guess parameters based on the presence of a parenthesized - group of identifiers. If successful, returns a list of parameter names; - otherwise, returns None. - """ - match = re.search('\(([\w\s,]+)\)', next_line) - if match: - return [arg.strip() for arg in match.group(1).split(',')] - else: - return None - - -def parse_comment(doc_comment, next_line): - r""" - Split the raw comment text into a dictionary of tags. The main comment - body is included as 'doc'. - - >>> comment = get_doc_comments(read_file('examples/module.js'))[4][0] - >>> parse_comment(strip_stars(comment), '')['doc'] - 'This is the documentation for the fourth function.\n\n Since the function being documented is itself generated from another\n function, its name needs to be specified explicitly. using the @function tag' - >>> parse_comment(strip_stars(comment), '')['function'] - 'not_auto_discovered' - - If there are multiple tags with the same name, they're included as a list: - - >>> parse_comment(strip_stars(comment), '')['param'] - ['{String} arg1 The first argument.', '{Int} arg2 The second argument.'] - - """ - sections = re.split('\n\s*@', doc_comment) - tags = { - 'doc': sections[0].strip(), - 'guessed_function': guess_function_name(next_line), - 'guessed_params': guess_parameters(next_line) - } - for section in sections[1:]: - tag, body = split_tag(section) - if tag in tags: - existing = tags[tag] - try: - existing.append(body) - except AttributeError: - tags[tag] = [existing, body] - else: - tags[tag] = body - return tags - - -#### Classes ##### - -class CommentDoc(object): - """ - Base class for all classes that represent a parsed comment of some sort. - """ - - def __init__(self, parsed_comment): - self.parsed = parsed_comment - - def __str__(self): - return "Docs for " + self.name - - def __repr__(self): - return str(self) - - def __contains__(self, tag_name): - return tag_name in self.parsed - - def __getitem__(self, tag_name): - return self.get(tag_name) - - def get(self, tag_name, default=''): - """ - Return the value of a particular tag, or None if that tag doesn't - exist. Use 'doc' for the comment body itself. - """ - return self.parsed.get(tag_name, default) - - def get_as_list(self, tag_name): - """ - Return the value of a tag, making sure that it's a list. Absent - tags are returned as an empty-list; single tags are returned as a - one-element list. - - The returned list is a copy, and modifications do not affect the - original object. - """ - val = self.get(tag_name, []) - if isinstance(val, list): - return val[:] - else: - return [val] - - @property - def doc(self): - """ - Return the comment body. - """ - return self.get('doc') - - @property - def url(self): - """ - Return a URL for the comment, within the page. - """ - return '#' + self.name - - @property - def see(self): - """ - Return a list of all @see tags on the comment. - """ - return self.get_as_list('see') - - def to_json(self): - """ - Return a JSON representation of the CommentDoc. Keys are as per - to_dict. - """ - return json.dumps(self.to_dict()) - - def to_dict(self): - """ - Return a dictionary representation of the CommentDoc. The keys of - this correspond to the tags in the comment, with the comment body in - `doc`. - """ - return self.parsed.copy() - -class ParamDoc(object): - """ - Represents a parameter, option, or parameter-like object, basically - anything that has a name, a type, and a description, separated by spaces. - This is also used for return types and exceptions, which use an empty - string for the name. - - >>> param = ParamDoc('{Array<DOM>} elems The elements to act upon') - >>> param.name - 'elems' - >>> param.doc - 'The elements to act upon' - >>> param.type - 'Array<DOM>' - - You can also omit the type: if the first element is not surrounded by - curly braces, it's assumed to be the name instead: - - >>> param2 = ParamDoc('param1 The first param') - >>> param2.type - '' - >>> param2.name - 'param1' - >>> param2.doc - 'The first param' - - """ - - def __init__(self, text): - parsed = list(split_delimited('{}', ' ', text)) - if parsed[0].startswith('{') and parsed[0].endswith('}'): - self.type = parsed[0][1:-1] - self.name = parsed[1] - self.doc = ' '.join(parsed[2:]) - else: - self.type = '' - self.name = parsed[0] - self.doc = ' '.join(parsed[1:]) - - def to_dict(self): - """ - Convert this to a dict. Keys (all strings) are: - - - **name**: Parameter name - - **type**: Parameter type - - **doc**: Parameter description - """ - return { - 'name': self.name, - 'type': self.type, - 'doc': self.doc - } - - def to_html(self, css_class=''): - """ - Returns the parameter as a dt/dd pair. - """ - if self.name and self.type: - header_text = '%s (%s)' % (self.name, self.type) - elif self.type: - header_text = self.type - else: - header_text = self.name - return '<dt>%s</dt><dd>%s</dd>' % (header_text, self.doc) - - -##### DEPENDENCIES ##### - -class CyclicDependency(Exception): - """ - Exception raised if there is a cyclic dependency. - """ - - def __init__(self, remaining_dependencies): - self.values = remaining_dependencies - - def __str__(self): - return ('The following dependencies result in a cycle: ' - + ', '.join(self.values)) - - -class MissingDependency(Exception): - """ - Exception raised if a file references a dependency that doesn't exist. - """ - - def __init__(self, file, dependency): - self.file = file - self.dependency = dependency - - def __str__(self): - return "Couldn't find dependency %s when processing %s" % \ - (self.dependency, self.file) - - -def build_dependency_graph(start_nodes, js_doc): - """ - Build a graph where nodes are filenames and edges are reverse dependencies - (so an edge from jquery.js to jquery.dimensions.js indicates that jquery.js - must be included before jquery.dimensions.js). The graph is represented - as a dictionary from filename to (in-degree, edges) pair, for ease of - topological sorting. Also returns a list of nodes of degree zero. - """ - queue = [] - dependencies = {} - start_sort = [] - - def add_vertex(file): - in_degree = len(js_doc[file].module.dependencies) - dependencies[file] = [in_degree, []] - queue.append(file) - if in_degree == 0: - start_sort.append(file) - - def add_edge(from_file, to_file): - dependencies[from_file][1].append(to_file) - - def is_in_graph(file): - return file in dependencies - - for file in start_nodes: - add_vertex(file) - for file in queue: - for dependency in js_doc[file].module.dependencies: - if dependency not in js_doc: - raise MissingDependency(file, dependency) - if not is_in_graph(dependency): - add_vertex(dependency) - add_edge(dependency, file) - return dependencies, start_sort - - -def topological_sort(dependencies, start_nodes): - """ - Perform a topological sort on the dependency graph `dependencies`, starting - from list `start_nodes`. - """ - retval = [] - - def edges(node): - return dependencies[node][1] - - def in_degree(node): - return dependencies[node][0] - - def remove_incoming(node): - dependencies[node][0] = in_degree(node) - 1 - - while start_nodes: - node = start_nodes.pop() - retval.append(node) - for child in edges(node): - remove_incoming(child) - if not in_degree(child): - start_nodes.append(child) - leftover_nodes = [node for node in dependencies.keys() - if in_degree(node) > 0] - if leftover_nodes: - raise CyclicDependency(leftover_nodes) - else: - return retval - - -def find_dependencies(start_nodes, js_doc): - """ - Sort the dependency graph, taking in a list of starting module names and a - CodeBaseDoc (or equivalent dictionary). Returns an ordered list of - transitive dependencies such that no module appears before its - dependencies. - """ - return topological_sort(*build_dependency_graph(start_nodes, js_doc)) |
