Revision: 4272
Author: pekka.klarck
Date: Wed Oct 27 05:52:27 2010
Log: if __all__ is used with module libs, create keywords only from functions in it (issue 678)
http://code.google.com/p/robotframework/source/detail?r=4272

Modified:
 /trunk/atest/robot/test_libraries/module_library.txt
 /trunk/atest/testdata/test_libraries/module_library.txt
 /trunk/atest/testresources/testlibs/module_library.py
 /trunk/doc/userguide/src/ExtendingRobotFramework/CreatingTestLibraries.txt
 /trunk/src/robot/running/testlibraries.py

=======================================
--- /trunk/atest/robot/test_libraries/module_library.txt Mon Apr 12 05:17:10 2010 +++ /trunk/atest/robot/test_libraries/module_library.txt Wed Oct 27 05:52:27 2010
@@ -5,53 +5,70 @@

 *** Test Cases ***
 Passing
-    Check Test Case  Passing
+    Check Test Case  ${TESTNAME}

 Failing
-    Check Test Case  Failing
+    Check Test Case  ${TESTNAME}

 Logging
-    ${test} =  Check Test Case  Logging
+    ${test} =  Check Test Case  ${TESTNAME}
     Check Log Message  ${test.kws[0].msgs[0]}  Hello from module library
     Check Log Message  ${test.kws[0].msgs[1]}  WARNING!  WARN

 Returning
-    Check Test Case  Returning
+    Check Test Case  ${TESTNAME}

 One Argument
-    Check Test Case  One Argument
+    Check Test Case  ${TESTNAME}

 Many Arguments
-    Check Test Case  Many arguments
+    Check Test Case  ${TESTNAME}

 Default Arguments
-    Check Test Case  Default Arguments
+    Check Test Case  ${TESTNAME}

 Variable Arguments
-    Check Test Case  Variable Arguments
+    Check Test Case  ${TESTNAME}

 Only Methods And Functions Are Keywords
-    Check Test Case  Only methods and functions are Keywords
+    Check Test Case  ${TESTNAME}

 Class Methods In Module Library Are Not Keywords
-    Check Test Case  Class Methods in Module Library are Not Keywords
+    Check Test Case  ${TESTNAME}
+
+Functions starting with underscore are not keywords
+    Check Test Case  ${TESTNAME}
+
+If __all__ is present, only functions listed there are available
+    Check Test Case  ${TESTNAME} 1
+    Check Test Case  ${TESTNAME} 2
+    Check Test Case  ${TESTNAME} 3
+    Check Test Case  ${TESTNAME} 4
+    Keyword should not have been added  join
+    Keyword should not have been added  not_in_all

 Class Method Assigned To Module Variable
-    Check Test Case  Class method assigned to Module variable
+    Check Test Case  ${TESTNAME}

 Lambda Keyword
-    Check Test Case  Lambda Keyword
+    Check Test Case  ${TESTNAME}

 Lambda Keyword With Arguments
-    Check Test Case  Lambda Keyword With Arguments
+    Check Test Case  ${TESTNAME}

 Attribute With Same Name As Module
-    Check Test Case  Attribute With Same Name as Module
+    Check Test Case  ${TESTNAME}

 Importing Submodule As Library
-    Check Test Case  Importing submodule As Library
+    Check Test Case  ${TESTNAME}

 Module Library Scope Should Be Global
