Revision: 6713f7c7c6da
Author: Pekka Klärck
Date: Mon Feb 20 00:26:57 2012
Log: libdoc: Initial implementation to show library information on
console.
This implementation supports following usages:
robot.libdoc MyLib show [keywords]
robot.libdoc MyLib list [keywords]
robot.libdoc MyLib search [patterns]
robot.libdoc MyLib version
Todo:
- Discuss usage. I'm not sure are list and search needed separately.
- Code review.
- Add some more tests.
- Documentation.
http://code.google.com/p/robotframework/source/detail?r=6713f7c7c6da
Added:
/atest/robot/libdoc/console_viewer.txt
/src/robot/libdocpkg/consoleviewer.py
Modified:
/atest/robot/libdoc/LibDocLib.py
/atest/robot/libdoc/invalid_usage.txt
/src/robot/libdoc.py
/src/robot/libdocpkg/__init__.py
=======================================
--- /dev/null
+++ /atest/robot/libdoc/console_viewer.txt Mon Feb 20 00:26:57 2012
@@ -0,0 +1,51 @@
+*** Settings ***
+Force Tags regression pybot jybot
+Resource libdoc_resource.txt
+
+*** Test Cases ***
+List all keywords
+ Run Libdoc And Verify Output Dialogs list
+ ... Execute Manual Step
+ ... Get Selection From User
+ ... Get Value From User
+ ... Pause Execution
+
+List some keywords
+ Run Libdoc And Verify Output ${TESTDATADIR}/resource.txt LIST KW?
CURDIR
+ ... curdir
+ ... kw 3
+ ... kw 4
+ ... kw 5
+
+Show whole library
+ ${output}= Run Libdoc Dialogs show
+ Should Start With ${output} Dialogs\n=======\nVersion:
+ Should Contain ${output} Execute Manual Step\n----
+ Should Contain ${output} Get Selection From User\n----
+ Should Contain ${output} Get Value From User\n----
+ Should Contain ${output} Pause Execution\n----
+
+Show intro only
+ ${output}= Run Libdoc Dialogs SHOW intro
+ Should Start With ${output} Dialogs\n=======\nVersion:
+ Should Not Contain ${output} Execute Manual Step\n----
+ Should Not Contain ${output} Get Selection From User\n----
+ Should Not Contain ${output} Get Value From User\n----
+ Should Not Contain ${output} Pause Execution\n----
+
+Show intro and keywords
+ ${output}= Run Libdoc ${TESTDATADIR}/resource.txt SHOW NONASC*
INTRO
+ Should Start With ${output} resource\n========\nNamed arguments:
+ ${expected} = Catenate SEPARATOR=\n
+ ... non ascii doc
+ ... -------------
+ ... Arguments:${SPACE * 2}[]
+ ... ${EMPTY}
+ ... Hyvää yötä.
+ ... ${EMPTY}
+ ... Спасибо!
+ Should Contain ${output} ${expected}
+
+Show version
+ Run Libdoc And Verify Output ${TESTDATADIR}/module.py version
0.1-alpha
+ Run Libdoc And Verify Output ${TESTDATADIR}/resource.txt version
N/A
=======================================
--- /dev/null
+++ /src/robot/libdocpkg/consoleviewer.py Mon Feb 20 00:26:57 2012
@@ -0,0 +1,118 @@
+#!/usr/bin/env python
+
+# Copyright 2008-2012 Nokia Siemens Networks Oyj
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import textwrap
+
+from robot import utils
+from robot.errors import DataError
+
+
+class ConsoleViewer(object):
+
+ def __init__(self, libdoc):
+ self._libdoc = libdoc
+
+ @classmethod
+ def handles(cls, method):
+ return hasattr(cls, method.lower())
+
+ def view(self, method, *args):
+ try:
+ getattr(self, method.lower())(*args)
+ except AttributeError:
+ raise DataError("Cannot view '%s'." % method)
+ except TypeError:
+ raise DataError("Wrong number of arguments to view '%s'." %
method)
+
+ def list(self, *included):
+ for kw in KeywordMatcher(self._libdoc).list(included):
+ self._console(kw.name)
+
+ def show(self, *included):
+ if not included or any(utils.eq('intro', inc) for inc in included):
+ self._show_intro(self._libdoc)
+ if self._libdoc.inits:
+ self._show_inits(self._libdoc)
+ for kw in KeywordMatcher(self._libdoc).list(included):
+ self._show_keyword(kw)
+
+ def search(self, *patterns):
+ for kw in KeywordMatcher(self._libdoc).search(patterns):
+ self._console(kw.name)
+
+ def version(self):
+ self._console(self._libdoc.version or 'N/A')
+
+ def _console(self, msg):
+ print utils.encode_output(msg)
+
+ def _show_intro(self, lib):
+ self._header(lib.name, underline='=')
+ named_args = 'supported' if lib.named_args else 'not supported'
+ self._data([('Version', lib.version), ('Scope', lib.scope),
+ ('Named arguments', named_args)])
+ self._doc(lib.doc)
+
+ def _show_inits(self, lib):
+ self._header('Importing', underline='-')
+ for init in lib.inits:
+ self._show_keyword(init, show_name=False)
+
+ def _show_keyword(self, kw, show_name=True):
+ if show_name:
+ self._header(kw.name, underline='-')
+ self._data([('Arguments', '[%s]' % ', '.join(kw.args))])
+ self._doc(kw.doc)
+
+ def _header(self, name, underline):
+ self._console('%s\n%s' % (name, underline * len(name)))
+
+ def _data(self, items):
+ ljust = max(len(name) for name, _ in items) + 3
+ for name, value in items:
+ if value:
+ text = '%s%s' % ((name+':').ljust(ljust), value)
+ self._console(self._wrap(text,
subsequent_indent=' '*ljust))
+
+ def _doc(self, doc):
+ self._console('')
+ for line in doc.splitlines():
+ self._console(self._wrap(line))
+ if doc:
+ self._console('')
+
+ def _wrap(self, text, width=78, **config):
+ return '\n'.join(textwrap.wrap(text, width=width, **config))
+
+
+class KeywordMatcher(object):
+
+ def __init__(self, libdoc):
+ self._keywords = libdoc.keywords
+
+ def list(self, patterns):
+ for kw in self._keywords:
+ if not patterns or self._matches(kw.name, patterns):
+ yield kw
+
+ def _matches(self, item, patterns):
+ return any(utils.matches(item, p) for p in patterns)
+
+ def search(self, patterns):
+ patterns = ['*%s*' % p for p in patterns]
+ for kw in self._keywords:
+ if self._matches(kw.name, patterns) or self._matches(kw.doc,
patterns):
+ yield kw
=======================================
--- /atest/robot/libdoc/LibDocLib.py Thu Feb 16 03:42:26 2012
+++ /atest/robot/libdoc/LibDocLib.py Mon Feb 20 00:26:57 2012
@@ -4,6 +4,7 @@
from subprocess import call, STDOUT
from robot.api import logger
+from robot.utils import decode_output
ROBOT_SRC = join(dirname(abspath(__file__)), '..', '..', '..', 'src')
@@ -25,4 +26,4 @@
stdout.seek(0)
output = stdout.read().replace('\r\n', '\n')
logger.info(output)
- return output
+ return decode_output(output)
=======================================
--- /atest/robot/libdoc/invalid_usage.txt Thu Feb 16 03:42:26 2012
+++ /atest/robot/libdoc/invalid_usage.txt Mon Feb 20 00:26:57 2012
@@ -10,22 +10,26 @@
*** Test Cases ***
No arguments
- ${EMPTY} Expected 2 arguments, got 0.
-
-Too many arguments
- arg1 arg2 arg3 Expected 2 arguments, got 3.
+ ${EMPTY} Expected at least 2 arguments, got 0.
+
+Too many arguments when creating output
+ MyLib out.xml extra Only two arguments allowed when writing
output.
+
+Too many arguments with version
+ Dialogs version extra Wrong number of arguments to view 'version'.
Invalid option
--invalid option --invalid not recognized
Invalid format
- --format XXX BuiltIn ${OUTHTML} Format must be either 'HTML'
or 'XML', got 'XXX'.
+ -f XXX BuiltIn out.html Format must be either 'HTML' or 'XML',
got 'XXX'.
+ BuiltIn out.ext Format must be either 'HTML' or 'XML',
got 'EXT'.
Non-existing library
- NonExistingLib ${OUTHTML} Importing test library 'NonExistingLib'
failed: *
+ NonExistingLib out.html Importing test library 'NonExistingLib'
failed: *
Invalid resource
- ${CURDIR}/invalid_usage.txt ${OUTHTML}
+ ${CURDIR}/invalid_usage.txt out.html
... [ ERROR ] *: Non-existing setting 'Force Tags'.
... [ ERROR ] *: Non-existing setting 'Test Template'.
... Resource file '*' contains a test case table which is not
allowed.
=======================================
--- /src/robot/libdoc.py Thu Feb 16 05:37:32 2012
+++ /src/robot/libdoc.py Mon Feb 20 00:26:57 2012
@@ -72,22 +72,34 @@
import pythonpathsetter # running libdoc.py as script
from robot.utils import Application
-from robot.libdocpkg import LibraryDocumentation
+from robot.errors import DataError
+from robot.libdocpkg import LibraryDocumentation, ConsoleViewer
class LibDoc(Application):
def __init__(self):
- Application.__init__(self, USAGE, arg_limits=2, auto_version=False)
+ Application.__init__(self, USAGE, arg_limits=(2,),
auto_version=False)
+
+ def validate(self, options, arguments):
+ if len(arguments) > 2 and not ConsoleViewer.handles(arguments[1]):
+ raise DataError('Only two arguments allowed when writing
output.')
+ return options, arguments
def main(self, args, argument=None, name='', version='', format=None):
- lib_or_res, outfile = args
+ lib_or_res, output = args[:2]
libdoc = LibraryDocumentation(lib_or_res, argument, name, version)
- libdoc.save(outfile, self._get_format(format, outfile))
- self.console(os.path.abspath(outfile))
+ if ConsoleViewer.handles(output):
+ ConsoleViewer(libdoc).view(output, *args[2:])
+ else:
+ libdoc.save(output, self._get_format(format, output))
+ self.console(os.path.abspath(output))
def _get_format(self, format, output):
- return format if format else os.path.splitext(output)[1][1:]
+ format = (format if format else
os.path.splitext(output)[1][1:]).upper()
+ if format in ['HTML', 'XML']:
+ return format
+ raise DataError("Format must be either 'HTML' or 'XML',
got '%s'." % format)
def libdoc_cli(args):
=======================================
--- /src/robot/libdocpkg/__init__.py Tue Feb 14 05:38:50 2012
+++ /src/robot/libdocpkg/__init__.py Mon Feb 20 00:26:57 2012
@@ -13,6 +13,7 @@
# limitations under the License.
from .builder import DocumentationBuilder
+from .consoleviewer import ConsoleViewer
def LibraryDocumentation(library_or_resource, arguments=None, name=None,