Skip to content

Python 3 compatibility #65

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ RELEASE-VERSION
.idea
*.pyc
*.egg-info
*.egg
*.log
report.html
log.html
output.xml
\#*\#
*~
nosetests.xml
po_log.txt
po_log.txt
libdoc.xml

2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ python:
install:
- pip install .
script:
- nosetests -vs --with-xunit tests/test_unit.py tests/test_functional.py
- nosetests -vs --with-xunit robotpageobjects/tests/test_unit.py robotpageobjects/tests/test_functional.py
deploy:
provider: pypi
user: hellmanj
Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
decorator
six
mock==1.0.1
requests==2.1.0
robotframework-selenium2library==1.7.2
robotframework-selenium2library
uritemplate==0.6
4 changes: 2 additions & 2 deletions robotpageobjects/abstractedlogger.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import robot.api.logger
import robot.output.pyloggingconf as robot_logging_conf

from optionhandler import OptionHandler
from context import Context
from .optionhandler import OptionHandler
from .context import Context


class Logger(object):
Expand Down
7 changes: 4 additions & 3 deletions robotpageobjects/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import re
import importlib
import inspect
import six
import warnings

from robot.utils import asserts
Expand Down Expand Up @@ -124,7 +125,7 @@ def get_funcname_from_robot_alias(cls, alias, pageobject_name):
"""
# Look for a stub matching the alias in the aliases dict.
# If we find one, return the original func name.
for fname, stub in cls._aliases.iteritems():
for fname, stub in six.iteritems(cls._aliases):
if alias == stub.replace(cls._alias_delimiter, "_" + pageobject_name + "_"):
return fname
# We didn't find a match, so take the class name off the end.
Expand Down Expand Up @@ -240,7 +241,7 @@ def merge(self, other_dict, from_subclass=False):
:type other_dict: dict
:returns: None
"""
for key, value in other_dict.iteritems():
for key, value in six.iteritems(other_dict):
overridden = False
if isinstance(key, Override):
key = key.obj
Expand Down Expand Up @@ -718,7 +719,7 @@ def _element_find(self, locator, *args, **kwargs):


self.driver.implicitly_wait(our_wait)


if locator in self.selectors:
locator = self.resolve_selector(locator)
Expand Down
7 changes: 4 additions & 3 deletions robotpageobjects/context.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from robot.libraries.BuiltIn import BuiltIn
from robot.running.context import EXECUTION_CONTEXTS
from monkeypatches import do_monkeypatches
from .monkeypatches import do_monkeypatches

do_monkeypatches()


class Context(object):
"""
Encapsulates the logic for whether we're in Robot or not.
Expand Down Expand Up @@ -39,11 +40,11 @@ def in_robot():
@classmethod
def set_keywords_exposed(cls):
cls._keywords_exposed = True

@classmethod
def set_cache(cls, cache):
cls._cache = cache

@classmethod
def get_cache(cls):
return cls._cache
Expand Down
70 changes: 35 additions & 35 deletions robotpageobjects/monkeypatches.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ def _make_phantomjs(self , remote , desired_capabilities , profile_dir):
try:
browser = self._generic_make_browser(webdriver.PhantomJS,
webdriver.DesiredCapabilities.PHANTOMJS, remote, desired_capabilities)
except WebDriverException, e:
print "Couldn't connect to webdriver. WebDriverException was: " + str(e)
except WebDriverException as e:
print("Couldn't connect to webdriver. WebDriverException was: " + str(e))
browser = None
tries += 1
if browser:
Expand All @@ -29,34 +29,34 @@ def _make_phantomjs(self , remote , desired_capabilities , profile_dir):
Selenium2Library._make_phantomjs = _make_phantomjs

### BEGIN QAR-48165 monkey patch
### This adds consistent support for negative indexes in Robot keywords.
__old_tef_init = TableElementFinder.__init__.__func__
def __new_tef_init(self, *args, **kwargs):
"""
The _locator_suffixes data attribute is used at the end of built-in
locator strings used by Selenium2Library.
Monkey patch: added support for negative indexes (QAR-48165). The
additional locator suffixes are used by the monkey-patched methods
'find_by_row' and 'find_by_col' defined below.
"""
__old_tef_init(self, *args, **kwargs)
self._locator_suffixes[('css', 'last-row')] = [' tr:nth-last-child(%s)']
self._locator_suffixes[('xpath', 'last-row')] = [' //tbody/tr[position()=last()-(%s-1)]']
self._locator_suffixes[('xpath', 'row')] = [' //tbody/tr[%s]']
self._locator_suffixes[('css', 'last-col')] = [' tr td:nth-last-child(%s)', ' tr th:nth-last-child(%s)']
self._locator_suffixes[('xpath', 'last-col')] = [' //tbody/tr/td[position()=last()-(%s-1)]', ' //tbody/tr/td[position()=last()-(%s-1)]']
### This adds consistent support for negative indexes in Robot keywords.

# __old_tef_init = TableElementFinder.__init__.__func__
# def __new_tef_init(self, *args, **kwargs):
# """
# The _locator_suffixes data attribute is used at the end of built-in
# locator strings used by Selenium2Library.

