From e682e4c64043fb16c2242e31784fbbd8b159b035 Mon Sep 17 00:00:00 2001 From: Nicholas Gates Date: Thu, 10 May 2018 17:45:34 +0100 Subject: [PATCH 1/7] Markdown --- pyls/_utils.py | 12 +- pyls/lsp.py | 5 + pyls/markdown.py | 252 ++++++++++++++++++ pyls/plugins/hover.py | 11 +- pyls/plugins/jedi_completion.py | 3 + test/markdown_files/__init__.py | 0 test/markdown_files/aifc.md | 142 ++++++++++ test/markdown_files/aifc.pydoc | 134 ++++++++++ test/markdown_files/anydbm.md | 33 +++ test/markdown_files/anydbm.pydoc | 33 +++ test/markdown_files/astroid.md | 24 ++ test/markdown_files/astroid.pydoc | 23 ++ test/markdown_files/numpy.linspace.md | 73 +++++ test/markdown_files/numpy.linspace.pydoc | 71 +++++ test/markdown_files/scipy.md | 47 ++++ test/markdown_files/scipy.pydoc | 53 ++++ test/markdown_files/scipy.spatial.distance.md | 54 ++++ .../scipy.spatial.distance.pydoc | 71 +++++ test/markdown_files/scipy.spatial.md | 65 +++++ test/markdown_files/scipy.spatial.pydoc | 86 ++++++ test/test_markdown.py | 23 ++ tox.ini | 10 +- 22 files changed, 1208 insertions(+), 17 deletions(-) create mode 100644 pyls/markdown.py create mode 100644 test/markdown_files/__init__.py create mode 100644 test/markdown_files/aifc.md create mode 100644 test/markdown_files/aifc.pydoc create mode 100644 test/markdown_files/anydbm.md create mode 100644 test/markdown_files/anydbm.pydoc create mode 100644 test/markdown_files/astroid.md create mode 100644 test/markdown_files/astroid.pydoc create mode 100644 test/markdown_files/numpy.linspace.md create mode 100644 test/markdown_files/numpy.linspace.pydoc create mode 100644 test/markdown_files/scipy.md create mode 100644 test/markdown_files/scipy.pydoc create mode 100644 test/markdown_files/scipy.spatial.distance.md create mode 100644 test/markdown_files/scipy.spatial.distance.pydoc create mode 100644 test/markdown_files/scipy.spatial.md create mode 100644 test/markdown_files/scipy.spatial.pydoc create mode 100644 test/test_markdown.py diff --git a/pyls/_utils.py b/pyls/_utils.py index 25cf889d..b45c2329 100644 --- a/pyls/_utils.py +++ b/pyls/_utils.py @@ -5,6 +5,8 @@ import os import threading +from .markdown import rst2markdown + log = logging.getLogger(__name__) @@ -97,15 +99,9 @@ def _merge_dicts_(a, b): def format_docstring(contents): - """Python doc strings come in a number of formats, but LSP wants markdown. - - Until we can find a fast enough way of discovering and parsing each format, - we can do a little better by at least preserving indentation. - """ - contents = contents.replace('\t', u'\u00A0' * 4) - contents = contents.replace(' ', u'\u00A0' * 2) - contents = contents.replace('*', '\\*') + """Python doc strings come in a number of formats, but LSP wants markdown.""" return contents + #return rst2markdown(contents) def clip_column(column, lines, line_number): diff --git a/pyls/lsp.py b/pyls/lsp.py index 728b1044..2a1cfd34 100644 --- a/pyls/lsp.py +++ b/pyls/lsp.py @@ -33,6 +33,11 @@ class DiagnosticSeverity(object): Hint = 4 +class MarkupKind(object): + PlainText = 'plaintext' + Markdown = 'markdown' + + class MessageType(object): Error = 1 Warning = 2 diff --git a/pyls/markdown.py b/pyls/markdown.py new file mode 100644 index 00000000..c2240621 --- /dev/null +++ b/pyls/markdown.py @@ -0,0 +1,252 @@ +# Copyright 2017 Palantir Technologies, Inc. +# +# Based on https://github.com/Microsoft/vscode-python/blob/29a0caea60354ac24232bc038d1f5af23db29732 \ +# /src/client/common/markdown/restTextConverter.ts + + +def rst2markdown(docstring): + """Translates reStructruredText (Python doc syntax) to markdown. + + It only translates as much as needed to display nice-ish hovers. See https://en.wikipedia.org/wiki/ReStructuredText + """ + return _Rst2Markdown().convert(docstring) + + +_STATE_DEFAULT = "default" +_STATE_PREFORMATTED = "preformatted" +_STATE_CODE = "code" +_STATE_DOCTEST = "doctest" + + +class _Rst2Markdown(object): + + def __init__(self): + self._md = [] + self._state = _STATE_DEFAULT + + def convert(self, docstring): + lines = docstring.splitlines() + i = 0 + + while i < len(lines): + line = lines[i] + + # Ignore leading empty lines + if not self._md and not line: + i += 1 + continue + + if self._state == _STATE_DEFAULT: + i += self._default(lines, i) + elif self._state == _STATE_PREFORMATTED: + i += self._preformatted(lines, i) + elif self._state == _STATE_CODE: + self._code(line) + elif self._state == _STATE_DOCTEST: + self._doctest(line) + + i += 1 + + self._end_code() + self._end_preformatted() + + return '\n'.join(self._md).strip() + + def _default(self, lines, i): + line = lines[i] + + if line.startswith('```'): + self._start_code() + return 0 + + if line.startswith('>>>'): + self._start_doctest() + return -1 + + if line.startswith('===') or line.startswith('---'): + # Eat standalone === or --- lines + return 0 + + if self._double_colon(line): + return 0 + + if _is_ignorable(line): + return 0 + + if self._section_header(lines, i): + # Eat line with === or --- + return 1 + + result = self._check_pre_content(lines, i) + if self._state != _STATE_DEFAULT: + return result + + line = _cleanup(line) + # Convert double backticks to single + line = line.replace('``', '`') + line = _escape_markdown(line) + self._md.append(line) + + return 0 + + def _preformatted(self, lines, i): + line = lines[i] + if _is_ignorable(line): + return 0 + + # Preformatted block terminates by a line without leading whitespace + if line and not _is_whitespace(line[0]) and not _is_list_item(line): + self._end_preformatted() + return -1 + + prev_line = self._md[-1] if self._md else None + if not line and prev_line is not None and (not prev_line or prev_line.startswith('```')): + # Avoid more than one empty line in a row + return 0 + + # Since we use HTML blocks for preformatted text, drop angle brackets + line = line.replace('<', ' ').replace('>', ' ').rstrip() + # Convert double backticks to single + line = line.replace('``', '`') + + self._md.append(line) + + return 0 + + def _code(self, line): + prev_line = self._md[-1] if self._md else None + if not line and prev_line is not None and (not prev_line or prev_line.startswith('```')): + # Avoid more than one empty line in a row + return + + if line.startswith('```'): + self._end_code() + else: + self._md.append(line) + + def _doctest(self, line): + if not line: + self._end_code() + else: + self._md.append(line) + + def _check_pre_content(self, lines, i): + line = lines[i] + if i == 0 or not line.strip(): + return 0 + + if not _is_whitespace(line[0]) and not _is_list_item(line): + # Regular line, do nothing + return 0 + + # Indented content is considered to be preformatted + self._start_preformatted() + return -1 + + def _section_header(self, lines, i): + line = lines[i] + if i >= len(lines) - 1: + # No next line + return False + + next_line = lines[i + 1] + if next_line.startswith("==="): + # Section title -> heading level 3 + self._md.append('### ' + _cleanup(line)) + return True + elif next_line.startswith("---"): + # Subsection title -> heading level 4 + self._md.append('#### ' + _cleanup(line)) + return True + else: + return False + + def _double_colon(self, line): + if not line.endswith("::"): + return False + + # Literal blocks being with `::` + if len(line) > 2 and not line.startswith(".."): + # Ignore lines like .. autosummary:: blah + # Trim trailing : so :: turns into : + self._md.append(line[:-1]) + + self._start_preformatted() + return True + + def _start_doctest(self): + self._start_code() + self._state = _STATE_DOCTEST + + def _start_code(self): + # Remove previous empty line so we avoid double empties + self._try_remove_preceeding_empty_lines() + self._md.append('```python') + self._state = _STATE_CODE + + def _end_code(self): + if self._state == _STATE_CODE: + self._try_remove_preceeding_empty_lines() + self._md.append('```') + self._state = _STATE_DEFAULT + + def _start_preformatted(self): + # Remove previous empty line so we avoid double empties + self._try_remove_preceeding_empty_lines() + # Lie about the language since we don't want preformatted text + # to be colorized as Python. HTML is more 'appropriate' as it does + # not colorize - - or + or keywords like 'from'. + self._md.append('```html') + self._state = _STATE_PREFORMATTED + + def _end_preformatted(self): + if self._state == _STATE_PREFORMATTED: + self._try_remove_preceeding_empty_lines() + self._md.append('```') + self._state = _STATE_DEFAULT + + def _try_remove_preceeding_empty_lines(self): + while self._md and not len(self._md[-1].strip()): + self._md.pop() + + +def _is_ignorable(line): + if 'generated/' in line: + # Drop generated content + return True + + trimmed = line.strip() + if trimmed.startswith("..") and '::' in trimmed: + # Ignore lines like .. sectionauthor:: blah + return True + + return False + + +def _is_list_item(line): + """True if the line is part of a list.""" + trimmed = line.strip() + if trimmed: + char = trimmed[0] + return char == "*" or char == "-" or _is_decimal(char) + return False + + +def _is_whitespace(string): + return not string or string.isspace() + + +def _is_decimal(string): + try: + int(string) + return True + except ValueError: + return False + + +def _cleanup(line): + return line.replace(':mod:', 'module:') + + +def _escape_markdown(string): + return string.replace('#', '\\#').replace('*', '\\*').replace(' _', ' \\_') diff --git a/pyls/plugins/hover.py b/pyls/plugins/hover.py index fe1eca82..37371341 100644 --- a/pyls/plugins/hover.py +++ b/pyls/plugins/hover.py @@ -1,6 +1,6 @@ # Copyright 2017 Palantir Technologies, Inc. import logging -from pyls import hookimpl, _utils +from pyls import hookimpl, lsp, _utils log = logging.getLogger(__name__) @@ -14,7 +14,10 @@ def pyls_hover(document, position): definitions = [d for d in definitions if d.name == word] if not definitions: - # :( - return {'contents': ''} + return None - return {'contents': _utils.format_docstring(definitions[0].docstring()) or ""} + md_docstring = _utils.format_docstring(definitions[0].docstring()) + return {'contents': { + 'type': lsp.MarkupKind.Markdown, + 'value': md_docstring + }} if md_docstring else None diff --git a/pyls/plugins/jedi_completion.py b/pyls/plugins/jedi_completion.py index e6c5bf80..65d7a141 100644 --- a/pyls/plugins/jedi_completion.py +++ b/pyls/plugins/jedi_completion.py @@ -1,10 +1,13 @@ # Copyright 2017 Palantir Technologies, Inc. import logging from pyls import hookimpl, lsp, _utils +import numpy log = logging.getLogger(__name__) +numpy.linspace + @hookimpl def pyls_completions(document, position): definitions = document.jedi_script(position).completions() diff --git a/test/markdown_files/__init__.py b/test/markdown_files/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/test/markdown_files/aifc.md b/test/markdown_files/aifc.md new file mode 100644 index 00000000..8d647ba6 --- /dev/null +++ b/test/markdown_files/aifc.md @@ -0,0 +1,142 @@ +Stuff to parse AIFF-C and AIFF files. + +Unless explicitly stated otherwise, the description below is true +both for AIFF-C files and AIFF files. + +An AIFF-C file has the following structure. +```html + +-----------------+ + | FORM | + +-----------------+ + | size | + +----+------------+ + | | AIFC | + | +------------+ + | | chunks | + | | . | + | | . | + | | . | + +----+------------+ +``` +An AIFF file has the string "AIFF" instead of "AIFC". + +A chunk consists of an identifier (4 bytes) followed by a size (4 bytes, +big endian order), followed by the data. The size field does not include +the size of the 8 byte header. + +The following chunk types are recognized. +```html + FVER + version number of AIFF-C defining document (AIFF-C only). + MARK + # of markers (2 bytes) + list of markers: + marker ID (2 bytes, must be 0) + position (4 bytes) + marker name ("pstring") + COMM + # of channels (2 bytes) + # of sound frames (4 bytes) + size of the samples (2 bytes) + sampling frequency (10 bytes, IEEE 80-bit extended + floating point) + in AIFF-C files only: + compression type (4 bytes) + human-readable version of compression type ("pstring") + SSND + offset (4 bytes, not used by this program) + blocksize (4 bytes, not used by this program) + sound data +``` +A pstring consists of 1 byte length, a string of characters, and 0 or 1 +byte pad to make the total length even. + +Usage. + +Reading AIFF files: +```html + f = aifc.open(file, 'r') +``` +where file is either the name of a file or an open file pointer. +The open file pointer must have methods read(), seek(), and close(). +In some types of audio files, if the setpos() method is not used, +the seek() method is not necessary. + +This returns an instance of a class with the following public methods: +```html + getnchannels() -- returns number of audio channels (1 for + mono, 2 for stereo) + getsampwidth() -- returns sample width in bytes + getframerate() -- returns sampling frequency + getnframes() -- returns number of audio frames + getcomptype() -- returns compression type ('NONE' for AIFF files) + getcompname() -- returns human-readable version of + compression type ('not compressed' for AIFF files) + getparams() -- returns a tuple consisting of all of the + above in the above order + getmarkers() -- get the list of marks in the audio file or None + if there are no marks + getmark(id) -- get mark with the specified id (raises an error + if the mark does not exist) + readframes(n) -- returns at most n frames of audio + rewind() -- rewind to the beginning of the audio stream + setpos(pos) -- seek to the specified position + tell() -- return the current position + close() -- close the instance (make it unusable) +``` +The position returned by tell(), the position given to setpos() and +the position of marks are all compatible and have nothing to do with +the actual position in the file. +The close() method is called automatically when the class instance +is destroyed. + +Writing AIFF files: +```html + f = aifc.open(file, 'w') +``` +where file is either the name of a file or an open file pointer. +The open file pointer must have methods write(), tell(), seek(), and +close(). + +This returns an instance of a class with the following public methods: +```html + aiff() -- create an AIFF file (AIFF-C default) + aifc() -- create an AIFF-C file + setnchannels(n) -- set the number of channels + setsampwidth(n) -- set the sample width + setframerate(n) -- set the frame rate + setnframes(n) -- set the number of frames + setcomptype(type, name) + -- set the compression type and the + human-readable compression type + setparams(tuple) + -- set all parameters at once + setmark(id, pos, name) + -- add specified mark to the list of marks + tell() -- return current position in output file (useful + in combination with setmark()) + writeframesraw(data) + -- write audio frames without pathing up the + file header + writeframes(data) + -- write audio frames and patch up the file header + close() -- patch up the file header and close the + output file +``` +You should set the parameters before the first writeframesraw or +writeframes. The total number of frames does not need to be set, +but when it is set to the correct value, the header does not have to +be patched up. +It is best to first set all parameters, perhaps possibly the +compression type, and then write audio frames using writeframesraw. +When all frames have been written, either call writeframes('') or +close() to patch up the sizes in the header. +Marks can be added anytime. If there are any marks, you must call +close() after all frames have been written. +The close() method is called automatically when the class instance +is destroyed. + +When a file is opened with the extension '.aiff', an AIFF file is +written, otherwise an AIFF-C file is written. This default can be +changed by calling aiff() or aifc() before the first writeframes or +writeframesraw. diff --git a/test/markdown_files/aifc.pydoc b/test/markdown_files/aifc.pydoc new file mode 100644 index 00000000..a4cc346d --- /dev/null +++ b/test/markdown_files/aifc.pydoc @@ -0,0 +1,134 @@ +Stuff to parse AIFF-C and AIFF files. + +Unless explicitly stated otherwise, the description below is true +both for AIFF-C files and AIFF files. + +An AIFF-C file has the following structure. + + +-----------------+ + | FORM | + +-----------------+ + | | + +----+------------+ + | | AIFC | + | +------------+ + | | | + | | . | + | | . | + | | . | + +----+------------+ + +An AIFF file has the string "AIFF" instead of "AIFC". + +A chunk consists of an identifier (4 bytes) followed by a size (4 bytes, +big endian order), followed by the data. The size field does not include +the size of the 8 byte header. + +The following chunk types are recognized. + + FVER + (AIFF-C only). + MARK + <# of markers> (2 bytes) + list of markers: + (2 bytes, must be > 0) + (4 bytes) + ("pstring") + COMM + <# of channels> (2 bytes) + <# of sound frames> (4 bytes) + (2 bytes) + (10 bytes, IEEE 80-bit extended + floating point) + in AIFF-C files only: + (4 bytes) + ("pstring") + SSND + (4 bytes, not used by this program) + (4 bytes, not used by this program) + + +A pstring consists of 1 byte length, a string of characters, and 0 or 1 +byte pad to make the total length even. + +Usage. + +Reading AIFF files: + f = aifc.open(file, 'r') +where file is either the name of a file or an open file pointer. +The open file pointer must have methods read(), seek(), and close(). +In some types of audio files, if the setpos() method is not used, +the seek() method is not necessary. + +This returns an instance of a class with the following public methods: + getnchannels() -- returns number of audio channels (1 for + mono, 2 for stereo) + getsampwidth() -- returns sample width in bytes + getframerate() -- returns sampling frequency + getnframes() -- returns number of audio frames + getcomptype() -- returns compression type ('NONE' for AIFF files) + getcompname() -- returns human-readable version of + compression type ('not compressed' for AIFF files) + getparams() -- returns a tuple consisting of all of the + above in the above order + getmarkers() -- get the list of marks in the audio file or None + if there are no marks + getmark(id) -- get mark with the specified id (raises an error + if the mark does not exist) + readframes(n) -- returns at most n frames of audio + rewind() -- rewind to the beginning of the audio stream + setpos(pos) -- seek to the specified position + tell() -- return the current position + close() -- close the instance (make it unusable) +The position returned by tell(), the position given to setpos() and +the position of marks are all compatible and have nothing to do with +the actual position in the file. +The close() method is called automatically when the class instance +is destroyed. + +Writing AIFF files: + f = aifc.open(file, 'w') +where file is either the name of a file or an open file pointer. +The open file pointer must have methods write(), tell(), seek(), and +close(). + +This returns an instance of a class with the following public methods: + aiff() -- create an AIFF file (AIFF-C default) + aifc() -- create an AIFF-C file + setnchannels(n) -- set the number of channels + setsampwidth(n) -- set the sample width + setframerate(n) -- set the frame rate + setnframes(n) -- set the number of frames + setcomptype(type, name) + -- set the compression type and the + human-readable compression type + setparams(tuple) + -- set all parameters at once + setmark(id, pos, name) + -- add specified mark to the list of marks + tell() -- return current position in output file (useful + in combination with setmark()) + writeframesraw(data) + -- write audio frames without pathing up the + file header + writeframes(data) + -- write audio frames and patch up the file header + close() -- patch up the file header and close the + output file +You should set the parameters before the first writeframesraw or +writeframes. The total number of frames does not need to be set, +but when it is set to the correct value, the header does not have to +be patched up. +It is best to first set all parameters, perhaps possibly the +compression type, and then write audio frames using writeframesraw. +When all frames have been written, either call writeframes('') or +close() to patch up the sizes in the header. +Marks can be added anytime. If there are any marks, you must call +close() after all frames have been written. +The close() method is called automatically when the class instance +is destroyed. + +When a file is opened with the extension '.aiff', an AIFF file is +written, otherwise an AIFF-C file is written. This default can be +changed by calling aiff() or aifc() before the first writeframes or +writeframesraw. \ No newline at end of file diff --git a/test/markdown_files/anydbm.md b/test/markdown_files/anydbm.md new file mode 100644 index 00000000..e13bb0b4 --- /dev/null +++ b/test/markdown_files/anydbm.md @@ -0,0 +1,33 @@ +Generic interface to all dbm clones. + +Instead of +```html + import dbm + d = dbm.open(file, 'w', 0666) +``` +use +```html + import anydbm + d = anydbm.open(file, 'w') +``` +The returned object is a dbhash, gdbm, dbm or dumbdbm object, +dependent on the type of database being opened (determined by whichdb +module) in the case of an existing dbm. If the dbm does not exist and +the create or new flag ('c' or 'n') was specified, the dbm type will +be determined by the availability of the modules (tested in the above +order). + +It has the following interface (key and data are strings): +```html + d[key] = data # store data at key (may override data at + # existing key) + data = d[key] # retrieve data at key (raise KeyError if no + # such key) + del d[key] # delete data stored at key (raises KeyError + # if no such key) + flag = key in d # true if the key exists + list = d.keys() # return a list of all existing keys (slow!) +``` +Future versions may change the order in which implementations are +tested for existence, and add interfaces to other dbm-like +implementations. diff --git a/test/markdown_files/anydbm.pydoc b/test/markdown_files/anydbm.pydoc new file mode 100644 index 00000000..2d46b588 --- /dev/null +++ b/test/markdown_files/anydbm.pydoc @@ -0,0 +1,33 @@ +Generic interface to all dbm clones. + +Instead of + + import dbm + d = dbm.open(file, 'w', 0666) + +use + + import anydbm + d = anydbm.open(file, 'w') + +The returned object is a dbhash, gdbm, dbm or dumbdbm object, +dependent on the type of database being opened (determined by whichdb +module) in the case of an existing dbm. If the dbm does not exist and +the create or new flag ('c' or 'n') was specified, the dbm type will +be determined by the availability of the modules (tested in the above +order). + +It has the following interface (key and data are strings): + + d[key] = data # store data at key (may override data at + # existing key) + data = d[key] # retrieve data at key (raise KeyError if no + # such key) + del d[key] # delete data stored at key (raises KeyError + # if no such key) + flag = key in d # true if the key exists + list = d.keys() # return a list of all existing keys (slow!) + +Future versions may change the order in which implementations are +tested for existence, and add interfaces to other dbm-like +implementations. \ No newline at end of file diff --git a/test/markdown_files/astroid.md b/test/markdown_files/astroid.md new file mode 100644 index 00000000..2e1a5e21 --- /dev/null +++ b/test/markdown_files/astroid.md @@ -0,0 +1,24 @@ +Python Abstract Syntax Tree New Generation + +The aim of this module is to provide a common base representation of +python source code for projects such as pychecker, pyreverse, +pylint... Well, actually the development of this library is essentially +governed by pylint's needs. + +It extends class defined in the python's \_ast module with some +additional methods and attributes. Instance attributes are added by a +builder object, which can either generate extended ast (let's call +them astroid ;) by visiting an existent ast tree or by inspecting living +object. Methods are added by monkey patching ast classes. + +Main modules are: +```html +* nodes and scoped_nodes for more information about methods and + attributes added to different node classes + +* the manager contains a high level object to get astroid trees from + source files and living objects. It maintains a cache of previously + constructed tree for quick access + +* builder contains the class responsible to build astroid trees +``` diff --git a/test/markdown_files/astroid.pydoc b/test/markdown_files/astroid.pydoc new file mode 100644 index 00000000..84d58487 --- /dev/null +++ b/test/markdown_files/astroid.pydoc @@ -0,0 +1,23 @@ +Python Abstract Syntax Tree New Generation + +The aim of this module is to provide a common base representation of +python source code for projects such as pychecker, pyreverse, +pylint... Well, actually the development of this library is essentially +governed by pylint's needs. + +It extends class defined in the python's _ast module with some +additional methods and attributes. Instance attributes are added by a +builder object, which can either generate extended ast (let's call +them astroid ;) by visiting an existent ast tree or by inspecting living +object. Methods are added by monkey patching ast classes. + +Main modules are: + +* nodes and scoped_nodes for more information about methods and + attributes added to different node classes + +* the manager contains a high level object to get astroid trees from + source files and living objects. It maintains a cache of previously + constructed tree for quick access + +* builder contains the class responsible to build astroid trees \ No newline at end of file diff --git a/test/markdown_files/numpy.linspace.md b/test/markdown_files/numpy.linspace.md new file mode 100644 index 00000000..74c8d862 --- /dev/null +++ b/test/markdown_files/numpy.linspace.md @@ -0,0 +1,73 @@ +Return evenly spaced numbers over a specified interval. + +Returns `num` evenly spaced samples, calculated over the +interval [`start`, `stop`]. + +The endpoint of the interval can optionally be excluded. + +#### Parameters + +```html +start : scalar + The starting value of the sequence. +stop : scalar + The end value of the sequence, unless `endpoint` is set to False. + In that case, the sequence consists of all but the last of ``num + 1`` + evenly spaced samples, so that `stop` is excluded. Note that the step + size changes when `endpoint` is False. +num : int, optional + Number of samples to generate. Default is 50. Must be non-negative. +endpoint : bool, optional + If True, `stop` is the last sample. Otherwise, it is not included. + Default is True. +retstep : bool, optional + If True, return (`samples`, `step`), where `step` is the spacing + between samples. +dtype : dtype, optional + The type of the output array. If `dtype` is not given, infer the data + type from the other input arguments. +``` + + .. versionadded:: 1.9.0 + +#### Returns + +samples : ndarray + There are `num` equally spaced samples in the closed interval + ``[start, stop]`` or the half-open interval ``[start, stop)`` + (depending on whether `endpoint` is True or False). +step : float, optional + Only returned if `retstep` is True + + Size of spacing between samples. + + +#### See Also + +arange : Similar to `linspace`, but uses a step size (instead of the + number of samples). +logspace : Samples uniformly distributed in log space. + +#### Examples + +>>> np.linspace(2.0, 3.0, num=5) +array([ 2. , 2.25, 2.5 , 2.75, 3. ]) +>>> np.linspace(2.0, 3.0, num=5, endpoint=False) +array([ 2. , 2.2, 2.4, 2.6, 2.8]) +>>> np.linspace(2.0, 3.0, num=5, retstep=True) +(array([ 2. , 2.25, 2.5 , 2.75, 3. ]), 0.25) + +Graphical illustration: + +>>> import matplotlib.pyplot as plt +>>> N = 8 +>>> y = np.zeros(N) +>>> x1 = np.linspace(0, 10, N, endpoint=True) +>>> x2 = np.linspace(0, 10, N, endpoint=False) +>>> plt.plot(x1, y, 'o') +[] +>>> plt.plot(x2, y + 0.5, 'o') +[] +>>> plt.ylim([-0.5, 1]) +(-0.5, 1) +>>> plt.show() diff --git a/test/markdown_files/numpy.linspace.pydoc b/test/markdown_files/numpy.linspace.pydoc new file mode 100644 index 00000000..5b881d6a --- /dev/null +++ b/test/markdown_files/numpy.linspace.pydoc @@ -0,0 +1,71 @@ +Return evenly spaced numbers over a specified interval. + +Returns `num` evenly spaced samples, calculated over the +interval [`start`, `stop`]. + +The endpoint of the interval can optionally be excluded. + +Parameters +---------- +start : scalar + The starting value of the sequence. +stop : scalar + The end value of the sequence, unless `endpoint` is set to False. + In that case, the sequence consists of all but the last of ``num + 1`` + evenly spaced samples, so that `stop` is excluded. Note that the step + size changes when `endpoint` is False. +num : int, optional + Number of samples to generate. Default is 50. Must be non-negative. +endpoint : bool, optional + If True, `stop` is the last sample. Otherwise, it is not included. + Default is True. +retstep : bool, optional + If True, return (`samples`, `step`), where `step` is the spacing + between samples. +dtype : dtype, optional + The type of the output array. If `dtype` is not given, infer the data + type from the other input arguments. + + .. versionadded:: 1.9.0 + +Returns +------- +samples : ndarray + There are `num` equally spaced samples in the closed interval + ``[start, stop]`` or the half-open interval ``[start, stop)`` + (depending on whether `endpoint` is True or False). +step : float, optional + Only returned if `retstep` is True + + Size of spacing between samples. + + +See Also +-------- +arange : Similar to `linspace`, but uses a step size (instead of the + number of samples). +logspace : Samples uniformly distributed in log space. + +Examples +-------- +>>> np.linspace(2.0, 3.0, num=5) +array([ 2. , 2.25, 2.5 , 2.75, 3. ]) +>>> np.linspace(2.0, 3.0, num=5, endpoint=False) +array([ 2. , 2.2, 2.4, 2.6, 2.8]) +>>> np.linspace(2.0, 3.0, num=5, retstep=True) +(array([ 2. , 2.25, 2.5 , 2.75, 3. ]), 0.25) + +Graphical illustration: + +>>> import matplotlib.pyplot as plt +>>> N = 8 +>>> y = np.zeros(N) +>>> x1 = np.linspace(0, 10, N, endpoint=True) +>>> x2 = np.linspace(0, 10, N, endpoint=False) +>>> plt.plot(x1, y, 'o') +[] +>>> plt.plot(x2, y + 0.5, 'o') +[] +>>> plt.ylim([-0.5, 1]) +(-0.5, 1) +>>> plt.show() diff --git a/test/markdown_files/scipy.md b/test/markdown_files/scipy.md new file mode 100644 index 00000000..dc14a2a8 --- /dev/null +++ b/test/markdown_files/scipy.md @@ -0,0 +1,47 @@ +### SciPy: A scientific computing package for Python + +Documentation is available in the docstrings and +online at https://docs.scipy.org. + +#### Contents +SciPy imports all the functions from the NumPy namespace, and in +addition provides: + +#### Subpackages +Using any of these subpackages requires an explicit import. For example, +`import scipy.cluster`. +```html + cluster --- Vector Quantization / Kmeans + fftpack --- Discrete Fourier Transform algorithms + integrate --- Integration routines + interpolate --- Interpolation Tools + io --- Data input and output + linalg --- Linear algebra routines + linalg.blas --- Wrappers to BLAS library + linalg.lapack --- Wrappers to LAPACK library + misc --- Various utilities that don't have + another home. + ndimage --- n-dimensional image package + odr --- Orthogonal Distance Regression + optimize --- Optimization Tools + signal --- Signal Processing Tools + sparse --- Sparse Matrices + sparse.linalg --- Sparse Linear Algebra + sparse.linalg.dsolve --- Linear Solvers + sparse.linalg.dsolve.umfpack --- :Interface to the UMFPACK library: + Conjugate Gradient Method (LOBPCG) + sparse.linalg.eigen --- Sparse Eigenvalue Solvers + sparse.linalg.eigen.lobpcg --- Locally Optimal Block Preconditioned + Conjugate Gradient Method (LOBPCG) + spatial --- Spatial data structures and algorithms + special --- Special functions + stats --- Statistical Functions +``` +#### Utility tools +```html + test --- Run scipy unittests + show_config --- Show scipy build configuration + show_numpy_config --- Show numpy build configuration + __version__ --- Scipy version string + __numpy_version__ --- Numpy version string +``` diff --git a/test/markdown_files/scipy.pydoc b/test/markdown_files/scipy.pydoc new file mode 100644 index 00000000..293445fb --- /dev/null +++ b/test/markdown_files/scipy.pydoc @@ -0,0 +1,53 @@ +SciPy: A scientific computing package for Python +================================================ + +Documentation is available in the docstrings and +online at https://docs.scipy.org. + +Contents +-------- +SciPy imports all the functions from the NumPy namespace, and in +addition provides: + +Subpackages +----------- +Using any of these subpackages requires an explicit import. For example, +``import scipy.cluster``. + +:: + + cluster --- Vector Quantization / Kmeans + fftpack --- Discrete Fourier Transform algorithms + integrate --- Integration routines + interpolate --- Interpolation Tools + io --- Data input and output + linalg --- Linear algebra routines + linalg.blas --- Wrappers to BLAS library + linalg.lapack --- Wrappers to LAPACK library + misc --- Various utilities that don't have + another home. + ndimage --- n-dimensional image package + odr --- Orthogonal Distance Regression + optimize --- Optimization Tools + signal --- Signal Processing Tools + sparse --- Sparse Matrices + sparse.linalg --- Sparse Linear Algebra + sparse.linalg.dsolve --- Linear Solvers + sparse.linalg.dsolve.umfpack --- :Interface to the UMFPACK library: + Conjugate Gradient Method (LOBPCG) + sparse.linalg.eigen --- Sparse Eigenvalue Solvers + sparse.linalg.eigen.lobpcg --- Locally Optimal Block Preconditioned + Conjugate Gradient Method (LOBPCG) + spatial --- Spatial data structures and algorithms + special --- Special functions + stats --- Statistical Functions + +Utility tools +------------- +:: + + test --- Run scipy unittests + show_config --- Show scipy build configuration + show_numpy_config --- Show numpy build configuration + __version__ --- Scipy version string + __numpy_version__ --- Numpy version string \ No newline at end of file diff --git a/test/markdown_files/scipy.spatial.distance.md b/test/markdown_files/scipy.spatial.distance.md new file mode 100644 index 00000000..93473991 --- /dev/null +++ b/test/markdown_files/scipy.spatial.distance.md @@ -0,0 +1,54 @@ +### Distance computations (module:`scipy.spatial.distance`) + + +#### Function Reference + +Distance matrix computation from a collection of raw observation vectors +stored in a rectangular array. +```html + pdist -- pairwise distances between observation vectors. + cdist -- distances between two collections of observation vectors + squareform -- convert distance matrix to a condensed one and vice versa + directed_hausdorff -- directed Hausdorff distance between arrays +``` +Predicates for checking the validity of distance matrices, both +condensed and redundant. Also contained in this module are functions +for computing the number of observations in a distance matrix. +```html + is_valid_dm -- checks for a valid distance matrix + is_valid_y -- checks for a valid condensed distance matrix + num_obs_dm -- # of observations in a distance matrix + num_obs_y -- # of observations in a condensed distance matrix +``` +Distance functions between two numeric vectors `u` and `v`. Computing +distances over a large collection of vectors is inefficient for these +functions. Use `pdist` for this purpose. +```html + braycurtis -- the Bray-Curtis distance. + canberra -- the Canberra distance. + chebyshev -- the Chebyshev distance. + cityblock -- the Manhattan distance. + correlation -- the Correlation distance. + cosine -- the Cosine distance. + euclidean -- the Euclidean distance. + mahalanobis -- the Mahalanobis distance. + minkowski -- the Minkowski distance. + seuclidean -- the normalized Euclidean distance. + sqeuclidean -- the squared Euclidean distance. + wminkowski -- (deprecated) alias of `minkowski`. +``` +Distance functions between two boolean vectors (representing sets) `u` and +`v`. As in the case of numerical vectors, `pdist` is more efficient for +computing the distances between all pairs. +```html + dice -- the Dice dissimilarity. + hamming -- the Hamming distance. + jaccard -- the Jaccard distance. + kulsinski -- the Kulsinski distance. + rogerstanimoto -- the Rogers-Tanimoto dissimilarity. + russellrao -- the Russell-Rao dissimilarity. + sokalmichener -- the Sokal-Michener dissimilarity. + sokalsneath -- the Sokal-Sneath dissimilarity. + yule -- the Yule dissimilarity. +``` +:func:`hamming` also operates over discrete numerical vectors. diff --git a/test/markdown_files/scipy.spatial.distance.pydoc b/test/markdown_files/scipy.spatial.distance.pydoc new file mode 100644 index 00000000..cfc9b700 --- /dev/null +++ b/test/markdown_files/scipy.spatial.distance.pydoc @@ -0,0 +1,71 @@ + +===================================================== +Distance computations (:mod:`scipy.spatial.distance`) +===================================================== + +.. sectionauthor:: Damian Eads + +Function Reference +------------------ + +Distance matrix computation from a collection of raw observation vectors +stored in a rectangular array. + +.. autosummary:: + :toctree: generated/ + + pdist -- pairwise distances between observation vectors. + cdist -- distances between two collections of observation vectors + squareform -- convert distance matrix to a condensed one and vice versa + directed_hausdorff -- directed Hausdorff distance between arrays + +Predicates for checking the validity of distance matrices, both +condensed and redundant. Also contained in this module are functions +for computing the number of observations in a distance matrix. + +.. autosummary:: + :toctree: generated/ + + is_valid_dm -- checks for a valid distance matrix + is_valid_y -- checks for a valid condensed distance matrix + num_obs_dm -- # of observations in a distance matrix + num_obs_y -- # of observations in a condensed distance matrix + +Distance functions between two numeric vectors ``u`` and ``v``. Computing +distances over a large collection of vectors is inefficient for these +functions. Use ``pdist`` for this purpose. + +.. autosummary:: + :toctree: generated/ + + braycurtis -- the Bray-Curtis distance. + canberra -- the Canberra distance. + chebyshev -- the Chebyshev distance. + cityblock -- the Manhattan distance. + correlation -- the Correlation distance. + cosine -- the Cosine distance. + euclidean -- the Euclidean distance. + mahalanobis -- the Mahalanobis distance. + minkowski -- the Minkowski distance. + seuclidean -- the normalized Euclidean distance. + sqeuclidean -- the squared Euclidean distance. + wminkowski -- (deprecated) alias of `minkowski`. + +Distance functions between two boolean vectors (representing sets) ``u`` and +``v``. As in the case of numerical vectors, ``pdist`` is more efficient for +computing the distances between all pairs. + +.. autosummary:: + :toctree: generated/ + + dice -- the Dice dissimilarity. + hamming -- the Hamming distance. + jaccard -- the Jaccard distance. + kulsinski -- the Kulsinski distance. + rogerstanimoto -- the Rogers-Tanimoto dissimilarity. + russellrao -- the Russell-Rao dissimilarity. + sokalmichener -- the Sokal-Michener dissimilarity. + sokalsneath -- the Sokal-Sneath dissimilarity. + yule -- the Yule dissimilarity. + +:func:`hamming` also operates over discrete numerical vectors. diff --git a/test/markdown_files/scipy.spatial.md b/test/markdown_files/scipy.spatial.md new file mode 100644 index 00000000..26371412 --- /dev/null +++ b/test/markdown_files/scipy.spatial.md @@ -0,0 +1,65 @@ +### Spatial algorithms and data structures (module:`scipy.spatial`) + + +### Nearest-neighbor Queries +```html + KDTree -- class for efficient nearest-neighbor queries + cKDTree -- class for efficient nearest-neighbor queries (faster impl.) + distance -- module containing many different distance measures + Rectangle +``` +### Delaunay Triangulation, Convex Hulls and Voronoi Diagrams +```html + Delaunay -- compute Delaunay triangulation of input points + ConvexHull -- compute a convex hull for input points + Voronoi -- compute a Voronoi diagram hull from input points + SphericalVoronoi -- compute a Voronoi diagram from input points on the surface of a sphere + HalfspaceIntersection -- compute the intersection points of input halfspaces +``` +### Plotting Helpers +```html + delaunay_plot_2d -- plot 2-D triangulation + convex_hull_plot_2d -- plot 2-D convex hull + voronoi_plot_2d -- plot 2-D voronoi diagram +``` +### Simplex representation +The simplices (triangles, tetrahedra, ...) appearing in the Delaunay +tesselation (N-dim simplices), convex hull facets, and Voronoi ridges +(N-1 dim simplices) are represented in the following scheme: +```html + tess = Delaunay(points) + hull = ConvexHull(points) + voro = Voronoi(points) + + # coordinates of the j-th vertex of the i-th simplex + tess.points[tess.simplices[i, j], :] # tesselation element + hull.points[hull.simplices[i, j], :] # convex hull facet + voro.vertices[voro.ridge_vertices[i, j], :] # ridge between Voronoi cells +``` +For Delaunay triangulations and convex hulls, the neighborhood +structure of the simplices satisfies the condition: +```html + `tess.neighbors[i,j]` is the neighboring simplex of the i-th + simplex, opposite to the j-vertex. It is -1 in case of no + neighbor. +``` +Convex hull facets also define a hyperplane equation: +```html + (hull.equations[i,:-1] * coord).sum() + hull.equations[i,-1] == 0 +``` +Similar hyperplane equations for the Delaunay triangulation correspond +to the convex hull facets on the corresponding N+1 dimensional +paraboloid. + +The Delaunay triangulation objects offer a method for locating the +simplex containing a given point, and barycentric coordinate +computations. + +#### Functions +```html + tsearch + distance_matrix + minkowski_distance + minkowski_distance_p + procrustes +``` diff --git a/test/markdown_files/scipy.spatial.pydoc b/test/markdown_files/scipy.spatial.pydoc new file mode 100644 index 00000000..1613b943 --- /dev/null +++ b/test/markdown_files/scipy.spatial.pydoc @@ -0,0 +1,86 @@ +============================================================= +Spatial algorithms and data structures (:mod:`scipy.spatial`) +============================================================= + +.. currentmodule:: scipy.spatial + +Nearest-neighbor Queries +======================== +.. autosummary:: + :toctree: generated/ + + KDTree -- class for efficient nearest-neighbor queries + cKDTree -- class for efficient nearest-neighbor queries (faster impl.) + distance -- module containing many different distance measures + Rectangle + +Delaunay Triangulation, Convex Hulls and Voronoi Diagrams +========================================================= + +.. autosummary:: + :toctree: generated/ + + Delaunay -- compute Delaunay triangulation of input points + ConvexHull -- compute a convex hull for input points + Voronoi -- compute a Voronoi diagram hull from input points + SphericalVoronoi -- compute a Voronoi diagram from input points on the surface of a sphere + HalfspaceIntersection -- compute the intersection points of input halfspaces + +Plotting Helpers +================ + +.. autosummary:: + :toctree: generated/ + + delaunay_plot_2d -- plot 2-D triangulation + convex_hull_plot_2d -- plot 2-D convex hull + voronoi_plot_2d -- plot 2-D voronoi diagram + +.. seealso:: :ref:`Tutorial ` + + +Simplex representation +====================== +The simplices (triangles, tetrahedra, ...) appearing in the Delaunay +tesselation (N-dim simplices), convex hull facets, and Voronoi ridges +(N-1 dim simplices) are represented in the following scheme:: + + tess = Delaunay(points) + hull = ConvexHull(points) + voro = Voronoi(points) + + # coordinates of the j-th vertex of the i-th simplex + tess.points[tess.simplices[i, j], :] # tesselation element + hull.points[hull.simplices[i, j], :] # convex hull facet + voro.vertices[voro.ridge_vertices[i, j], :] # ridge between Voronoi cells + +For Delaunay triangulations and convex hulls, the neighborhood +structure of the simplices satisfies the condition: + + ``tess.neighbors[i,j]`` is the neighboring simplex of the i-th + simplex, opposite to the j-vertex. It is -1 in case of no + neighbor. + +Convex hull facets also define a hyperplane equation:: + + (hull.equations[i,:-1] * coord).sum() + hull.equations[i,-1] == 0 + +Similar hyperplane equations for the Delaunay triangulation correspond +to the convex hull facets on the corresponding N+1 dimensional +paraboloid. + +The Delaunay triangulation objects offer a method for locating the +simplex containing a given point, and barycentric coordinate +computations. + +Functions +--------- + +.. autosummary:: + :toctree: generated/ + + tsearch + distance_matrix + minkowski_distance + minkowski_distance_p + procrustes \ No newline at end of file diff --git a/test/test_markdown.py b/test/test_markdown.py new file mode 100644 index 00000000..0a62ba6c --- /dev/null +++ b/test/test_markdown.py @@ -0,0 +1,23 @@ +# Copyright 2017 Palantir Technologies, Inc. +import os +import pytest +from pyls import markdown + +from . import markdown_files + +MD_FILES_DIR = os.path.dirname(markdown_files.__file__) +FILES = [os.path.splitext(filename)[0] for filename in os.listdir(MD_FILES_DIR) if filename.endswith(".pydoc")] + + +@pytest.mark.parametrize('basename', FILES) +def test_rst2markdown(basename): + md_file = os.path.join(MD_FILES_DIR, basename + ".md") + pydoc_file = os.path.join(MD_FILES_DIR, basename + ".pydoc") + + expected = open(md_file).read().rstrip('\n') + actual = markdown.rst2markdown(open(pydoc_file).read()) + + if basename == "numpy.linspace": + with open(md_file + ".test.md", 'w+') as f: + f.write(actual) + assert actual == expected diff --git a/tox.ini b/tox.ini index e8e9617a..034f604f 100644 --- a/tox.ini +++ b/tox.ini @@ -11,11 +11,11 @@ ignore = E226, E722, W504 max-line-length = 120 exclude = test/plugins/.ropeproject,test/.ropeproject -[pytest] -testpaths = test -addopts = - --cov-report html --cov-report term --junitxml=pytest.xml - --cov pyls --cov test +#[pytest] +#testpaths = test +#addopts = +# --cov-report html --cov-report term --junitxml=pytest.xml +# --cov pyls --cov test [testenv] commands = From 29d5cf474fe28b31ebfde1a1ee32de888518d2d8 Mon Sep 17 00:00:00 2001 From: Nicholas Gates Date: Thu, 10 May 2018 18:14:07 +0100 Subject: [PATCH 2/7] Markdown --- test/markdown_files/numpy.linspace.md | 73 ------------------------ test/markdown_files/numpy.linspace.pydoc | 71 ----------------------- 2 files changed, 144 deletions(-) delete mode 100644 test/markdown_files/numpy.linspace.md delete mode 100644 test/markdown_files/numpy.linspace.pydoc diff --git a/test/markdown_files/numpy.linspace.md b/test/markdown_files/numpy.linspace.md deleted file mode 100644 index 74c8d862..00000000 --- a/test/markdown_files/numpy.linspace.md +++ /dev/null @@ -1,73 +0,0 @@ -Return evenly spaced numbers over a specified interval. - -Returns `num` evenly spaced samples, calculated over the -interval [`start`, `stop`]. - -The endpoint of the interval can optionally be excluded. - -#### Parameters - -```html -start : scalar - The starting value of the sequence. -stop : scalar - The end value of the sequence, unless `endpoint` is set to False. - In that case, the sequence consists of all but the last of ``num + 1`` - evenly spaced samples, so that `stop` is excluded. Note that the step - size changes when `endpoint` is False. -num : int, optional - Number of samples to generate. Default is 50. Must be non-negative. -endpoint : bool, optional - If True, `stop` is the last sample. Otherwise, it is not included. - Default is True. -retstep : bool, optional - If True, return (`samples`, `step`), where `step` is the spacing - between samples. -dtype : dtype, optional - The type of the output array. If `dtype` is not given, infer the data - type from the other input arguments. -``` - - .. versionadded:: 1.9.0 - -#### Returns - -samples : ndarray - There are `num` equally spaced samples in the closed interval - ``[start, stop]`` or the half-open interval ``[start, stop)`` - (depending on whether `endpoint` is True or False). -step : float, optional - Only returned if `retstep` is True - - Size of spacing between samples. - - -#### See Also - -arange : Similar to `linspace`, but uses a step size (instead of the - number of samples). -logspace : Samples uniformly distributed in log space. - -#### Examples - ->>> np.linspace(2.0, 3.0, num=5) -array([ 2. , 2.25, 2.5 , 2.75, 3. ]) ->>> np.linspace(2.0, 3.0, num=5, endpoint=False) -array([ 2. , 2.2, 2.4, 2.6, 2.8]) ->>> np.linspace(2.0, 3.0, num=5, retstep=True) -(array([ 2. , 2.25, 2.5 , 2.75, 3. ]), 0.25) - -Graphical illustration: - ->>> import matplotlib.pyplot as plt ->>> N = 8 ->>> y = np.zeros(N) ->>> x1 = np.linspace(0, 10, N, endpoint=True) ->>> x2 = np.linspace(0, 10, N, endpoint=False) ->>> plt.plot(x1, y, 'o') -[] ->>> plt.plot(x2, y + 0.5, 'o') -[] ->>> plt.ylim([-0.5, 1]) -(-0.5, 1) ->>> plt.show() diff --git a/test/markdown_files/numpy.linspace.pydoc b/test/markdown_files/numpy.linspace.pydoc deleted file mode 100644 index 5b881d6a..00000000 --- a/test/markdown_files/numpy.linspace.pydoc +++ /dev/null @@ -1,71 +0,0 @@ -Return evenly spaced numbers over a specified interval. - -Returns `num` evenly spaced samples, calculated over the -interval [`start`, `stop`]. - -The endpoint of the interval can optionally be excluded. - -Parameters ----------- -start : scalar - The starting value of the sequence. -stop : scalar - The end value of the sequence, unless `endpoint` is set to False. - In that case, the sequence consists of all but the last of ``num + 1`` - evenly spaced samples, so that `stop` is excluded. Note that the step - size changes when `endpoint` is False. -num : int, optional - Number of samples to generate. Default is 50. Must be non-negative. -endpoint : bool, optional - If True, `stop` is the last sample. Otherwise, it is not included. - Default is True. -retstep : bool, optional - If True, return (`samples`, `step`), where `step` is the spacing - between samples. -dtype : dtype, optional - The type of the output array. If `dtype` is not given, infer the data - type from the other input arguments. - - .. versionadded:: 1.9.0 - -Returns -------- -samples : ndarray - There are `num` equally spaced samples in the closed interval - ``[start, stop]`` or the half-open interval ``[start, stop)`` - (depending on whether `endpoint` is True or False). -step : float, optional - Only returned if `retstep` is True - - Size of spacing between samples. - - -See Also --------- -arange : Similar to `linspace`, but uses a step size (instead of the - number of samples). -logspace : Samples uniformly distributed in log space. - -Examples --------- ->>> np.linspace(2.0, 3.0, num=5) -array([ 2. , 2.25, 2.5 , 2.75, 3. ]) ->>> np.linspace(2.0, 3.0, num=5, endpoint=False) -array([ 2. , 2.2, 2.4, 2.6, 2.8]) ->>> np.linspace(2.0, 3.0, num=5, retstep=True) -(array([ 2. , 2.25, 2.5 , 2.75, 3. ]), 0.25) - -Graphical illustration: - ->>> import matplotlib.pyplot as plt ->>> N = 8 ->>> y = np.zeros(N) ->>> x1 = np.linspace(0, 10, N, endpoint=True) ->>> x2 = np.linspace(0, 10, N, endpoint=False) ->>> plt.plot(x1, y, 'o') -[] ->>> plt.plot(x2, y + 0.5, 'o') -[] ->>> plt.ylim([-0.5, 1]) -(-0.5, 1) ->>> plt.show() From 36141e0f61a3f08385976342757c82786f6bec5b Mon Sep 17 00:00:00 2001 From: Nicholas Gates Date: Thu, 10 May 2018 18:41:19 +0100 Subject: [PATCH 3/7] Fix doctest --- pyls/_utils.py | 3 +- pyls/markdown.py | 14 +++- test/markdown_files/numpy.logspace.md | 87 ++++++++++++++++++++++++ test/markdown_files/numpy.logspace.pydoc | 74 ++++++++++++++++++++ test/test_markdown.py | 4 -- 5 files changed, 173 insertions(+), 9 deletions(-) create mode 100644 test/markdown_files/numpy.logspace.md create mode 100644 test/markdown_files/numpy.logspace.pydoc diff --git a/pyls/_utils.py b/pyls/_utils.py index b45c2329..0ab1e3ab 100644 --- a/pyls/_utils.py +++ b/pyls/_utils.py @@ -100,8 +100,7 @@ def _merge_dicts_(a, b): def format_docstring(contents): """Python doc strings come in a number of formats, but LSP wants markdown.""" - return contents - #return rst2markdown(contents) + return rst2markdown(contents) def clip_column(column, lines, line_number): diff --git a/pyls/markdown.py b/pyls/markdown.py index c2240621..9a860540 100644 --- a/pyls/markdown.py +++ b/pyls/markdown.py @@ -48,6 +48,7 @@ def convert(self, docstring): i += 1 self._end_code() + self._end_doctest() self._end_preformatted() return '\n'.join(self._md).strip() @@ -59,7 +60,7 @@ def _default(self, lines, i): self._start_code() return 0 - if line.startswith('>>>'): + if line.startswith('>>>') or line.startswith('...'): self._start_doctest() return -1 @@ -126,7 +127,7 @@ def _code(self, line): def _doctest(self, line): if not line: - self._end_code() + self._end_doctest() else: self._md.append(line) @@ -175,7 +176,8 @@ def _double_colon(self, line): return True def _start_doctest(self): - self._start_code() + self._try_remove_preceeding_empty_lines() + self._md.append('```pydocstring') self._state = _STATE_DOCTEST def _start_code(self): @@ -190,6 +192,12 @@ def _end_code(self): self._md.append('```') self._state = _STATE_DEFAULT + def _end_doctest(self): + if self._state == _STATE_DOCTEST: + self._try_remove_preceeding_empty_lines() + self._md.append('```') + self._state = _STATE_DEFAULT + def _start_preformatted(self): # Remove previous empty line so we avoid double empties self._try_remove_preceeding_empty_lines() diff --git a/test/markdown_files/numpy.logspace.md b/test/markdown_files/numpy.logspace.md new file mode 100644 index 00000000..19c9f576 --- /dev/null +++ b/test/markdown_files/numpy.logspace.md @@ -0,0 +1,87 @@ +Return numbers spaced evenly on a log scale. + +In linear space, the sequence starts at `base \*\* start` +(`base` to the power of `start`) and ends with `base \*\* stop` +(see `endpoint` below). + +#### Parameters +start : float +```html + `base ** start` is the starting value of the sequence. +``` +stop : float +```html + `base ** stop` is the final value of the sequence, unless `endpoint` + is False. In that case, `num + 1` values are spaced over the + interval in log-space, of which all but the last (a sequence of + length `num`) are returned. +``` +num : integer, optional +```html + Number of samples to generate. Default is 50. +``` +endpoint : boolean, optional +```html + If true, `stop` is the last sample. Otherwise, it is not included. + Default is True. +``` +base : float, optional +```html + The base of the log space. The step size between the elements in + `ln(samples) / ln(base)` (or `log_base(samples)`) is uniform. + Default is 10.0. +``` +dtype : dtype +```html + The type of the output array. If `dtype` is not given, infer the data + type from the other input arguments. +``` +#### Returns +samples : ndarray +```html + `num` samples, equally spaced on a log scale. +``` +#### See Also +arange : Similar to linspace, with the step size specified instead of the +```html + number of samples. Note that, when used with a float endpoint, the + endpoint may or may not be included. +``` +linspace : Similar to logspace, but with the samples uniformly distributed +```html + in linear space, instead of log space. +``` +geomspace : Similar to logspace, but with endpoints specified directly. + +#### Notes +Logspace is equivalent to the code +```pydocstring +>>> y = np.linspace(start, stop, num=num, endpoint=endpoint) +... # doctest: +SKIP +>>> power(base, y).astype(dtype) +... # doctest: +SKIP +``` +#### Examples +```pydocstring +>>> np.logspace(2.0, 3.0, num=4) +array([ 100. , 215.443469 , 464.15888336, 1000. ]) +>>> np.logspace(2.0, 3.0, num=4, endpoint=False) +array([ 100. , 177.827941 , 316.22776602, 562.34132519]) +>>> np.logspace(2.0, 3.0, num=4, base=2.0) +array([ 4. , 5.0396842 , 6.34960421, 8. ]) +``` +Graphical illustration: +```pydocstring +>>> import matplotlib.pyplot as plt +>>> N = 10 +>>> x1 = np.logspace(0.1, 1, N, endpoint=True) +>>> x2 = np.logspace(0.1, 1, N, endpoint=False) +>>> y = np.zeros(N) +>>> plt.plot(x1, y, 'o') +[] +>>> plt.plot(x2, y + 0.5, 'o') +[] +>>> plt.ylim([-0.5, 1]) +(-0.5, 1) +>>> plt.show() +``` diff --git a/test/markdown_files/numpy.logspace.pydoc b/test/markdown_files/numpy.logspace.pydoc new file mode 100644 index 00000000..b608fd9c --- /dev/null +++ b/test/markdown_files/numpy.logspace.pydoc @@ -0,0 +1,74 @@ +Return numbers spaced evenly on a log scale. + +In linear space, the sequence starts at ``base ** start`` +(`base` to the power of `start`) and ends with ``base ** stop`` +(see `endpoint` below). + +Parameters +---------- +start : float + ``base ** start`` is the starting value of the sequence. +stop : float + ``base ** stop`` is the final value of the sequence, unless `endpoint` + is False. In that case, ``num + 1`` values are spaced over the + interval in log-space, of which all but the last (a sequence of + length `num`) are returned. +num : integer, optional + Number of samples to generate. Default is 50. +endpoint : boolean, optional + If true, `stop` is the last sample. Otherwise, it is not included. + Default is True. +base : float, optional + The base of the log space. The step size between the elements in + ``ln(samples) / ln(base)`` (or ``log_base(samples)``) is uniform. + Default is 10.0. +dtype : dtype + The type of the output array. If `dtype` is not given, infer the data + type from the other input arguments. + +Returns +------- +samples : ndarray + `num` samples, equally spaced on a log scale. + +See Also +-------- +arange : Similar to linspace, with the step size specified instead of the + number of samples. Note that, when used with a float endpoint, the + endpoint may or may not be included. +linspace : Similar to logspace, but with the samples uniformly distributed + in linear space, instead of log space. +geomspace : Similar to logspace, but with endpoints specified directly. + +Notes +----- +Logspace is equivalent to the code + +>>> y = np.linspace(start, stop, num=num, endpoint=endpoint) +... # doctest: +SKIP +>>> power(base, y).astype(dtype) +... # doctest: +SKIP + +Examples +-------- +>>> np.logspace(2.0, 3.0, num=4) +array([ 100. , 215.443469 , 464.15888336, 1000. ]) +>>> np.logspace(2.0, 3.0, num=4, endpoint=False) +array([ 100. , 177.827941 , 316.22776602, 562.34132519]) +>>> np.logspace(2.0, 3.0, num=4, base=2.0) +array([ 4. , 5.0396842 , 6.34960421, 8. ]) + +Graphical illustration: + +>>> import matplotlib.pyplot as plt +>>> N = 10 +>>> x1 = np.logspace(0.1, 1, N, endpoint=True) +>>> x2 = np.logspace(0.1, 1, N, endpoint=False) +>>> y = np.zeros(N) +>>> plt.plot(x1, y, 'o') +[] +>>> plt.plot(x2, y + 0.5, 'o') +[] +>>> plt.ylim([-0.5, 1]) +(-0.5, 1) +>>> plt.show() diff --git a/test/test_markdown.py b/test/test_markdown.py index 0a62ba6c..e50f3007 100644 --- a/test/test_markdown.py +++ b/test/test_markdown.py @@ -16,8 +16,4 @@ def test_rst2markdown(basename): expected = open(md_file).read().rstrip('\n') actual = markdown.rst2markdown(open(pydoc_file).read()) - - if basename == "numpy.linspace": - with open(md_file + ".test.md", 'w+') as f: - f.write(actual) assert actual == expected From 110d1e02b00a5fe18904ccc449bbdc90e062e823 Mon Sep 17 00:00:00 2001 From: Nicholas Gates Date: Thu, 10 May 2018 18:42:02 +0100 Subject: [PATCH 4/7] Fix doctest --- tox.ini | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tox.ini b/tox.ini index 034f604f..e8e9617a 100644 --- a/tox.ini +++ b/tox.ini @@ -11,11 +11,11 @@ ignore = E226, E722, W504 max-line-length = 120 exclude = test/plugins/.ropeproject,test/.ropeproject -#[pytest] -#testpaths = test -#addopts = -# --cov-report html --cov-report term --junitxml=pytest.xml -# --cov pyls --cov test +[pytest] +testpaths = test +addopts = + --cov-report html --cov-report term --junitxml=pytest.xml + --cov pyls --cov test [testenv] commands = From 2d89cdc01f41adbe85c465d9fc6fab243abecf97 Mon Sep 17 00:00:00 2001 From: Nicholas Gates Date: Thu, 10 May 2018 19:39:08 +0100 Subject: [PATCH 5/7] Update jedi_completion.py --- pyls/plugins/jedi_completion.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/pyls/plugins/jedi_completion.py b/pyls/plugins/jedi_completion.py index 65d7a141..e6c5bf80 100644 --- a/pyls/plugins/jedi_completion.py +++ b/pyls/plugins/jedi_completion.py @@ -1,13 +1,10 @@ # Copyright 2017 Palantir Technologies, Inc. import logging from pyls import hookimpl, lsp, _utils -import numpy log = logging.getLogger(__name__) -numpy.linspace - @hookimpl def pyls_completions(document, position): definitions = document.jedi_script(position).completions() From 25201f6770253a3975224fef7715a0fe5dfe746c Mon Sep 17 00:00:00 2001 From: Nicholas Gates Date: Thu, 10 May 2018 19:39:41 +0100 Subject: [PATCH 6/7] Update tox.ini --- tox.ini | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tox.ini b/tox.ini index 034f604f..e8e9617a 100644 --- a/tox.ini +++ b/tox.ini @@ -11,11 +11,11 @@ ignore = E226, E722, W504 max-line-length = 120 exclude = test/plugins/.ropeproject,test/.ropeproject -#[pytest] -#testpaths = test -#addopts = -# --cov-report html --cov-report term --junitxml=pytest.xml -# --cov pyls --cov test +[pytest] +testpaths = test +addopts = + --cov-report html --cov-report term --junitxml=pytest.xml + --cov pyls --cov test [testenv] commands = From 0ccabd5d1b396b5d177f680512f6a6e94c5e4549 Mon Sep 17 00:00:00 2001 From: Nicholas Gates Date: Thu, 17 May 2018 12:55:39 +0100 Subject: [PATCH 7/7] markup content --- pyls/_utils.py | 11 +++++++++-- pyls/plugins/hover.py | 8 ++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/pyls/_utils.py b/pyls/_utils.py index 0ab1e3ab..83ca4a7c 100644 --- a/pyls/_utils.py +++ b/pyls/_utils.py @@ -5,6 +5,7 @@ import os import threading +from . import lsp from .markdown import rst2markdown log = logging.getLogger(__name__) @@ -99,8 +100,14 @@ def _merge_dicts_(a, b): def format_docstring(contents): - """Python doc strings come in a number of formats, but LSP wants markdown.""" - return rst2markdown(contents) + """Python doc strings come in a number of formats, but LSP wants markdown. + + Returns: MarkupContent + """ + return { + 'kind': lsp.MarkupKind.Markdown, + 'value': rst2markdown(contents) + } def clip_column(column, lines, line_number): diff --git a/pyls/plugins/hover.py b/pyls/plugins/hover.py index 37371341..9e859268 100644 --- a/pyls/plugins/hover.py +++ b/pyls/plugins/hover.py @@ -1,6 +1,6 @@ # Copyright 2017 Palantir Technologies, Inc. import logging -from pyls import hookimpl, lsp, _utils +from pyls import hookimpl, _utils log = logging.getLogger(__name__) @@ -16,8 +16,4 @@ def pyls_hover(document, position): if not definitions: return None - md_docstring = _utils.format_docstring(definitions[0].docstring()) - return {'contents': { - 'type': lsp.MarkupKind.Markdown, - 'value': md_docstring - }} if md_docstring else None + return {'contents': _utils.format_docstring(definitions[0].docstring())}