${lib module path} = Join Path ${CURDIR}/../../ testresources/testlibs/module_library Check Syslog Contains Imported library 'module_library' with arguments [ ] (version test, module type, global scope, 12 keywords, source ${lib module path}

+
+***Keywords***
+
+Keyword should not have been added
+    [Arguments]  ${kw}  ${lib}=module_lib_with_all
+ Check Syslog Contains Adding keyword '${kw}' to library '${lib}' failed: Not exposed as keyword
=======================================
--- /trunk/atest/testdata/test_libraries/module_library.txt Mon Mar 29 04:47:59 2010 +++ /trunk/atest/testdata/test_libraries/module_library.txt Wed Oct 27 05:52:27 2010
@@ -1,6 +1,7 @@
 *** Settings ***
 Library         module_library
 Library         pythonmodule.library
+Library         module_lib_with_all.py

 *** Variables ***
 @{INTS}  1  2  3  4  5  6  7
@@ -51,6 +52,30 @@
     [Documentation]  FAIL No keyword with name 'Not Keyword' found.
     Not Keyword

+Functions starting with underscore are not keywords
+    [Documentation]  FAIL No keyword with name '_not_keyword' found.
+    _not_keyword
+
+If __all__ is present, only functions listed there are available 1
+    [Documentation]  FAIL No keyword with name 'Not in all' found.
+    ${path} =  Join with execdir  xxx
+    Should Be Equal  ${path}  ${EXECDIR}${/}xxx
+    ${path} =  Abspath  .
+    Should Be Equal  ${path}  ${EXECDIR}
+    Not in all
+
+If __all__ is present, only functions listed there are available 2
+    [Documentation]  FAIL No keyword with name 'Join' found.
+    Join  arg1  arg2
+
+If __all__ is present, only functions listed there are available 3
+    [Documentation]  FAIL No keyword with name 'attr_is_not_kw' found.
+    attr_is_not_kw
+
+If __all__ is present, only functions listed there are available 4
+ [Documentation] FAIL No keyword with name '_not_kw_even_if_listed_in_all' found.
+    _not_kw_even_if_listed_in_all
+
 Class Method Assigned To Module Variable
[Documentation] FAIL Arguments should have been unequal, both were 'Hi'
     Two Arguments From Class  Hello  World
=======================================
--- /trunk/atest/testresources/testlibs/module_library.py Sun Mar 15 08:33:37 2009 +++ /trunk/atest/testresources/testlibs/module_library.py Wed Oct 27 05:52:27 2010
@@ -1,4 +1,4 @@
-ROBOT_LIBRARY_SCOPE = 'Test Suite' # this should be igonred.
+ROBOT_LIBRARY_SCOPE = 'Test Suite'  # this should be ignored
 __version__ = 'test'  # this should be used as version of this library


@@ -46,6 +46,8 @@
 lambda_keyword = lambda arg: int(arg) + 1
 lambda_keyword_with_two_args = lambda x, y: int(x) / int(y)

+def _not_keyword():
+    pass

 def module_library():
return "It should be OK to have an attribute with same name as the module"
=======================================
--- /trunk/doc/userguide/src/ExtendingRobotFramework/CreatingTestLibraries.txt Sat Aug 28 08:09:32 2010 +++ /trunk/doc/userguide/src/ExtendingRobotFramework/CreatingTestLibraries.txt Wed Oct 27 05:52:27 2010
@@ -311,16 +311,72 @@
 Creating static keywords
 ~~~~~~~~~~~~~~~~~~~~~~~~

-Keyword names
-'''''''''''''
+What methods are considered keywords
+''''''''''''''''''''''''''''''''''''

 When the static library API is used, Robot Framework uses reflection
-to find out what methods the library implements. With `dynamic library
-API`_ and `hybrid library API`_, keyword names are got from the
-library directly. Naturally, Robot Framework can see only the public
-methods and it also excludes all methods starting with an
-underscore. With Java libraries, also methods that are implemented in
-:code:`java.lang.Object` and not overridden are ignored.
+to find out what public methods the library class or module
+implements. It will exclude all methods starting with an underscore,
+and with Java libraries also methods that are implemented only in
+:code:`java.lang.Object` are ignored. All the methods that are not
+ignored are considered keywords. For example, the Python and Java
+libraries below implement single keyword :name:`My Keyword`.
+
+.. sourcecode:: python
+
+    class MyLibrary:
+
+        def my_keyword(self, arg):
+            return self._helper_method(arg):
+
+        def _helper_method(self, arg):
+            return arg.upper()
+
+.. sourcecode:: java
+
+    public class MyLibrary {
+
+        public String myKeyword(String arg) {
+            return helperMethod(arg);
+        }
+
+        private String helperMethod(String arg) {
+            return arg.toUpperCase();
+        }
+    }
+
+When the library is implemented as a Python module, it is also
+possible to limit what methods are keywords by using Python's
+:code:`__all__` attribute. If :code:`__all__` is used, only methods
+listed in it can be keywords. For example, the library below
+implements keywords :name:`Example Keyword` and :name:`Second
+Example`. Without :code:`__all__`, it would implement also keywords
+:name:`Not Exposed As Keyword` and :name:`Current Thread`. The most
+important usage for :code:`__all__` is making sure imported helper
+methods, such as :code:`current_thread` in the example below, are not
+accidentally exposed as keywords.
+
+.. sourcecode:: python
+
+   from threading import current_thread
+
+   __all__ = ['example_keyword', 'second_example']
+
+   def example_keyword():
+       if current_thread().name == 'MainThread':
+           print 'Running in main thread'
+
+   def second_example():
+       pass
+
+   def not_exposed_as_keyword():
+       pass
+
+.. note:: Support for the :code:`__all__` attribute is available from
+          Robot Framework 2.5.5 onwards.
+
+Keyword names
+'''''''''''''

 Keyword names used in the test data are compared with method names to
 find the method implementing these keywords. Name comparison is
=======================================
--- /trunk/src/robot/running/testlibraries.py   Tue Sep 21 03:12:02 2010
+++ /trunk/src/robot/running/testlibraries.py   Wed Oct 27 05:52:27 2010
@@ -260,6 +260,12 @@
     def _get_scope(self, libcode):
         return 'GLOBAL'

+    def _get_handler_method(self, libcode, name):
+        method = _BaseTestLibrary._get_handler_method(self, libcode, name)
+        if hasattr(libcode, '__all__') and name not in libcode.__all__:
+            raise TypeError('Not exposed as keyword')
+        return method
+
     def get_instance(self):
         self.init.arguments.check_arg_limits(self.positional_args)
         return self._libcode

Reply via email to