summaryrefslogtreecommitdiff
path: root/doc/_extensions/pyjsdoc/__init__.py
diff options
context:
space:
mode:
authorVictor Feyens <vfe@odoo.com>2021-04-30 13:24:57 +0000
committerAntoine Vandevenne (anv) <anv@odoo.com>2021-05-04 08:09:20 +0000
commit27bad8c3e32049ec69a588df0558e57a0b928309 (patch)
treeb60e430163d486c8c1fd052738c9854aeb8300f5 /doc/_extensions/pyjsdoc/__init__.py
parent75a122bbae3a32bfa79da3506179fc63a8bced5d (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__.py408
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))