Skip to content

Commit fa1fee3

Browse files
pekkaklarckaaltat
authored andcommitted
Little import cleanups. Expose some often used robot.utils via our utils. Fixes and cleanup to running keyword on failure. 1. Return value from `Register keyword to run on failure` can now always be used as an argument to the keyword. In practice the keyword nowadays returns `None` if the functionality was disabled and `None` (as well as `'NONE'`) can be used to disable the functionality. Fixes robotframework#176. 2. Moved the hook method to run when a keyword fails to the core. This simplifies implementation. The hook method was also renamed from `run_on_failure` to `failure_occurred`. Make public API public. robotframework#882 Renamed these: _run_on_failure_keyword _speed_in_secs _timeout_in_secs _implicit_wait_in_secs to these: run_on_failure_keyword speed timeout implicit_wait Original attributes still work but are deprecated. Deprecation isn't currently tested but unit tests could be added. _browser -> browser robotframework#882
1 parent a2e6cb5 commit fa1fee3

18 files changed

+277
-229
lines changed

src/SeleniumLibrary/__init__.py

Lines changed: 54 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,24 @@
1616

1717
import warnings
1818

19-
from .base import DynamicCore
20-
from .keywords import AlertKeywords
21-
from .keywords import BrowserManagementKeywords
22-
from .keywords import CookieKeywords
23-
from .keywords import ElementKeywords
24-
from .keywords import FormElementKeywords
25-
from .keywords import JavaScriptKeywords
26-
from .keywords import RunOnFailureKeywords
27-
from .keywords import ScreenshotKeywords
28-
from .keywords import SelectElementKeywords
29-
from .keywords import TableElementKeywords
30-
from .keywords import WaitingKeywords
31-
from .locators import ElementFinder
32-
from .utils import BrowserCache
33-
from .utils import LibraryListener
19+
from robot.api import logger
20+
from robot.libraries.BuiltIn import BuiltIn
21+
22+
from SeleniumLibrary.base import DynamicCore
23+
from SeleniumLibrary.keywords import (AlertKeywords,
24+
BrowserManagementKeywords,
25+
CookieKeywords,
26+
ElementKeywords,
27+
FormElementKeywords,
28+
JavaScriptKeywords,
29+
RunOnFailureKeywords,
30+
ScreenshotKeywords,
31+
SelectElementKeywords,
32+
TableElementKeywords,
33+
WaitingKeywords)
34+
from SeleniumLibrary.locators import ElementFinder
35+
from SeleniumLibrary.utils import (BrowserCache, Deprecated, LibraryListener,
36+
timestr_to_secs)
3437

3538