# Monkey patch: added support for negative indexes (QAR-48165). The
# additional locator suffixes are used by the monkey-patched methods
# 'find_by_row' and 'find_by_col' defined below.
# """
# __old_tef_init(self, *args, **kwargs)
# self._locator_suffixes[('css', 'last-row')] = [' tr:nth-last-child(%s)']
# self._locator_suffixes[('xpath', 'last-row')] = [' //tbody/tr[position()=last()-(%s-1)]']
# self._locator_suffixes[('xpath', 'row')] = [' //tbody/tr[%s]']
# self._locator_suffixes[('css', 'last-col')] = [' tr td:nth-last-child(%s)', ' tr th:nth-last-child(%s)']
# self._locator_suffixes[('xpath', 'last-col')] = [' //tbody/tr/td[position()=last()-(%s-1)]', ' //tbody/tr/td[position()=last()-(%s-1)]']

TableElementFinder.__init__ = __new_tef_init
# TableElementFinder.__init__ = __new_tef_init

def find_by_row(self, browser, table_locator, row, content):
"""
"""
Selenium2Library locator method used by _TableElementKeywords.table_row_should_contain
This in turn is used by the built-in Robot keyword 'Table Row Should Contain'.

Monkey patch: added support for negative indexes (QAR-48165).
"""
"""
location_method = "row"
if "-" == row[0]:
row = row[1:]
Expand All @@ -68,32 +68,32 @@ def find_by_row(self, browser, table_locator, row, content):
TableElementFinder.find_by_row = find_by_row

def find_by_col(self, browser, table_locator, col, content):
"""
"""
Selenium2Library locator method used by _TableElementKeywords.table_row_should_contain

Monkey patch: added support for negative indexes (QAR-48165).
"""
"""
location_method = "col"
if "-" == col[0]:
col = col[1:]
location_method = "last-col"
locators = self._parse_table_locator(table_locator, location_method)
locators = [locator % str(col) for locator in locators]
return self._search_in_locators(browser, locators, content)

TableElementFinder.find_by_col = find_by_col

def get_table_cell(self, table_locator, row, column, loglevel='INFO'):
"""Returns the content from a table cell.

Row and column number start from 1. Header and footer rows are
included in the count. A negative row or column number can be used
to get rows counting from the end (end: -1) This means that also
cell content from header or footer rows can be obtained with this
to get rows counting from the end (end: -1) This means that also
cell content from header or footer rows can be obtained with this
keyword. To understand how tables are identified, please take a look at
the `introduction`.
Monkey patch: added support for negative indexes (QAR-48165).

Monkey patch: added support for negative indexes (QAR-48165).
get_table_cell is used by the built-in keyword 'Table Cell Should Contain'.
"""
row = int(row)
Expand All @@ -105,13 +105,13 @@ def get_table_cell(self, table_locator, row, column, loglevel='INFO'):
table = self._table_element_finder.find(self._current_browser(), table_locator)
if table is not None:
rows = table.find_elements_by_xpath("./thead/tr")
if row_index >= len(rows) or row_index < 0:
if row_index >= len(rows) or row_index < 0:
rows.extend(table.find_elements_by_xpath("./tbody/tr"))
if row_index >= len(rows) or row_index < 0:
if row_index >= len(rows) or row_index < 0:
rows.extend(table.find_elements_by_xpath("./tfoot/tr"))
if row_index < len(rows):
columns = rows[row_index].find_elements_by_tag_name('th')
if column_index >= len(columns) or column_index < 0:
if column_index >= len(columns) or column_index < 0:
columns.extend(rows[row_index].find_elements_by_tag_name('td'))
if column_index < len(columns):
return columns[column_index].text
Expand Down
17 changes: 9 additions & 8 deletions robotpageobjects/optionhandler.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import re
import os
import imp
import six

from context import Context
import exceptions
from .context import Context
from .exceptions import VarFileImportErrorError


from robot.libraries.BuiltIn import BuiltIn
Expand All @@ -24,7 +25,7 @@ def __new__(cls, *args, **kwargs):

# Singleton pattern...
if cls._instance is None:
cls._instance = super(OptionHandler, cls).__new__(cls, *args, **kwargs)
cls._instance = super().__new__(cls)
cls._new_called += 1

return cls._instance
Expand All @@ -49,7 +50,7 @@ def _populate_opts(self, robot=True):
def _get_opts_from_robot(self):
ret = {}
robot_vars = BuiltIn().get_variables()
for var, val in robot_vars.iteritems():
for var, val in six.iteritems(robot_vars):
ret[self._normalize(var)] = val
return ret

