Source code for capybara.utils
from socket import socket
from threading import Lock
from capybara.compat import (
ParseResult,
bytes_,
parse_qsl,
quote,
str_,
unquote,
urlencode,
urlparse)
_missing = object()
[docs]class cached_property(property):
""" Decorates an instance method, turning it into a property whose value is cached. """
def __init__(self, func):
self.__name__ = func.__name__
self.__module__ = func.__module__
self.__doc__ = func.__doc__
self.func = func
def __get__(self, obj, type=None):
if obj is None:
return self
value = obj.__dict__.get(self.__name__, _missing)
if value is _missing:
value = self.func(obj)
obj.__dict__[self.__name__] = value
return value
[docs]class Counter(object):
""" Keeps track of a running count. """
def __init__(self):
self._lock = Lock()
self._value = 0
@property
def value(self):
""" int: The current value of the counter. """
return self._value
def __enter__(self):
with self._lock:
self._value += 1
def __exit__(self, *args):
with self._lock:
self._value -= 1
[docs]def decode_bytes(value):
""" str: Decodes the given byte sequence. """
return value.decode("utf-8") if isbytes(value) else value
[docs]def encode_string(value):
""" bytes: Encodes the given string. """
return value.encode("utf-8") if isstring(value) else value
[docs]def find_available_port():
""" int: A random available port. """
s = socket()
s.bind(("", 0))
return s.getsockname()[1]
[docs]def inner_content(node):
"""
Returns the inner content of a given XML node, including tags.
Args:
node (lxml.etree.Element): The node whose inner content is desired.
Returns:
str: The inner content of the node.
"""
from lxml import etree
# Include text content at the start of the node.
parts = [node.text]
for child in node.getchildren():
# Include the child serialized to raw XML.
parts.append(etree.tostring(child, encoding="utf-8"))
# Include any text following the child.
parts.append(child.tail)
# Discard any non-existent text parts and return.
return "".join(filter(None, parts))
[docs]def inner_text(node):
"""
Returns the inner text of a given XML node, excluding tags.
Args:
node: (lxml.etree.Element): The node whose inner text is desired.
Returns:
str: The inner text of the node.
"""
from lxml import etree
# Include text content at the start of the node.
parts = [node.text]
for child in node.getchildren():
# Include the raw text content of the child.
parts.append(etree.tostring(child, encoding="utf-8", method="text"))
# Include any text following the child.
parts.append(child.tail)
# Discard any non-existent text parts and return.
return "".join(map(decode_bytes, filter(None, parts)))
[docs]def isbytes(value):
""" bool: Whether the given value is a sequence of bytes. """
return isinstance(value, bytes_)
[docs]def isregex(possible_regex):
"""
Returns whether the given object is (probably) a regular expression object.
Args:
possible_regex (object): An object which may or may not be a regular expression.
Returns:
bool: Whether the given object is (probably) a regular expression object.
"""
return hasattr(possible_regex, "search") and callable(possible_regex.search)
[docs]def isstring(value):
""" bool: Whether the given value is a string. """
return isinstance(value, str_)
[docs]def normalize_url(url):
"""
Returns the given URL with all query keys properly escaped.
Args:
url (str): The URL to normalize.
Returns:
str: The normalized URL.
"""
uri = urlparse(url)
query = uri.query or ""
pairs = parse_qsl(query)
decoded_pairs = [(unquote(key), value) for key, value in pairs]
encoded_pairs = [(quote(key), value) for key, value in decoded_pairs]
normalized_query = urlencode(encoded_pairs)
return ParseResult(
scheme=uri.scheme,
netloc=uri.netloc,
path=uri.path,
params=uri.params,
query=normalized_query,
fragment=uri.fragment).geturl()
[docs]def setter_decorator(fset):
"""
Define a write-only property that, in addition to the given setter function, also
provides a setter decorator defined as the property's getter function.
This allows one to set the property either through traditional assignment, as a
method argument, or through decoration::
class Widget(object):
@setter_decorator
def handler(self, value):
self._handler = value
widget = Widget()
# Method 1: Traditional assignment
widget.handler = lambda input: process(input)
# Method 2: Assignment via method argument
widget.handler(lambda input: process(input))
# Method 3: Assignment via decoration
@widget.handler
def handler(input):
return process(input)
# Method 3b: Assignment via decoration with extraneous parens
@widget.handler()
def handler(input):
return process(input)
"""
def fget(self):
def inner(value):
fset(self, value)
def outer(value=None):
if value:
# We are being called with the desired value, either directly or
# as a decorator.
inner(value)
else:
# Assume we are being called as a decorator with extraneous parens,
# so return the setter as the actual decorator.
return inner
return outer
fdoc = fset.__doc__
return property(fget, fset, None, fdoc)