3639
__version__ = '3.0.0b1'
@@ -194,7 +197,6 @@ class SeleniumLibrary(DynamicCore):
194197
Note that prior to SeleniumLibrary 3.0, all non-empty strings, including
195198
``false``, ``no`` and ``none``, were considered true.
196199
"""
197-
198200
ROBOT_LIBRARY_SCOPE = 'GLOBAL'
199201
ROBOT_LIBRARY_VERSION = __version__
200202

@@ -234,11 +236,13 @@ def __init__(self,
234236
| Library `|` SeleniumLibrary `|` implicit_wait=5 `|` run_on_failure=Log Source | # Sets default implicit_wait to 5 seconds and runs `Log Source` on failure |
235237
| Library `|` SeleniumLibrary `|` timeout=10 `|` run_on_failure=Nothing | # Sets default timeout to 10 seconds and does nothing on failure |
236238
"""
237-
self._run_on_failure_keyword = None
238-
self._running_on_failure_routine = False
239-
self._speed_in_secs = 0.0
240-
self._timeout_in_secs = 5.0
241-
self._implicit_wait_in_secs = 5.0
239+
self.timeout = timestr_to_secs(timeout)
240+
self.implicit_wait = timestr_to_secs(implicit_wait)
241+
self.speed = 0.0
242+
self.run_on_failure_keyword \
243+
= RunOnFailureKeywords.resolve_keyword(run_on_failure)
244+
self._running_on_failure_keyword = False
245+
self.screenshot_root_directory = screenshot_root_directory
242246
libraries = [
243247
AlertKeywords(self),
244248
BrowserManagementKeywords(self),
@@ -254,35 +258,46 @@ def __init__(self,
254258
]
255259
self._browsers = BrowserCache()
256260
DynamicCore.__init__(self, libraries)
257-
self.screenshot_root_directory = screenshot_root_directory
258-
self.set_selenium_timeout(timeout)
259-
self.set_selenium_implicit_wait(implicit_wait)
260-
self.register_keyword_to_run_on_failure(run_on_failure)
261261
self.ROBOT_LIBRARY_LISTENER = LibraryListener()
262262
self.element_finder = ElementFinder(self)
263263

264+
_speed_in_secs = Deprecated('_speed_in_secs', 'speed')
265+
_timeout_in_secs = Deprecated('_timeout_in_secs', 'timeout')
266+
_implicit_wait_in_secs = Deprecated('_implicit_wait_in_secs',
267+
'implicit_wait')
268+
_run_on_failure_keyword = Deprecated('_run_on_failure_keyword',
269+
'run_on_failure_keyword')
270+
264271
def run_keyword(self, name, args, kwargs):
265272
try:
266273
return DynamicCore.run_keyword(self, name, args, kwargs)
267274
except Exception:
268-
self.run_on_failure()
275+
self.failure_occurred()
269276
raise
270277

271278
def register_browser(self, browser, alias):
272279
return self._browsers.register(browser, alias)
273280

274-
def run_on_failure(self):
275-
"""Executes the registered run on failure keyword.
281+
def failure_occurred(self):
282+
"""Method that is executed when a SeleniumLibrary keyword fails.
276283
277-
This is designed as an API when writing library which extends the
278-
SeleniumLibrary with new functionality. If that new functionality
279-
does not (always) relay on SeleniumLibrary keyword methods, then the
280-
new functionality can use this method to execute the run on failure
281-
functionality in SeleniumLibrary"""
282-
RunOnFailureKeywords(self).run_on_failure()
284+
By default executes the registered run-on-failure keyword.
285+
Libraries extending SeleniumLibrary can overwrite this hook
286+
method if they want to provide custom functionality instead.
287+
"""
288+
if self._running_on_failure_keyword or not self.run_on_failure_keyword:
289+
return
290+
try:
291+
self._running_on_failure_keyword = True
292+
BuiltIn().run_keyword(self.run_on_failure_keyword)
293+
except Exception as err:
294+
logger.warn("Keyword '%s' could not be run on failure: %s"
295+
% (self.run_on_failure_keyword, err))
296+
finally:
297+
self._running_on_failure_keyword = False
283298

284299
@property
285-
def _browser(self):
300+
def browser(self):
286301
"""Current active browser"""
287302
if not self._browsers.current:
288303
raise RuntimeError('No browser is open')
@@ -296,12 +311,12 @@ def _cache(self):
296311

297312
def _current_browser(self):
298313
warnings.warn('"SeleniumLibrary._current_browser" is deprecated, '
299-
'use "SeleniumLibrary._browser" instead.',
314+
'use "SeleniumLibrary.browser" instead.',
300315
DeprecationWarning)
301-
return self._browser
316+
return self.browser
302317

303318
def _run_on_failure(self):
304319
warnings.warn('"SeleniumLibrary._run_on_failure" is deprecated, '
305-
'use "SeleniumLibrary.run_on_failure" instead.',
320+
'use "SeleniumLibrary.failure_occurred" instead.',
306321
DeprecationWarning)
307-
self.run_on_failure()
322+
self.failure_occurred()

src/SeleniumLibrary/base/context.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@
1414
# See the License for the specific language governing permissions and
1515
# limitations under the License.
1616

17+
1718
class ContextAware(object):
1819

1920
def __init__(self, ctx):
2021
self.ctx = ctx
2122

2223
@property
2324
def browser(self):
24-
return self.ctx._browser
25+
return self.ctx.browser
2526

2627
@property
2728
def browsers(self):

src/SeleniumLibrary/keywords/browsermanagement.py

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@
1919
import types
2020

2121
from robot.errors import DataError
22-
from robot.utils import secs_to_timestr, timestr_to_secs
2322
from selenium import webdriver
2423
from selenium.common.exceptions import NoSuchWindowException
2524

2625
from SeleniumLibrary.base import LibraryComponent, keyword
2726
from SeleniumLibrary.locators.windowmanager import WindowManager
28-
from SeleniumLibrary.utils import is_truthy, is_falsy
27+
from SeleniumLibrary.utils import (is_truthy, is_falsy,
28+
secs_to_timestr, timestr_to_secs)
2929

3030

3131
ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
@@ -479,22 +479,25 @@ def reload_page(self):
479479
def get_selenium_speed(self):
480480
"""Gets the delay in seconds that is waited after each Selenium command.
481481
482-
See `Set Selenium Speed` for an explanation."""
483-
return secs_to_timestr(self.ctx._speed_in_secs)
482+
See `Set Selenium Speed` for an explanation.
483+
"""
484+
return secs_to_timestr(self.ctx.speed)
484485

485486
@keyword
486487
def get_selenium_timeout(self):
487488
"""Gets the timeout in seconds that is used by various keywords.
488489
489-
See `Set Selenium Timeout` for an explanation."""
490-
return secs_to_timestr(self.ctx._timeout_in_secs)
490+
See `Set Selenium Timeout` for an explanation.
491+
"""
492+
return secs_to_timestr(self.ctx.timeout)
491493

492494
@keyword
493495
def get_selenium_implicit_wait(self):
494496
"""Gets the wait in seconds that is waited by Selenium.
495497
496-
See `Set Selenium Implicit Wait` for an explanation."""
497-
return secs_to_timestr(self.ctx._implicit_wait_in_secs)
498+
See `Set Selenium Implicit Wait` for an explanation.
499+
"""
500+
return secs_to_timestr(self.ctx.implicit_wait)
498501

499502
@keyword
500503
def set_selenium_speed(self, seconds):
@@ -514,10 +517,10 @@ def set_selenium_speed(self, seconds):
514517
Example:
515518
| Set Selenium Speed | .5 seconds |
516519
"""
517-
old_speed = self.ctx._speed_in_secs
518-
self.ctx._speed_in_secs = timestr_to_secs(seconds)
520+
old_speed = self.ctx.speed
521+
self.ctx.speed = timestr_to_secs(seconds)
519522
for browser in self.browsers.browsers:
520-
browser._speed = self.ctx._speed_in_secs
523+
browser._speed = self.ctx.speed
521524
self._monkey_patch_speed(browser)
522525
return old_speed
523526

@@ -540,9 +543,9 @@ def set_selenium_timeout(self, seconds):
540543
| Set Selenium Timeout | ${orig timeout} |
541544
"""
542545
old_timeout = self.get_selenium_timeout()
543-
self.ctx._timeout_in_secs = timestr_to_secs(seconds)
546+
self.ctx.timeout = timestr_to_secs(seconds)
544547
for browser in self.browsers.get_open_browsers():
545-
browser.set_script_timeout(self.ctx._timeout_in_secs)
548+
browser.set_script_timeout(self.ctx.timeout)
546549
return old_timeout
547550

548551
@keyword
@@ -560,9 +563,9 @@ def set_selenium_implicit_wait(self, seconds):
560563
| Set Selenium Implicit Wait | ${orig wait} |
561564
"""
562565
old_wait = self.get_selenium_implicit_wait()
563-
self.ctx._implicit_wait_in_secs = timestr_to_secs(seconds)
566+
self.ctx.implicit_wait = timestr_to_secs(seconds)
564567
for browser in self.browsers.get_open_browsers():
565-
browser.implicitly_wait(self.ctx._implicit_wait_in_secs)
568+
browser.implicitly_wait(self.ctx.implicit_wait)
566569
return old_wait
567570

568571
@keyword
@@ -578,8 +581,7 @@ def set_browser_implicit_wait(self, seconds):
578581
579582
See also `Set Selenium Implicit Wait`.
580583
"""
581-
implicit_wait_in_secs = timestr_to_secs(seconds)
582-
self.browser.implicitly_wait(implicit_wait_in_secs)
584+
self.browser.implicitly_wait(timestr_to_secs(seconds))
583585

584586
def _get_browser_creation_function(self, browser_name):
585587
func_name = BROWSER_NAMES.get(browser_name.lower().replace(' ', ''))
@@ -593,8 +595,8 @@ def _make_browser(self, browser_name, desired_capabilities=None,
593595
raise ValueError(browser_name + " is not a supported browser.")
594596

595597
browser = creation_func(remote, desired_capabilities, profile_dir)
596-
browser.set_script_timeout(self.ctx._timeout_in_secs)
597-
browser.implicitly_wait(self.ctx._implicit_wait_in_secs)
598+
browser.set_script_timeout(self.ctx.timeout)
599+
browser.implicitly_wait(self.ctx.implicit_wait)
598600

599601
return browser
600602

src/SeleniumLibrary/keywords/element.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@
1919

2020
from SeleniumLibrary.base import LibraryComponent, keyword
2121
from SeleniumLibrary.keywords.formelement import FormElementKeywords
22-
from SeleniumLibrary.utils import escape_xpath_value
23-
from SeleniumLibrary.utils import is_truthy, is_falsy
22+
from SeleniumLibrary.utils import escape_xpath_value, is_falsy, is_truthy
2423

2524

2625
class ElementKeywords(LibraryComponent):

src/SeleniumLibrary/keywords/runonfailure.py

Lines changed: 29 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,8 @@
1414
# See the License for the specific language governing permissions and
1515
# limitations under the License.
1616

17-
from robot.libraries.BuiltIn import BuiltIn
18-
1917
from SeleniumLibrary.base import LibraryComponent, keyword
18+
from SeleniumLibrary.utils import is_falsy, is_string
2019

2120

2221
class RunOnFailureKeywords(LibraryComponent):
@@ -25,53 +24,45 @@ class RunOnFailureKeywords(LibraryComponent):
2524
def register_keyword_to_run_on_failure(self, keyword):
2625
"""Sets the keyword to execute when a SeleniumLibrary keyword fails.
2726
28-
`keyword_name` is the name of a keyword (from any available
29-
libraries) that will be executed if a SeleniumLibrary keyword fails.
30-
It is not possible to use a keyword that requires arguments.
31-
Using the value "Nothing" will disable this feature altogether.
27+
`keyword` is the name of a keyword that will be executed if a
28+
SeleniumLibrary keyword fails. It is possible to use any available
29+
keyword, including user keywords or keywords from other libraries,
30+
but the keyword must not take any arguments.
3231
3332
The initial keyword to use is set in `importing`, and the
3433
keyword that is used by default is `Capture Page Screenshot`.
3534
Taking a screenshot when something failed is a very useful
3635
feature, but notice that it can slow down the execution.
3736
37+
It is possible to use string "Nothing" or "None", case-insensitively,
38+
as well as any value considered false in Python to disable this
39+
feature altogether.
40+
3841
This keyword returns the name of the previously registered
39-
failure keyword. It can be used to restore the original
40-
value later.
42+
failure keyword or Python ``None`` if this functionality was
43+
previously disabled. The return value can be always used to
44+
restore the original value later.
4145
4246
Example:
4347
| Register Keyword To Run On Failure | Log Source | # Run `Log Source` on failure. |
4448
| ${previous kw}= | Register Keyword To Run On Failure | Nothing | # Disables run-on-failure functionality and stores the previous kw name in a variable. |
4549
| Register Keyword To Run On Failure | ${previous kw} | # Restore to the previous keyword. |
46-
"""
47-
old_keyword = self.ctx._run_on_failure_keyword
48-
old_keyword_text = old_keyword if old_keyword else "No keyword"
49-
50-
new_keyword = keyword if keyword.strip().lower() != "nothing" else None
51-
new_keyword_text = new_keyword if new_keyword else "No keyword"
5250
53-
self.ctx._run_on_failure_keyword = new_keyword
54-
self.info('%s will be run on failure.' % new_keyword_text)
55-
56-
return old_keyword_text
57-
58-
def run_on_failure(self):
59-
if not self.ctx._run_on_failure_keyword:
60-
return
61-
if self.ctx._running_on_failure_routine:
62-
return
63-
self.ctx._running_on_failure_routine = True
64-
try:
65-
BuiltIn().run_keyword(self.ctx._run_on_failure_keyword)
66-
except Exception as err:
67-
self.run_on_failure_error(err)
68-
finally:
69-
self.ctx._running_on_failure_routine = False
51+
Changes in version 3.0.0:
52+
- Possible to use string "NONE" or any falsy value to disable the
53+
feature.
54+
- Return Python ``None`` when the functionality was disabled earlier.
55+
In previous versions special value "No Keyword" was returned and
56+
it could not be used to restore the original state.
57+
"""
58+
old_keyword = self.ctx.run_on_failure_keyword
59+
keyword = self.resolve_keyword(keyword)
60+
self.ctx.run_on_failure_keyword = keyword
61+
self.info('%s will be run on failure.' % (keyword or 'No keyword'))
62+
return old_keyword
7063

71-
def run_on_failure_error(self, err):
72-
err = ("Keyword '%s' could not be run on failure: %s"
73-
% (self.ctx._run_on_failure_keyword, err))
74-
if hasattr(self, 'warn'):
75-
self.warn(err)
76-
return
77-
raise Exception(err)
64+
@staticmethod
65+
def resolve_keyword(name):
66+
if is_falsy(name) or is_string(name) and name.upper() == 'NOTHING':
67+
return None
68+
return name

src/SeleniumLibrary/keywords/screenshot.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,11 @@
1818
import os
1919
import re
2020

21-
from robot.libraries.BuiltIn import BuiltIn
22-
from robot.libraries.BuiltIn import RobotNotRunningError
21+
from robot.libraries.BuiltIn import BuiltIn, RobotNotRunningError
2322
from robot.utils import get_link_path
2423

2524
from SeleniumLibrary.base import LibraryComponent, keyword
26-
from SeleniumLibrary.utils import events
27-
from SeleniumLibrary.utils import is_falsy
25+
from SeleniumLibrary.utils import events, is_falsy
2826

2927

3028
class ScreenshotKeywords(LibraryComponent):

0 commit comments

Comments
 (0)