Expand All @@ -61,8 +62,8 @@ def _get_opts_from_var_file(self):
try:
vars_mod = imp.load_source("vars", abs_var_file_path)

except (ImportError, IOError), e:
raise exceptions.VarFileImportErrorError(
except (ImportError, IOError) as e:
raise VarFileImportErrorError(
"Couldn't import variable file: %s. Ensure it exists and is importable." % var_file_path)

var_file_attrs = vars_mod.__dict__
Expand All @@ -85,13 +86,13 @@ def _normalize(self, opts):
Convert an option keyname to lower-cased robot format, or convert
all the keys in a dictionary to robot format.
"""
if isinstance(opts, basestring):
if isinstance(opts, str):
name = opts.lower()
rmatch = re.search("\$\{(.+)\}", name)
return rmatch.group(1) if rmatch else name
else:
# We're dealing with a dict
return {self._normalize(key): val for (key, val) in opts.iteritems()}
return {self._normalize(key): val for (key, val) in six.iteritems(opts)}

def get(self, name, default=None):
"""
Expand Down
13 changes: 8 additions & 5 deletions robotpageobjects/page.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@
from __future__ import print_function
import inspect
import re
import urllib2
try:
import urllib.request as urllib2
except ImportError:
import urllib2

import decorator
from Selenium2Library import Selenium2Library
Expand Down Expand Up @@ -241,7 +244,7 @@ def get_keyword_names(self):
def _attempt_screenshot(self):
try:
self.capture_page_screenshot()
except Exception, e:
except Exception as e:
if e.message.find("No browser is open") != -1:
pass

Expand Down Expand Up @@ -387,7 +390,7 @@ def _vars_match_template(template, vars):
:param vars: The variables to match against the template
:type vars: tuple or list
:returns: bool"""
keys = vars.keys()
keys = list(vars.keys())
keys.sort()
template_vars = list(uritemplate.variables(template))
template_vars.sort()
Expand Down Expand Up @@ -435,7 +438,7 @@ def _resolve_url(self, *args):

first_arg = args[0]
if not self._is_robot:
if isinstance(first_arg, basestring):
if isinstance(first_arg, str):
# In Python, if the first argument is a string and not a dict, it's a url or path.
arg_type = "url"
else:
Expand Down Expand Up @@ -574,7 +577,7 @@ class MyPageObject(PageObject):

try:
self.open_browser(resolved_url, self.browser, remote_url=remote_url, desired_capabilities=caps)
except (urllib2.HTTPError, WebDriverException, ValueError), e:
except (urllib2.HTTPError, WebDriverException, ValueError) as e:
raise exceptions.SauceConnectionError("Unable to run Sauce job.\n%s\n"
"Sauce variables were:\n"
"sauce_platform: %s\n"
Expand Down
File renamed without changes.
File renamed without changes.
11 changes: 6 additions & 5 deletions tests/basetestcase.py → robotpageobjects/tests/basetestcase.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import re
import os
import glob
import six
from nose.tools import nottest


Expand Down Expand Up @@ -102,7 +103,7 @@ def tearDown(self):
for key in self.original_env_vars:
os.environ[key] = self.original_env_vars[key]

for key in os.environ.keys():
for key in list(os.environ):
if key not in self.original_env_vars:
del os.environ[key]

Expand Down Expand Up @@ -135,7 +136,7 @@ def run_scenario(self, scenario, *args, **kwargs):
if "env" in kwargs:
env_vars = kwargs["env"]
del kwargs["env"]
for var, val in env_vars.iteritems():
for var, val in six.iteritems(env_vars):
self.set_env(var, val)

if scenario.endswith(".py"):
Expand Down Expand Up @@ -180,14 +181,14 @@ def __repr__(self):
else:
dash = "-" if len(name) == 1 else "--"
opt_str += dash + name.replace("_", "-") + " " + val + " "
cmd = ' '.join([base_cmd, opt_str, target])
cmd = str.join(six.u(' '), [base_cmd, opt_str, target])

# execute command
p = subprocess.Popen(cmd, cwd=working_dir, shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
com = p.communicate()
code = p.wait()
# Out is either stdout, or stderr
out = " ".join(com)
out = str.join(six.u(" "), [str(x) for x in com])
# Splice out trailing new line
out = out[:-1]
return Ret(cmd, code, out)
Expand Down Expand Up @@ -287,7 +288,7 @@ def write_var_file(self, *args, **kwargs):
for i in kwargs:
line = "%s = '%s'\n" % (i, kwargs[i])
f.write(line)
except Exception, e:
except Exception as e:
raise Exception("Problem creating vars file: %s" % e)
finally:
if f:
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .homepage import BaseHomePage
from .resultspage import BaseResultsPage
File renamed without changes.
Loading