<commit_before>import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
import tornado.httpclient

import urllib
import json
import datetime
import time

from oauth import oauth

from tornado.options import define, options
define("port", default=8000, help="run on the given port", type=int)

class IndexHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    def get(self):
        query = self.get_argument('q')
        client = tornado.httpclient.AsyncHTTPClient()
        url = "https://api.twitter.com/1.1/search/tweets.json?" + \
                urllib.urlencode({"q": query, "result_type": "recent", "count": 100})
        client.fetch(url, headers={'Authorization': oauth(url)}, callback=self.on_response)

    def on_response(self, response):
        body = json.loads(response.body)
        result_count = len(body['statuses'])
        now = datetime.datetime.utcnow()
        raw_oldest_tweet_at = body['statuses'][-1]['created_at']
        oldest_tweet_at = datetime.datetime.strptime(raw_oldest_tweet_at,
                "%a %b %d %H:%M:%S +0000 %Y")
        seconds_diff = time.mktime(now.timetuple()) - \
                time.mktime(oldest_tweet_at.timetuple())
        tweets_per_second = float(result_count) / seconds_diff
        self.write("""

%s

%.02f

tweets per second

""" % (self.get_argument('q'), tweets_per_second))
        self.finish()

if __name__ == "__main__":
    tornado.options.parse_command_line()
    app = tornado.web.Application(handlers=[(r"/", IndexHandler)])
    http_server = tornado.httpserver.HTTPServer(app)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()<commit_msg>added bit notes about asynchronous calls, self.finish and decorators<commit_after>import urllib
import json
import datetime
import time

import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
import tornado.httpclient

from oauth import oauth # for twitter

from tornado.options import define, options
define("port", default=8000, help="run on the given port", type=int)

class IndexHandler(tornado.web.RequestHandler):
    # decorator is used to leave the connection open till the callback is
    # executed.
    @tornado.web.asynchronous
    def get(self):
        query = self.get_argument('q')
        # here AsynHTTPClient is used to to handle requests asynchrnously
        # and a call back function is needed which will be called when the
        # response is returned.
        # in this case, when twitter returns the response, the callback
        # is called.
        # compared to earlier the code is now split in two functions to
        # handle the requests.
        client = tornado.httpclient.AsyncHTTPClient()
        url = "https://api.twitter.com/1.1/search/tweets.json?" + \
                urllib.urlencode({"q": query, "result_type": "recent", "count": 100})
        client.fetch(url, headers={'Authorization': oauth(url)}, callback=self.on_response)

    def on_response(self, response):
        body = json.loads(response.body)
        result_count = len(body['statuses'])
        now = datetime.datetime.utcnow()
        raw_oldest_tweet_at = body['statuses'][-1]['created_at']
        oldest_tweet_at = datetime.datetime.strptime(raw_oldest_tweet_at,
                "%a %b %d %H:%M:%S +0000 %Y")
        seconds_diff = time.mktime(now.timetuple()) - \
                time.mktime(oldest_tweet_at.timetuple())
        tweets_per_second = float(result_count) / seconds_diff
        self.write("""

%s

%.02f

tweets per second

""" % (self.get_argument('q'), tweets_per_second))
        # self.finish() is very important as Tornado will never close the
        # connection on it's own. So by using self.finish we are telling
        # Tornado to close the connection.
        # otherise bad things will happen.. mmkay.
        self.finish()

if __name__ == "__main__":
    tornado.options.parse_command_line()
    app = tornado.web.Application(handlers=[(r"/", IndexHandler)])
    http_server = tornado.httpserver.HTTPServer(app)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()<|endoftext|>


<commit_before># Licensed to the Software Freedom Conservancy (SFC) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The SFC licenses this file
# to you 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.

"""
The ActionChains implementation,
"""
import warnings

from .actions.wheel_input import ScrollOrigin

from .utils import keys_to_typing
from .actions.action_builder import ActionBuilder
from selenium.webdriver.remote.webelement import WebElement

class ActionChains:
    """
    ActionChains are a way to automate low level interactions such as
    mouse movements, mouse button actions, key press, and context menu interactions.
    This is useful for doing more complex actions like hover over and drag and drop.

    Generate user actions.
       When you call methods for actions on the ActionChains object,
       the actions are stored in a queue in the ActionChains object.
       When you call perform(), the events are fired in the order they
       are queued up.

    ActionChains can be used in a chain pattern::

        menu = driver.find_element(By.CSS_SELECTOR, ".nav")
        hidden_submenu = driver.find_element(By.CSS_SELECTOR, ".nav #submenu1")

        ActionChains(driver).move_to_element(menu).click(hidden_submenu).perform()

    Or actions can be queued up one by one, then performed.::

        menu = driver.find_element(By.CSS_SELECTOR, ".nav")
        hidden_submenu = driver.find_element(By.CSS_SELECTOR, ".nav #submenu1")

        actions = ActionChains(driver)
        actions.move_to_element(menu)
        actions.click(hidden_submenu)
        actions.perform()

    Either way, the actions are performed in the order they are called, one after
    another.
    """

    def __init__(self, driver, duration=250):
        """
        Creates a new ActionChains.

        :Args:
         - driver: The WebDriver instance which performs user actions.
         - duration: override the default 250 msecs of DEFAULT_MOVE_DURATION in PointerInput
        """
        self._driver = driver
        self._actions = []
        self.w3c_actions = ActionBuilder(driver, duration=duration)

    def perform(self):
        """
        Performs all stored actions.
        """
        self.w3c_actions.perform()

    def reset_actions(self):
        """
        Clears actions that are already stored locally and on the remote end
        """
        self.w3c_actions.clear_actions()
        for device in self.w3c_actions.devices:
            device.clear_actions()
        self._actions = []

    def click(self, on_element=None):
        """
        Clicks an element.

        :Args:
         - on_element: The element to click.
           If None, clicks on current mouse position.
        """
        if on_element:
            self.move_to_element(on_element)

        self.w3c_actions.pointer_action.click()
        self.w3c_actions.key_action.pause()
        self.w3c_actions.key_action.pause()

        return self

    def click_and_hold(self, on_element=None):
        """
        Holds down the left mouse button on an element.

        :Args:
         - on_element: The element to mouse down.
           If None, clicks on current mouse position.
        """
        if on_element:
            self.move_to_element(on_element)

        self.w3c_actions.pointer_action.click_and_hold()
        self.w3c_actions.key_action.pause()

        return self

    def context_click(self, on_element=None):
        """
        Performs a context-click (right click) on an element.

        :Args:
         - on_element: The element to context-click.
           If None, clicks on current mouse position.
        """
        if on_element:
            self.move_to_element(on_element)

        self.w3c_actions.pointer_action.context_click()
        self.w3c_actions.key_action.pause()
        self.w3c_actions.key_action.pause()

        return self

    def double_click(self, on_element=None):
        """
        Double-clicks an element.

        :Args:
         - on_element: The element to double-click.
           If None, clicks on current mouse position.
        """
        if on_element:
            self.move_to_element(on_element)

        self.w3c_actions.pointer_action.double_click()
        for _ in range(4):
            self.w3c_actions.key_action.pause()

        return self

    def drag_and_drop(self, source, target):
        """
        Holds down the left mouse button on the source element,
           then moves to the target element and releases the mouse button.

        :Args:
         - source: The element to mouse down.
         - target: The element to mouse up.
        """
        self.click_and_hold(source)
        self.release(target)
        return self

    def drag_and_drop_by_offset(self, source, xoffset, yoffset):
        """
        Holds down the left mouse button on the source element,
           then moves to the target offset and releases the mouse button.

        :Args:
         - source: The element to mouse down.
         - xoffset: X offset to move to.
         - yoffset: Y offset to move to.
        """
        self.click_and_hold(source)
        self.move_by_offset(xoffset, yoffset)
        self.release()
        return self

    def key_down(self, value, element=None):
        """
        Sends a key press only, without releasing it.
           Should only be used with modifier keys (Control, Alt and Shift).

        :Args:
         - value: The modifier key to send. Values are defined in `Keys` class.
         - element: The element to send keys.
           If None, sends a key to current focused element.

        Example, pressing ctrl+c::

            ActionChains(driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()

        """
        if element:
            self.click(element)

        self.w3c_actions.key_action.key_down(value)
        self.w3c_actions.pointer_action.pause()

        return self

    def key_up(self, value, element=None):
        """
        Releases a modifier key.

        :Args:
         - value: The modifier key to send. Values are defined in Keys class.
         - element: The element to send keys.
           If None, sends a key to current focused element.

        Example, pressing ctrl+c::

            ActionChains(driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()

        """
        if element:
            self.click(element)

        self.w3c_actions.key_action.key_up(value)
        self.w3c_actions.pointer_action.pause()

        return self

    def move_by_offset(self, xoffset, yoffset):
        """
        Moving the mouse to an offset from current mouse position.

        :Args:
         - xoffset: X offset to move to, as a positive or negative integer.
         - yoffset: Y offset to move to, as a positive or negative integer.
        """

        self.w3c_actions.pointer_action.move_by(xoffset, yoffset)
        self.w3c_actions.key_action.pause()

        return self

    def move_to_element(self, to_element):
        """
        Moving the mouse to the middle of an element.

        :Args:
         - to_element: The WebElement to move to.
        """

        self.w3c_actions.pointer_action.move_to(to_element)
        self.w3c_actions.key_action.pause()

        return self

    def move_to_element_with_offset(self, to_element, xoffset, yoffset):
        """
        Move the mouse by an offset of the specified element.
           Offsets are relative to the top-left corner of the element.

        :Args:
         - to_element: The WebElement to move to.
         - xoffset: X offset to move to.
         - yoffset: Y offset to move to.
        """

        self.w3c_actions.pointer_action.move_to(to_element,
                                                int(xoffset),
                                                int(yoffset))
        self.w3c_actions.key_action.pause()

        return self

    def pause(self, seconds):
        """ Pause all inputs for the specified duration in seconds """

        self.w3c_actions.pointer_action.pause(seconds)
        self.w3c_actions.key_action.pause(seconds)

        return self

    def release(self, on_element=None):
        """
        Releasing a held mouse button on an element.

        :Args:
         - on_element: The element to mouse up.
           If None, releases on current mouse position.
        """
        if on_element:
            self.move_to_element(on_element)

        self.w3c_actions.pointer_action.release()
        self.w3c_actions.key_action.pause()

        return self

    def send_keys(self, *keys_to_send):
        """
        Sends keys to current focused element.

        :Args:
         - keys_to_send: The keys to send.  Modifier keys constants can be found in the
           'Keys' class.
        """
        typing = keys_to_typing(keys_to_send)

        for key in typing:
            self.key_down(key)
            self.key_up(key)

        return self

    def send_keys_to_element(self, element, *keys_to_send):
        """
        Sends keys to an element.

        :Args:
         - element: The element to send keys.
         - keys_to_send: The keys to send.  Modifier keys constants can be found in the
           'Keys' class.
        """
        self.click(element)
        self.send_keys(*keys_to_send)
        return self

    def scroll_to_element(self, element: WebElement):
        """
        If the element is outside the viewport, scrolls the bottom of the element to the bottom of the viewport.

        :Args:
         - element: Which element to scroll into the viewport.
        """

        self.w3c_actions.wheel_action.scroll(origin=element)
        return self

    def scroll_by_amount(self, delta_x: int, delta_y: int):
        """
        Scrolls by provided amounts with the origin in the top left corner of the viewport.

        :Args:
         - delta_x: Distance along X axis to scroll using the wheel. A negative value scrolls left.
         - delta_y: Distance along Y axis to scroll using the wheel. A negative value scrolls up.
        """

        self.w3c_actions.wheel_action.scroll(delta_x=delta_x, delta_y=delta_y)
        return self

    def scroll_from_origin(self, scroll_origin: ScrollOrigin, delta_x: int, delta_y: int):
        """
        Scrolls by provided amount based on a provided origin.
        The scroll origin is either the center of an element or the upper left of the viewport plus any offsets.
        If the origin is an element, and the element is not in the viewport, the bottom of the element will first
        be scrolled to the bottom of the viewport.

        :Args:
         - origin: Where scroll originates (viewport or element center) plus provided offsets.
         - delta_x: Distance along X axis to scroll using the wheel. A negative value scrolls left.
         - delta_y: Distance along Y axis to scroll using the wheel. A negative value scrolls up.

         :Raises: If the origin with offset is outside the viewport.
          - MoveTargetOutOfBoundsException - If the origin with offset is outside the viewport.
        """

        if not isinstance(scroll_origin, ScrollOrigin):
            raise TypeError('Expected object of type ScrollOrigin, got: '
                            '{}'.format(type(scroll_origin)))

        self.w3c_actions.wheel_action.scroll(origin=scroll_origin.origin,
                                             x=scroll_origin.x_offset,
                                             y=scroll_origin.y_offset,
                                             delta_x=delta_x,
                                             delta_y=delta_y)
        return self

    def scroll(self, x: int, y: int, delta_x: int, delta_y: int, duration: int = 0, origin: str = "viewport"):
        """
        Sends wheel scroll information to the browser to be processed.

        :Args:
         - x: starting X coordinate
         - y: starting Y coordinate
         - delta_x: the distance the mouse will scroll on the x axis
         - delta_y: the distance the mouse will scroll on the y axis
        """
        warnings.warn(
            "scroll() has been deprecated, please use scroll_to_element(), scroll_by_amount() or scroll_from_origin().",
            DeprecationWarning,
            stacklevel=2
        )

        self.w3c_actions.wheel_action.scroll(x=x, y=y, delta_x=delta_x, delta_y=delta_y,
                                             duration=duration, origin=origin)
        return self

    # Context manager so ActionChains can be used in a 'with .. as' statements.

    def __enter__(self):
        return self  # Return created instance of self.

    def __exit__(self, _type, _value, _traceback):
        pass  # Do nothing, does not require additional cleanup.
<commit_msg>[py]: remove redundant actions array of `action_chains` (#10716)<commit_after># Licensed to the Software Freedom Conservancy (SFC) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The SFC licenses this file
# to you 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.

"""
The ActionChains implementation,
"""
import warnings

from .actions.wheel_input import ScrollOrigin

from .utils import keys_to_typing
from .actions.action_builder import ActionBuilder
from selenium.webdriver.remote.webelement import WebElement

class ActionChains:
    """
    ActionChains are a way to automate low level interactions such as
    mouse movements, mouse button actions, key press, and context menu interactions.
    This is useful for doing more complex actions like hover over and drag and drop.

    Generate user actions.
       When you call methods for actions on the ActionChains object,
       the actions are stored in a queue in the ActionChains object.
       When you call perform(), the events are fired in the order they
       are queued up.

    ActionChains can be used in a chain pattern::

        menu = driver.find_element(By.CSS_SELECTOR, ".nav")
        hidden_submenu = driver.find_element(By.CSS_SELECTOR, ".nav #submenu1")

        ActionChains(driver).move_to_element(menu).click(hidden_submenu).perform()

    Or actions can be queued up one by one, then performed.::

        menu = driver.find_element(By.CSS_SELECTOR, ".nav")
        hidden_submenu = driver.find_element(By.CSS_SELECTOR, ".nav #submenu1")

        actions = ActionChains(driver)
        actions.move_to_element(menu)
        actions.click(hidden_submenu)
        actions.perform()

    Either way, the actions are performed in the order they are called, one after
    another.
    """

    def __init__(self, driver, duration=250):
        """
        Creates a new ActionChains.

        :Args:
         - driver: The WebDriver instance which performs user actions.
         - duration: override the default 250 msecs of DEFAULT_MOVE_DURATION in PointerInput
        """
        self._driver = driver
        self.w3c_actions = ActionBuilder(driver, duration=duration)

    def perform(self):
        """
        Performs all stored actions.
        """
        self.w3c_actions.perform()

    def reset_actions(self):
        """
        Clears actions that are already stored locally and on the remote end
        """
        self.w3c_actions.clear_actions()
        for device in self.w3c_actions.devices:
            device.clear_actions()

    def click(self, on_element=None):
        """
        Clicks an element.

        :Args:
         - on_element: The element to click.
           If None, clicks on current mouse position.
        """
        if on_element:
            self.move_to_element(on_element)

        self.w3c_actions.pointer_action.click()
        self.w3c_actions.key_action.pause()
        self.w3c_actions.key_action.pause()

        return self

    def click_and_hold(self, on_element=None):
        """
        Holds down the left mouse button on an element.

        :Args:
         - on_element: The element to mouse down.
           If None, clicks on current mouse position.
        """
        if on_element:
            self.move_to_element(on_element)

        self.w3c_actions.pointer_action.click_and_hold()
        self.w3c_actions.key_action.pause()

        return self

    def context_click(self, on_element=None):
        """
        Performs a context-click (right click) on an element.

        :Args:
         - on_element: The element to context-click.
           If None, clicks on current mouse position.
        """
        if on_element:
            self.move_to_element(on_element)

        self.w3c_actions.pointer_action.context_click()
        self.w3c_actions.key_action.pause()
        self.w3c_actions.key_action.pause()

        return self

    def double_click(self, on_element=None):
        """
        Double-clicks an element.

        :Args:
         - on_element: The element to double-click.
           If None, clicks on current mouse position.
        """
        if on_element:
            self.move_to_element(on_element)

        self.w3c_actions.pointer_action.double_click()
        for _ in range(4):
            self.w3c_actions.key_action.pause()

        return self

    def drag_and_drop(self, source, target):
        """
        Holds down the left mouse button on the source element,
           then moves to the target element and releases the mouse button.

        :Args:
         - source: The element to mouse down.
         - target: The element to mouse up.
        """
        self.click_and_hold(source)
        self.release(target)
        return self

    def drag_and_drop_by_offset(self, source, xoffset, yoffset):
        """
        Holds down the left mouse button on the source element,
           then moves to the target offset and releases the mouse button.

        :Args:
         - source: The element to mouse down.
         - xoffset: X offset to move to.
         - yoffset: Y offset to move to.
        """
        self.click_and_hold(source)
        self.move_by_offset(xoffset, yoffset)
        self.release()
        return self

    def key_down(self, value, element=None):
        """
        Sends a key press only, without releasing it.
           Should only be used with modifier keys (Control, Alt and Shift).

        :Args:
         - value: The modifier key to send. Values are defined in `Keys` class.
         - element: The element to send keys.
           If None, sends a key to current focused element.

        Example, pressing ctrl+c::

            ActionChains(driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()

        """
        if element:
            self.click(element)

        self.w3c_actions.key_action.key_down(value)
        self.w3c_actions.pointer_action.pause()

        return self

    def key_up(self, value, element=None):
        """
        Releases a modifier key.

        :Args:
         - value: The modifier key to send. Values are defined in Keys class.
         - element: The element to send keys.
           If None, sends a key to current focused element.

        Example, pressing ctrl+c::

            ActionChains(driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()

        """
        if element:
            self.click(element)

        self.w3c_actions.key_action.key_up(value)
        self.w3c_actions.pointer_action.pause()

        return self

    def move_by_offset(self, xoffset, yoffset):
        """
        Moving the mouse to an offset from current mouse position.

        :Args:
         - xoffset: X offset to move to, as a positive or negative integer.
         - yoffset: Y offset to move to, as a positive or negative integer.
        """

        self.w3c_actions.pointer_action.move_by(xoffset, yoffset)
        self.w3c_actions.key_action.pause()

        return self

    def move_to_element(self, to_element):
        """
        Moving the mouse to the middle of an element.

        :Args:
         - to_element: The WebElement to move to.
        """

        self.w3c_actions.pointer_action.move_to(to_element)
        self.w3c_actions.key_action.pause()

        return self

    def move_to_element_with_offset(self, to_element, xoffset, yoffset):
        """
        Move the mouse by an offset of the specified element.
           Offsets are relative to the top-left corner of the element.

        :Args:
         - to_element: The WebElement to move to.
         - xoffset: X offset to move to.
         - yoffset: Y offset to move to.
        """

        self.w3c_actions.pointer_action.move_to(to_element,
                                                int(xoffset),
                                                int(yoffset))
        self.w3c_actions.key_action.pause()

        return self

    def pause(self, seconds):
        """ Pause all inputs for the specified duration in seconds """

        self.w3c_actions.pointer_action.pause(seconds)
        self.w3c_actions.key_action.pause(seconds)

        return self

    def release(self, on_element=None):
        """
        Releasing a held mouse button on an element.

        :Args:
         - on_element: The element to mouse up.
           If None, releases on current mouse position.
        """
        if on_element:
            self.move_to_element(on_element)

        self.w3c_actions.pointer_action.release()
        self.w3c_actions.key_action.pause()

        return self

    def send_keys(self, *keys_to_send):
        """
        Sends keys to current focused element.

        :Args:
         - keys_to_send: The keys to send.  Modifier keys constants can be found in the
           'Keys' class.
        """
        typing = keys_to_typing(keys_to_send)

        for key in typing:
            self.key_down(key)
            self.key_up(key)

        return self

    def send_keys_to_element(self, element, *keys_to_send):
        """
        Sends keys to an element.

        :Args:
         - element: The element to send keys.
         - keys_to_send: The keys to send.  Modifier keys constants can be found in the
           'Keys' class.
        """
        self.click(element)
        self.send_keys(*keys_to_send)
        return self

    def scroll_to_element(self, element: WebElement):
        """
        If the element is outside the viewport, scrolls the bottom of the element to the bottom of the viewport.

        :Args:
         - element: Which element to scroll into the viewport.
        """

        self.w3c_actions.wheel_action.scroll(origin=element)
        return self

    def scroll_by_amount(self, delta_x: int, delta_y: int):
        """
        Scrolls by provided amounts with the origin in the top left corner of the viewport.

        :Args:
         - delta_x: Distance along X axis to scroll using the wheel. A negative value scrolls left.
         - delta_y: Distance along Y axis to scroll using the wheel. A negative value scrolls up.
        """

        self.w3c_actions.wheel_action.scroll(delta_x=delta_x, delta_y=delta_y)
        return self

    def scroll_from_origin(self, scroll_origin: ScrollOrigin, delta_x: int, delta_y: int):
        """
        Scrolls by provided amount based on a provided origin.
        The scroll origin is either the center of an element or the upper left of the viewport plus any offsets.
        If the origin is an element, and the element is not in the viewport, the bottom of the element will first
        be scrolled to the bottom of the viewport.

        :Args:
         - origin: Where scroll originates (viewport or element center) plus provided offsets.
         - delta_x: Distance along X axis to scroll using the wheel. A negative value scrolls left.
         - delta_y: Distance along Y axis to scroll using the wheel. A negative value scrolls up.

         :Raises: If the origin with offset is outside the viewport.
          - MoveTargetOutOfBoundsException - If the origin with offset is outside the viewport.
        """

        if not isinstance(scroll_origin, ScrollOrigin):
            raise TypeError('Expected object of type ScrollOrigin, got: '
                            '{}'.format(type(scroll_origin)))

        self.w3c_actions.wheel_action.scroll(origin=scroll_origin.origin,
                                             x=scroll_origin.x_offset,
                                             y=scroll_origin.y_offset,
                                             delta_x=delta_x,
                                             delta_y=delta_y)
        return self

    def scroll(self, x: int, y: int, delta_x: int, delta_y: int, duration: int = 0, origin: str = "viewport"):
        """
        Sends wheel scroll information to the browser to be processed.

        :Args:
         - x: starting X coordinate
         - y: starting Y coordinate
         - delta_x: the distance the mouse will scroll on the x axis
         - delta_y: the distance the mouse will scroll on the y axis
        """
        warnings.warn(
            "scroll() has been deprecated, please use scroll_to_element(), scroll_by_amount() or scroll_from_origin().",
            DeprecationWarning,
            stacklevel=2
        )

        self.w3c_actions.wheel_action.scroll(x=x, y=y, delta_x=delta_x, delta_y=delta_y,
                                             duration=duration, origin=origin)
        return self

    # Context manager so ActionChains can be used in a 'with .. as' statements.

    def __enter__(self):
        return self  # Return created instance of self.

    def __exit__(self, _type, _value, _traceback):
        pass  # Do nothing, does not require additional cleanup.
<|endoftext|>


<commit_before>#!/usr/bin/env python
# encoding: utf-8

# The MIT License (MIT)

# Copyright (c) 2019-2020 CNRS

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

# AUTHORS
# Hervé BREDIN - http://herve.niderb.fr

"""
Neural building blocks for speaker diarization
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Usage:
  pyannote-audio (sad | scd | ovl | emb | dom) train    [--cpu | --gpu] [options] <root>     <protocol>
  pyannote-audio (sad | scd | ovl | emb | dom) validate [--cpu | --gpu] [options] <train>    <protocol>
  pyannote-audio (sad | scd | ovl | emb | dom) apply    [--cpu | --gpu] [options] <validate> <protocol>
  pyannote-audio -h | --help
  pyannote-audio --version

This command line tool can be used to train, validate, and apply neural networks
for the following blocks of a speaker diarization pipeline:

    * (sad) speech activity detection consists in detecting speech regions in
            an audio recording.
    * (scd) speaker change detection consists in detecting timestamps of
            speaker change point.
    * (ovl) overlapped speech detection consists in detection regions with two
            or more simultaneous speakers.
    * (emb) speaker embedding consists in projecting audio chunk into a
            (usually high-dimensional) vector space where same speaker
            embeddings are close to each other, and different speaker embeddings
            are not.
    * (dom) domain classification consists in predicting the domain of an
            audio recording

Running a complete speech activity detection experiment on the provided
"debug" dataset would go like this:

    * Run experiment on this pyannote.database protocol
      $ export DATABASE=Debug.SpeakerDiarization.Debug

    * This directory will contain experiments artifacts:
      $ mkdir my_experiment && cd my_experiment

    * A unique configuration file describes the experiment hyper-parameters
      (see "Configuration file" below for details):
      $ edit config.yml

    * This will train the model on the training set:
      $ pyannote-audio sad train ${PWD} ${DATABASE}

    * Training artifacts (including model weights) are stored in a sub-directory
      whose name makes it clear which dataset and subset (train, by default)
      were used for training the model.
      $ cd train/${DATABASE}.train

    * This will validate the model on the development set:
      $ pyannote-audio sad validate ${PWD} ${DATABASE}

    * Validation artifacts (including the selection of the best epoch) are
      stored in a sub-directory named after the dataset and subset (development,
      by default) used for validating the model.
      $ cd validate/${DATABASE}.development

    * This will apply the best model (according to the validation step) to the
      test set:
      $ pyannote-audio sad apply ${PWD} ${DATABASE}

    * Inference artifacts are stored in a sub-directory whose name makes it
      clear which epoch has been used (e.g. apply/0125). Artifacts include:
        * raw output of the best model (one numpy array per file  than can be
          loaded with pyannote.audio.features.Precomputed API and handled with
          pyannote.core.SlidingWindowFeature API)
        * (depending on the task) a file "${DATABASE}.test.rttm" containing the
          post-processing of raw output.
        * (depending on the task) a file "${DATABASE}.test.eval" containing the
          evaluation result computed with pyannote.metrics.

pyannote.database support
~~~~~~~~~~~~~~~~~~~~~~~~~

PYANNOTE_DATABASE_CONFIG=

Configuration file <root>/config.yml
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Reproducible research is facilitated by the systematic use of configuration
    files stored in <root>/config.yml in YAML format.

    .......................... <root>/config.yml ..........................
    task:
        name:
        params:

    feature_extraction:
        name:
        params:

    data_augmentation:
        name:
        params:

    architecture:
        name:
        params:

    scheduler:
        name:
        params:

    preprocessors:

    callbacks:
    ...................................................................

    File <root>/config.yml is mandatory, unless option --pretrained is used.

    When fine-tuning a model with option --pretrained=<model>, one can omit it
    and the original <model> configuration file is used instead. If (a possibly
    partial) <root>/config.yml file is provided anyway, it is used to override
    <model> configuration file.

Tensorboard support
~~~~~~~~~~~~~~~~~~~

    A bunch of metrics are logged during training and validation (e.g. loss,
    learning rate, computation time, validation metric). They can be visualized
    using tensorboard:

        $ tensorboard --logdir=<root>

Common options
~~~~~~~~~~~~~~

  <root>                  Experiment root directory. Should contain config.yml
                          configuration file, unless --pretrained option is
                          used (for which config.yml is optional).

  <protocol>              Name of protocol to use for training, validation, or
                          inference. Have a look at pyannote.database
                          documentation for instructions on how to define a
                          protocol with your own dataset:
                          https://github.com/pyannote/pyannote-database#custom-protocols

  <train>                 Path to <root> sub-directory containing training
                          artifacts (e.g. <root>/train/<protocol>.train)

  <validate>              Path to <train> sub-directory containing validation
                          artifacts (e.g. <train>/validate/<protocol>.development)
                          In case option --pretrained=<model> is used, the
                          output of the pretrained model is dumped into the
                          <validate> directory.

  --subset=<subset>       Subset to use for training (resp. validation,
                          inference). Defaults to "train" (resp. "development",
                          "test") for strict enforcement of machine learning
                          good practices.

  --gpu                   Run on GPU. When multiple GPUs are available, use
                          CUDA_AVAILABLE_DEVICES environment variable to force
                          using a specific one. Defaults to using CPU if no GPU
                          is available.

  --cpu                   Run on CPU. Defaults to using GPU when available.

  --debug                 Run using PyTorch's anomaly detection. This will throw
                          an error if a NaN value is produced, and the stacktrace
                          will point to the origin of it. This option can
                          considerably slow execution.

  --from=<epoch>          Start training (resp. validating) at epoch <epoch>.
                          Use --from=last to start from last available epoch at
                          launch time. Not used for inference [default: 0].

  --to=<epoch>            End training (resp. validating) at epoch <epoch>.
                          Use --end=last to validate until last available epoch
                          at launch time. Not used for inference [default: 100].

  --batch=<size>          Set batch size used for validation and inference.
                          Has no effect when training as this parameter should
                          be defined in the configuration file [default: 32].

  --step=<ratio>          Ratio of audio chunk duration used as step between
                          two consecutive audio chunks [default: 0.25]

  --parallel=<n_jobs>     Use at most that many threads for generating training
                          samples or validating files. Defaults to using all
                          CPUs but one.

Speaker embedding
~~~~~~~~~~~~~~~~~

  --duration=<duration>   Use audio chunks with that duration. Defaults to the
                          fixed duration used during training, when available.

   --metric=<metric>      Use this metric (e.g. "cosine" or "euclidean") to
                          compare embeddings. Defaults to the metric defined in
                          <root>/config.yml configuration file.

Pretrained model options
~~~~~~~~~~~~~~~~~~~~~~~~

  --pretrained=<model>    Warm start training with pre-trained model. Can be
                          either a path to an existing checkpoint (e.g.
                          <train>/weights/0050.pt) or the name of a model
                          available in torch.hub.list('pyannote/pyannote.audio')
                          This option can also be used to apply a pretrained
                          model. See description of <validate> for more details.

Validation options
~~~~~~~~~~~~~~~~~~

  --every=<epoch>         Validate model every <epoch> epochs [default: 1].

  --evergreen             Prioritize validation of most recent epoch.

  For speech activity and overlapped speech detection, validation consists in
  looking for the value of the detection threshold that maximizes the f-score
  of recall and precision.

  For speaker change detection, validation consists in looking for the value of
  the peak detection threshold that maximizes the f-score of purity and
  coverage:

  --diarization           Use diarization purity and coverage instead of
                          (default) segmentation purity and coverage.

  For speaker embedding and verification protocols, validation runs the actual
  speaker verification experiment (representing each recording by its average
  embedding) and reports equal error rate.

  For speaker embedding and diarization protocols, validation runs a speaker
  diarization pipeline based on oracle segmentation and "pool-linkage"
  agglomerative clustering of speech turns (represented by their average
  embedding), and looks for the threshold that maximizes the f-score of purity
  and coverage.

"""

import sys
import warnings
from docopt import docopt
from pathlib import Path
import multiprocessing

import torch
from .base import apply_pretrained
from .speech_detection import SpeechActivityDetection
from .change_detection import SpeakerChangeDetection
from .overlap_detection import OverlapDetection
from .speaker_embedding import SpeakerEmbedding
from .domain_classification import DomainClassification

def main():

    # TODO: update version automatically
    arg = docopt(__doc__, version='pyannote-audio 2.0')

    params = {}

    if arg['sad']:
        Application = SpeechActivityDetection

    elif arg['scd']:
        Application = SpeakerChangeDetection

    elif arg['ovl']:
        Application = OverlapDetection

    elif arg['emb']:
        Application = SpeakerEmbedding

    elif arg['dom']:
        Application = DomainClassification

    device = 'cuda' if torch.cuda.is_available() else 'cpu'
    if arg['--gpu'] and device == 'cpu':
        msg = 'No GPU is available. Using CPU instead.'
        warnings.warn(msg)
    if arg['--cpu'] and device == 'cuda':
        device = 'cpu'

    params['device'] = torch.device(device)

    protocol = arg['<protocol>']
    subset = arg['--subset']

    if arg['--debug']:
        msg = 'Debug mode is enabled, this option might slow execution considerably.'
        warnings.warn(msg, RuntimeWarning)
        torch.autograd.set_detect_anomaly(True)

    n_jobs = arg['--parallel']
    if n_jobs is None:
        n_jobs = max(1, multiprocessing.cpu_count() - 1)
    params['n_jobs'] = int(n_jobs)

    if arg['train']:

        params['subset'] = 'train' if subset is None else subset

        # start training at this epoch (defaults to 0, but 'last' is supported)
        warm_start = arg['--from']
        if warm_start != 'last':
            warm_start = int(warm_start)

        # or start from pretrained model
        pretrained = arg['--pretrained']
        pretrained_config_yml = None
        if pretrained is not None:

            # start from an existing model checkpoint
            # (from a different experiment)
            if Path(pretrained).exists():
                warm_start = Path(pretrained)

            else:
                try:
                    warm_start = torch.hub.load(
                        # TODO. change to 'pyannote/pyannote-audio'
                        # after 2.0 release
                        'pyannote/pyannote-audio:develop',
                        pretrained).weights_pt_
                except Exception as e:
                    msg = (
                        f'Could not load "{warm_start}" model from torch.hub.'
                        f'The following exception was raised:\n\n{e}\n\n')
                    sys.exit(msg)

            pretrained_config_yml = warm_start.parents[3] / 'config.yml'

        params['warm_start'] = warm_start

        # stop training at this epoch (defaults to never stop)
        params['epochs'] = int(arg['--to'])

        root_dir = Path(arg['<root>']).expanduser().resolve(strict=True)
        app = Application(root_dir, training=True,
                          pretrained_config_yml=pretrained_config_yml)
        app.train(protocol, **params)

    if arg['validate']:

        train_dir = Path(arg['<train>']).expanduser().resolve(strict=True)
        app = Application.from_train_dir(train_dir, training=False)

        params['subset'] = 'development' if subset is None else subset

        start = arg['--from']
        if start != 'last':
            start = int(start)
        params['start'] = start

        end = arg['--to']
        if end != 'last':
            end = int(end)
        params['end'] = end

        params['every'] = int(arg['--every'])
        params['chronological'] = not arg['--evergreen']
        params['batch_size'] = int(arg['--batch'])

        params['diarization'] = arg['--diarization']

        duration = arg['--duration']
        if duration is None:
            duration = getattr(app.task_, 'duration', None)
            if duration is None:
                msg = ("Task has no 'duration' defined. "
                       "Use '--duration' option to provide one.")
                raise ValueError(msg)
        else:
            duration = float(duration)
        params['duration'] = duration

        params['step'] = float(arg['--step'])

        if arg['emb']:

            metric = arg['--metric']
            if metric is None:
                metric = getattr(app.task_, 'metric', None)
                if metric is None:
                    msg = ("Approach has no 'metric' defined. "
                           "Use '--metric' option to provide one.")
                    raise ValueError(msg)
            params['metric'] = metric

        # FIXME: parallel is broken in pyannote.metrics
        params['n_jobs'] = 1

        app.validate(protocol, **params)

    if arg['apply']:

        validate_dir = Path(arg['<validate>']).expanduser().resolve(strict=True)

        params['subset'] = 'test' if subset is None else subset
        params['batch_size'] = int(arg['--batch'])

        duration = arg['--duration']
        if duration is not None:
            duration = float(duration)
        params['duration'] = duration

        params['step'] = float(arg['--step'])
        params['Pipeline'] = getattr(Application, 'Pipeline', None)

        params['pretrained'] = arg['--pretrained']

        apply_pretrained(validate_dir, protocol, **params)
<commit_msg>doc: fix typo in docopt docstring<commit_after>#!/usr/bin/env python
# encoding: utf-8

# The MIT License (MIT)

# Copyright (c) 2019-2020 CNRS

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

# AUTHORS
# Hervé BREDIN - http://herve.niderb.fr

"""
Neural building blocks for speaker diarization
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Usage:
  pyannote-audio (sad | scd | ovl | emb | dom) train    [--cpu | --gpu] [options] <root>     <protocol>
  pyannote-audio (sad | scd | ovl | emb | dom) validate [--cpu | --gpu] [options] <train>    <protocol>
  pyannote-audio (sad | scd | ovl | emb | dom) apply    [--cpu | --gpu] [options] <validate> <protocol>
  pyannote-audio -h | --help
  pyannote-audio --version

This command line tool can be used to train, validate, and apply neural networks
for the following blocks of a speaker diarization pipeline:

    * (sad) speech activity detection consists in detecting speech regions in
            an audio recording.
    * (scd) speaker change detection consists in detecting timestamps of
            speaker change point.
    * (ovl) overlapped speech detection consists in detection regions with two
            or more simultaneous speakers.
    * (emb) speaker embedding consists in projecting audio chunk into a
            (usually high-dimensional) vector space where same speaker
            embeddings are close to each other, and different speaker embeddings
            are not.
    * (dom) domain classification consists in predicting the domain of an
            audio recording

Running a complete speech activity detection experiment on the provided
"debug" dataset would go like this:

    * Run experiment on this pyannote.database protocol
      $ export DATABASE=Debug.SpeakerDiarization.Debug

    * This directory will contain experiments artifacts:
      $ mkdir my_experiment && cd my_experiment

    * A unique configuration file describes the experiment hyper-parameters
      (see "Configuration file" below for details):
      $ edit config.yml

    * This will train the model on the training set:
      $ pyannote-audio sad train ${PWD} ${DATABASE}

    * Training artifacts (including model weights) are stored in a sub-directory
      whose name makes it clear which dataset and subset (train, by default)
      were used for training the model.
      $ cd train/${DATABASE}.train

    * This will validate the model on the development set:
      $ pyannote-audio sad validate ${PWD} ${DATABASE}

    * Validation artifacts (including the selection of the best epoch) are
      stored in a sub-directory named after the dataset and subset (development,
      by default) used for validating the model.
      $ cd validate/${DATABASE}.development

    * This will apply the best model (according to the validation step) to the
      test set:
      $ pyannote-audio sad apply ${PWD} ${DATABASE}

    * Inference artifacts are stored in a sub-directory whose name makes it
      clear which epoch has been used (e.g. apply/0125). Artifacts include:
        * raw output of the best model (one numpy array per file  than can be
          loaded with pyannote.audio.features.Precomputed API and handled with
          pyannote.core.SlidingWindowFeature API)
        * (depending on the task) a file "${DATABASE}.test.rttm" containing the
          post-processing of raw output.
        * (depending on the task) a file "${DATABASE}.test.eval" containing the
          evaluation result computed with pyannote.metrics.

pyannote.database support
~~~~~~~~~~~~~~~~~~~~~~~~~

PYANNOTE_DATABASE_CONFIG=

Configuration file <root>/config.yml
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Reproducible research is facilitated by the systematic use of configuration
    files stored in <root>/config.yml in YAML format.

    .......................... <root>/config.yml ..........................
    task:
        name:
        params:

    feature_extraction:
        name:
        params:

    data_augmentation:
        name:
        params:

    architecture:
        name:
        params:

    scheduler:
        name:
        params:

    preprocessors:

    callbacks:
    ...................................................................

    File <root>/config.yml is mandatory, unless option --pretrained is used.

    When fine-tuning a model with option --pretrained=<model>, one can omit it
    and the original <model> configuration file is used instead. If (a possibly
    partial) <root>/config.yml file is provided anyway, it is used to override
    <model> configuration file.

Tensorboard support
~~~~~~~~~~~~~~~~~~~

    A bunch of metrics are logged during training and validation (e.g. loss,
    learning rate, computation time, validation metric). They can be visualized
    using tensorboard:

        $ tensorboard --logdir=<root>

Common options
~~~~~~~~~~~~~~

  <root>                  Experiment root directory. Should contain config.yml
                          configuration file, unless --pretrained option is
                          used (for which config.yml is optional).

  <protocol>              Name of protocol to use for training, validation, or
                          inference. Have a look at pyannote.database
                          documentation for instructions on how to define a
                          protocol with your own dataset:
                          https://github.com/pyannote/pyannote-database#custom-protocols

  <train>                 Path to <root> sub-directory containing training
                          artifacts (e.g. <root>/train/<protocol>.train)

  <validate>              Path to <train> sub-directory containing validation
                          artifacts (e.g. <train>/validate/<protocol>.development)
                          In case option --pretrained=<model> is used, the
                          output of the pretrained model is dumped into the
                          <validate> directory.

  --subset=<subset>       Subset to use for training (resp. validation,
                          inference). Defaults to "train" (resp. "development",
                          "test") for strict enforcement of machine learning
                          good practices.

  --gpu                   Run on GPU. When multiple GPUs are available, use
                          CUDA_AVAILABLE_DEVICES environment variable to force
                          using a specific one. Defaults to using CPU if no GPU
                          is available.

  --cpu                   Run on CPU. Defaults to using GPU when available.

  --debug                 Run using PyTorch's anomaly detection. This will throw
                          an error if a NaN value is produced, and the stacktrace
                          will point to the origin of it. This option can
                          considerably slow execution.

  --from=<epoch>          Start training (resp. validating) at epoch <epoch>.
                          Use --from=last to start from last available epoch at
                          launch time. Not used for inference [default: 0].

  --to=<epoch>            End training (resp. validating) at epoch <epoch>.
                          Use --end=last to validate until last available epoch
                          at launch time. Not used for inference [default: 100].

  --batch=<size>          Set batch size used for validation and inference.
                          Has no effect when training as this parameter should
                          be defined in the configuration file [default: 32].

  --step=<ratio>          Ratio of audio chunk duration used as step between
                          two consecutive audio chunks [default: 0.25]

  --parallel=<n_jobs>     Use at most that many threads for generating training
                          samples or validating files. Defaults to using all
                          CPUs but one.

Speaker embedding
~~~~~~~~~~~~~~~~~

  --duration=<duration>   Use audio chunks with that duration. Defaults to the
                          fixed duration used during training, when available.

  --metric=<metric>       Use this metric (e.g. "cosine" or "euclidean") to
                          compare embeddings. Defaults to the metric defined in
                          <root>/config.yml configuration file.

Pretrained model options
~~~~~~~~~~~~~~~~~~~~~~~~

  --pretrained=<model>    Warm start training with pre-trained model. Can be
                          either a path to an existing checkpoint (e.g.
                          <train>/weights/0050.pt) or the name of a model
                          available in torch.hub.list('pyannote/pyannote.audio')
                          This option can also be used to apply a pretrained
                          model. See description of <validate> for more details.

Validation options
~~~~~~~~~~~~~~~~~~

  --every=<epoch>         Validate model every <epoch> epochs [default: 1].

  --evergreen             Prioritize validation of most recent epoch.

  For speech activity and overlapped speech detection, validation consists in
  looking for the value of the detection threshold that maximizes the f-score
  of recall and precision.

  For speaker change detection, validation consists in looking for the value of
  the peak detection threshold that maximizes the f-score of purity and
  coverage:

  --diarization           Use diarization purity and coverage instead of
                          (default) segmentation purity and coverage.

  For speaker embedding and verification protocols, validation runs the actual
  speaker verification experiment (representing each recording by its average
  embedding) and reports equal error rate.

  For speaker embedding and diarization protocols, validation runs a speaker
  diarization pipeline based on oracle segmentation and "pool-linkage"
  agglomerative clustering of speech turns (represented by their average
  embedding), and looks for the threshold that maximizes the f-score of purity
  and coverage.

"""

import sys
import warnings
from docopt import docopt
from pathlib import Path
import multiprocessing

import torch
from .base import apply_pretrained
from .speech_detection import SpeechActivityDetection
from .change_detection import SpeakerChangeDetection
from .overlap_detection import OverlapDetection
from .speaker_embedding import SpeakerEmbedding
from .domain_classification import DomainClassification

def main():

    # TODO: update version automatically
    arg = docopt(__doc__, version='pyannote-audio 2.0')

    params = {}

    if arg['sad']:
        Application = SpeechActivityDetection

    elif arg['scd']:
        Application = SpeakerChangeDetection

    elif arg['ovl']:
        Application = OverlapDetection

    elif arg['emb']:
        Application = SpeakerEmbedding

    elif arg['dom']:
        Application = DomainClassification

    device = 'cuda' if torch.cuda.is_available() else 'cpu'
    if arg['--gpu'] and device == 'cpu':
        msg = 'No GPU is available. Using CPU instead.'
        warnings.warn(msg)
    if arg['--cpu'] and device == 'cuda':
        device = 'cpu'

    params['device'] = torch.device(device)

    protocol = arg['<protocol>']
    subset = arg['--subset']

    if arg['--debug']:
        msg = 'Debug mode is enabled, this option might slow execution considerably.'
        warnings.warn(msg, RuntimeWarning)
        torch.autograd.set_detect_anomaly(True)

    n_jobs = arg['--parallel']
    if n_jobs is None:
        n_jobs = max(1, multiprocessing.cpu_count() - 1)
    params['n_jobs'] = int(n_jobs)

    if arg['train']:

        params['subset'] = 'train' if subset is None else subset

        # start training at this epoch (defaults to 0, but 'last' is supported)
        warm_start = arg['--from']
        if warm_start != 'last':
            warm_start = int(warm_start)

        # or start from pretrained model
        pretrained = arg['--pretrained']
        pretrained_config_yml = None
        if pretrained is not None:

            # start from an existing model checkpoint
            # (from a different experiment)
            if Path(pretrained).exists():
                warm_start = Path(pretrained)

            else:
                try:
                    warm_start = torch.hub.load(
                        # TODO. change to 'pyannote/pyannote-audio'
                        # after 2.0 release
                        'pyannote/pyannote-audio:develop',
                        pretrained).weights_pt_
                except Exception as e:
                    msg = (
                        f'Could not load "{warm_start}" model from torch.hub.'
                        f'The following exception was raised:\n\n{e}\n\n')
                    sys.exit(msg)

            pretrained_config_yml = warm_start.parents[3] / 'config.yml'

        params['warm_start'] = warm_start

        # stop training at this epoch (defaults to never stop)
        params['epochs'] = int(arg['--to'])

        root_dir = Path(arg['<root>']).expanduser().resolve(strict=True)
        app = Application(root_dir, training=True,
                          pretrained_config_yml=pretrained_config_yml)
        app.train(protocol, **params)

    if arg['validate']:

        train_dir = Path(arg['<train>']).expanduser().resolve(strict=True)
        app = Application.from_train_dir(train_dir, training=False)

        params['subset'] = 'development' if subset is None else subset

        start = arg['--from']
        if start != 'last':
            start = int(start)
        params['start'] = start

        end = arg['--to']
        if end != 'last':
            end = int(end)
        params['end'] = end

        params['every'] = int(arg['--every'])
        params['chronological'] = not arg['--evergreen']
        params['batch_size'] = int(arg['--batch'])

        params['diarization'] = arg['--diarization']

        duration = arg['--duration']
        if duration is None:
            duration = getattr(app.task_, 'duration', None)
            if duration is None:
                msg = ("Task has no 'duration' defined. "
                       "Use '--duration' option to provide one.")
                raise ValueError(msg)
        else:
            duration = float(duration)
        params['duration'] = duration

        params['step'] = float(arg['--step'])

        if arg['emb']:

            metric = arg['--metric']
            if metric is None:
                metric = getattr(app.task_, 'metric', None)
                if metric is None:
                    msg = ("Approach has no 'metric' defined. "
                           "Use '--metric' option to provide one.")
                    raise ValueError(msg)
            params['metric'] = metric

        # FIXME: parallel is broken in pyannote.metrics
        params['n_jobs'] = 1

        app.validate(protocol, **params)

    if arg['apply']:

        validate_dir = Path(arg['<validate>']).expanduser().resolve(strict=True)

        params['subset'] = 'test' if subset is None else subset
        params['batch_size'] = int(arg['--batch'])

        duration = arg['--duration']
        if duration is not None:
            duration = float(duration)
        params['duration'] = duration

        params['step'] = float(arg['--step'])
        params['Pipeline'] = getattr(Application, 'Pipeline', None)

        params['pretrained'] = arg['--pretrained']

        apply_pretrained(validate_dir, protocol, **params)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
#!/usr/bin/python
##-------------------------------------------------------------------
## @copyright 2017 DennyZhang.com
## Licensed under MIT
##   https://raw.githubusercontent.com/DennyZhang/devops_public/master/LICENSE
##
## File : cleanup_old_files.py
## Author : Denny <denny@dennyzhang.com>
## Created : <2017-05-03>
## Updated: Time-stamp: <2017-05-15 15:02:15>
## Description :
##    Remove old files in a safe and organized way
## Sample:
##    # Remove files: Check /opt/app and remove files naming "app-.*-SNAPSHOT.jar". But keep latest 2 copies
##    python cleanup_old_files.py --working_dir "/opt/app" --filename_pattern "app-.*-SNAPSHOT.jar" \
##               --cleanup_type file --min_copies 3 --min_size_kb 10240
##
##    # Only list delete candidates, instead of perform the actual changes
##    python cleanup_old_files.py --working_dir "/opt/app" --filename_pattern "app-.*-SNAPSHOT.jar" \
##               --examine_only
##
##    # Remove files: Only cleanup files over 200MB
##    python cleanup_old_files.py --working_dir "/opt/app" --filename_pattern "app-.*-SNAPSHOT.jar" \
##               --cleanup_type file --min_size_kb 204800
##
##    # Remove folders: Cleanup subdirectories, keeping latest 2 directories
##    python cleanup_old_files.py --working_dir "/opt/app" --filename_pattern ".*" --cleanup_type directory
##-------------------------------------------------------------------
import os, sys
import argparse
import re
import shutil

import logging
log_file = "/var/log/%s.log" % (os.path.basename(__file__).rstrip('\.py'))

logging.basicConfig(filename=log_file, level=logging.DEBUG, format='%(asctime)s %(message)s')
logging.getLogger().addHandler(logging.StreamHandler())

def list_old_files(filename_pattern, min_copies, min_size_kb):
    l = []
    files = [f for f in os.listdir(".") if re.search(filename_pattern, f)]
    files.sort(key=os.path.getmtime, reverse=True)
    i = 0
    for f in files:
        if os.path.isfile(f) is False:
            continue
        # skip too small files
        filesize_kb = os.stat(f).st_size/(1000)
        if filesize_kb < min_size_kb:
            continue
        i = i + 1
        if i > min_copies:
            l.append(f)
    return l

def list_old_folders(filename_pattern, min_copies):
    l = []
    files = [f for f in os.listdir(".") if re.search(filename_pattern, f)]
    files.sort(key=os.path.getmtime, reverse=True)
    i = 0
    for f in files:
        if os.path.isdir(f) is False:
            continue
        i = i + 1
        if i > min_copies:
            l.append(f)
    return l

if __name__ == '__main__':
    # get parameters from users
    parser = argparse.ArgumentParser()
    parser.add_argument('--working_dir', required=True, \
                        help="Perform cleanup under which directory", type=str)
    parser.add_argument('--examine_only', dest='examine_only', action='store_true', default=False, \
                        help="Only list delete candidates, instead perform the actual removal")
    parser.add_argument('--filename_pattern', required=False, default=".*", \
                        help="Filter files/directories by filename, before cleanup", type=str)
    parser.add_argument('--cleanup_type', required=False, default='file', \
                        help="Whether to perform the cleanup for files or directories", type=str)
    parser.add_argument('--min_copies', default=3, required=False, \
                        help='minimal copies to keep, before removal.', type=int)
    parser.add_argument('--min_size_kb', default=100, required=False, \
                        help='When remove files, skip files too small. It will be skipped when removing directories', type=int)
    l = parser.parse_args()

    working_dir = l.working_dir
    examine_only = l.examine_only
    cleanup_type = l.cleanup_type.lower()
    filename_pattern = l.filename_pattern
    min_copies = l.min_copies
    min_size_kb = l.min_size_kb

    logging.info("Start to run cleanup for folder: %s." % working_dir)
    if os.path.exists(working_dir) is False:
        logging.warning("Directory(%s) doesn't exists." % (working_dir))
        sys.exit(0)

    os.chdir(working_dir)
    if cleanup_type == 'file':
        l = list_old_files(filename_pattern, min_copies, min_size_kb)
    else:
        l = list_old_folders(filename_pattern, min_copies)

    if l == []:
        logging.info("No matched files/directories to be clean.")
        sys.exit(0)
    else:
        if examine_only is True:
            logging.info("Below files/directories are selected to be removed: %s.\n"\
                         "Skip following removal, since --examine_only has already been given." \
                         % ",".join(l))
            sys.exit(0)
        else:
            # Perform the actual removal
            for f in l:
                if cleanup_type == 'file':
                    logging.info("Remove file. %s/%s" % (working_dir, f))
                else:
                    logging.info("Remove folder: %s/%s" % (working_dir, f))
                # TODO: error handling
                shutil.rmtree(f)
            logging.info("Cleanup is done.")
## File : cleanup_old_files.py ends
<commit_msg>minor change<commit_after># -*- coding: utf-8 -*-
#!/usr/bin/python
##-------------------------------------------------------------------
## @copyright 2017 DennyZhang.com
## Licensed under MIT
##   https://raw.githubusercontent.com/DennyZhang/devops_public/master/LICENSE
##
## File : cleanup_old_files.py
## Author : Denny <denny@dennyzhang.com>
## Created : <2017-05-03>
## Updated: Time-stamp: <2017-05-15 15:03:50>
## Description :
##    Remove old files in a safe and organized way
## Sample:
##    # Remove files: Check /opt/app and remove files naming "app-.*-SNAPSHOT.jar". But keep latest 2 copies
##    python cleanup_old_files.py --working_dir "/opt/app" --filename_pattern "app-.*-SNAPSHOT.jar" \
##               --cleanup_type file --min_copies 3 --min_size_kb 10240
##
##    # Only list delete candidates, instead of perform the actual changes
##    python cleanup_old_files.py --working_dir "/opt/app" --filename_pattern "app-.*-SNAPSHOT.jar" \
##               --examine_only
##
##    # Remove files: Only cleanup files over 200MB
##    python cleanup_old_files.py --working_dir "/opt/app" --filename_pattern "app-.*-SNAPSHOT.jar" \
##               --cleanup_type file --min_size_kb 204800
##
##    # Remove folders: Cleanup subdirectories, keeping latest 2 directories
##    python cleanup_old_files.py --working_dir "/opt/app" --filename_pattern ".*" --cleanup_type directory
##-------------------------------------------------------------------
import os, sys
import argparse
import re
import shutil

import logging
log_file = "/var/log/%s.log" % (os.path.basename(__file__).rstrip('\.py'))

logging.basicConfig(filename=log_file, level=logging.DEBUG, format='%(asctime)s %(message)s')
logging.getLogger().addHandler(logging.StreamHandler())

def list_old_files(filename_pattern, min_copies, min_size_kb):
    l = []
    files = [f for f in os.listdir(".") if re.search(filename_pattern, f)]
    files.sort(key=os.path.getmtime, reverse=True)
    i = 0
    for f in files:
        if os.path.isfile(f) is False:
            continue
        # skip too small files
        filesize_kb = os.stat(f).st_size/1000
        if filesize_kb < min_size_kb:
            continue
        i = i + 1
        if i > min_copies:
            l.append(f)
    return l

def list_old_folders(filename_pattern, min_copies):
    l = []
    files = [f for f in os.listdir(".") if re.search(filename_pattern, f)]
    files.sort(key=os.path.getmtime, reverse=True)
    i = 0
    for f in files:
        if os.path.isdir(f) is False:
            continue
        i = i + 1
        if i > min_copies:
            l.append(f)
    return l

if __name__ == '__main__':
    # get parameters from users
    parser = argparse.ArgumentParser()
    parser.add_argument('--working_dir', required=True, \
                        help="Perform cleanup under which directory", type=str)
    parser.add_argument('--examine_only', dest='examine_only', action='store_true', default=False, \
                        help="Only list delete candidates, instead perform the actual removal")
    parser.add_argument('--filename_pattern', required=False, default=".*", \
                        help="Filter files/directories by filename, before cleanup", type=str)
    parser.add_argument('--cleanup_type', required=False, default='file', \
                        help="Whether to perform the cleanup for files or directories", type=str)
    parser.add_argument('--min_copies', default=3, required=False, \
                        help='minimal copies to keep, before removal.', type=int)
    parser.add_argument('--min_size_kb', default=100, required=False, \
                        help='When remove files, skip files too small. It will be skipped when removing directories', type=int)
    l = parser.parse_args()

    working_dir = l.working_dir
    examine_only = l.examine_only
    cleanup_type = l.cleanup_type.lower()
    filename_pattern = l.filename_pattern
    min_copies = l.min_copies
    min_size_kb = l.min_size_kb

    logging.info("Start to run cleanup for folder: %s." % working_dir)
    if os.path.exists(working_dir) is False:
        logging.warning("Directory(%s) doesn't exists." % (working_dir))
        sys.exit(0)

    os.chdir(working_dir)
    if cleanup_type == 'file':
        l = list_old_files(filename_pattern, min_copies, min_size_kb)
    else:
        l = list_old_folders(filename_pattern, min_copies)

    if l == []:
        logging.info("No matched files/directories to be clean.")
        sys.exit(0)
    else:
        if examine_only is True:
            logging.info("Below files/directories are selected to be removed: %s.\n"\
                         "Skip following removal, since --examine_only has already been given." \
                         % ",".join(l))
            sys.exit(0)
        else:
            # Perform the actual removal
            for f in l:
                if cleanup_type == 'file':
                    logging.info("Remove file. %s/%s" % (working_dir, f))
                else:
                    logging.info("Remove folder: %s/%s" % (working_dir, f))
                # TODO: error handling
                shutil.rmtree(f)
            logging.info("Cleanup is done.")
## File : cleanup_old_files.py ends
<|endoftext|>


<commit_before># ----------------------------------------------------------------------
# Numenta Platform for Intelligent Computing (NuPIC)
# Copyright (C) 2021, Numenta, Inc.  Unless you have an agreement
# with Numenta, Inc., for a separate license for this software code, the
# following terms and conditions apply:
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero Public License version 3 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Affero Public License for more details.
#
# You should have received a copy of the GNU Affero Public License
# along with this program.  If not, see http://www.gnu.org/licenses.
#
# http://numenta.org/licenses/
# ----------------------------------------------------------------------

from nupic.research.frameworks.vernon import (
    SelfSupervisedExperiment as SelfSupervisedExperimentBase,
)
from nupic.research.frameworks.vernon.distributed.experiments.supervised_experiment import (
    SupervisedExperiment as DistributedSupervisedExperiment,
)

__all__ = ["SelfSupervisedExperiment"]

class SelfSupervisedExperiment(
    DistributedSupervisedExperiment, SelfSupervisedExperimentBase
):
    """
    Distributed SelfSupervisedExperiment.
    """

    @classmethod
    def create_unsupervised_sampler(cls, config, dataset):
        return cls.create_train_sampler(config, dataset)

    @classmethod
    def create_supervised_sampler(cls, config, dataset):
        return cls.create_train_sampler(config, dataset)

    @classmethod
    def get_execution_order(cls):
        eo = super().get_execution_order()
        exp = "DistributedSelfSupervisedExperiment"

        eo.update(
            # Overwritten methods
            create_unsupervised_sampler=[exp + ": Create distributed sampler"],
            create_supervised_sampler=[exp + ": Create distributed sampler"],
        )
        return eo
<commit_msg>changed import for checkstyle<commit_after># ----------------------------------------------------------------------
# Numenta Platform for Intelligent Computing (NuPIC)
# Copyright (C) 2021, Numenta, Inc.  Unless you have an agreement
# with Numenta, Inc., for a separate license for this software code, the
# following terms and conditions apply:
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero Public License version 3 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Affero Public License for more details.
#
# You should have received a copy of the GNU Affero Public License
# along with this program.  If not, see http://www.gnu.org/licenses.
#
# http://numenta.org/licenses/
# ----------------------------------------------------------------------

from nupic.research.frameworks.vernon import (
    SelfSupervisedExperiment as SelfSupervisedExperimentBase,
)
from nupic.research.frameworks.vernon.distributed.experiments import (
    SupervisedExperiment as DistributedSupervisedExperiment,
)

__all__ = ["SelfSupervisedExperiment"]

class SelfSupervisedExperiment(
    DistributedSupervisedExperiment, SelfSupervisedExperimentBase
):
    """
    Distributed SelfSupervisedExperiment.
    """

    @classmethod
    def create_unsupervised_sampler(cls, config, dataset):
        return cls.create_train_sampler(config, dataset)

    @classmethod
    def create_supervised_sampler(cls, config, dataset):
        return cls.create_train_sampler(config, dataset)

    @classmethod
    def get_execution_order(cls):
        eo = super().get_execution_order()
        exp = "DistributedSelfSupervisedExperiment"

        eo.update(
            # Overwritten methods
            create_unsupervised_sampler=[exp + ": Create distributed sampler"],
            create_supervised_sampler=[exp + ": Create distributed sampler"],
        )
        return eo
<|endoftext|>


<commit_before>##########################################################################
#
#  Copyright (c) 2012, John Haddon. All rights reserved.
#  Copyright (c) 2013, Image Engine Design Inc. All rights reserved.
#
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions are
#  met:
#
#      * Redistributions of source code must retain the above
#        copyright notice, this list of conditions and the following
#        disclaimer.
#
#      * Redistributions in binary form must reproduce the above
#        copyright notice, this list of conditions and the following
#        disclaimer in the documentation and/or other materials provided with
#        the distribution.
#
#      * Neither the name of John Haddon nor the names of
#        any other contributors to this software may be used to endorse or
#        promote products derived from this software without specific prior
#        written permission.
#
#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
#  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
#  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
#  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
#  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
#  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
#  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
#  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
#  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
#  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
#  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
##########################################################################

import os
import unittest

import IECore

import Gaffer
import GafferImage
import sys

class ConstantTest( unittest.TestCase ) :

	def testDefaultFormatHash( self ) :
		s = Gaffer.ScriptNode()
		n = GafferImage.Constant()
		s.addChild( n )

		with s.context():
			h = n["out"].image().hash()
			n["color"][0].setValue( .5 )
			n["color"][1].setValue( .1 )
			n["color"][2].setValue( .8 )
			h2 = n["out"].image().hash()
			self.assertNotEqual( h, h2 )

	def testColourHash( self ) :
		# Check that the hash changes when the colour does.
		s = Gaffer.ScriptNode()
		n = GafferImage.Constant()
		s.addChild( n )

		with s.context():
			h = n["out"].image().hash()
			n["color"][0].setValue( .5 )
			n["color"][1].setValue( .1 )
			n["color"][2].setValue( .8 )
			h2 = n["out"].image().hash()
			self.assertNotEqual( h, h2 )

	def testFormatHash( self ) :
		# Check that the data hash doesn't change when the format does.
		c = GafferImage.Constant()
		c["format"].setValue( GafferImage.Format( 2048, 1156, 1. ) )
		h1 = c["out"].channelData( "R", IECore.V2i( 0 ) ).hash()
		c["format"].setValue( GafferImage.Format( 1920, 1080, 1. ) )
		h2 = c["out"].channelData( "R", IECore.V2i( 0 ) ).hash()
		self.assertEqual( h1, h2 )

	def testTileHashes( self ) :
		# Test that two tiles within the image have the same hash.
		c = GafferImage.Constant()
		c["format"].setValue( GafferImage.Format( 2048, 1156, 1. ) )
		c["color"][0].setValue( .5 )

		self.assertEqual(
			c["out"].channelDataHash( "R", IECore.V2i( 0 ) ),
			c["out"].channelDataHash( "R", IECore.V2i( GafferImage.ImagePlug().tileSize() ) ),
		)

	def testEnableBehaviour( self ) :

		c = GafferImage.Constant()
		self.assertTrue( c.enabledPlug().isSame( c["enabled"] ) )
		self.assertEqual( c.correspondingInput( c["out"] ), None )
		self.assertEqual( c.correspondingInput( c["color"] ), None )
		self.assertEqual( c.correspondingInput( c["format"] ), None )

	def testChannelNamesHash( self ) :

		c = GafferImage.Constant()
		h1 = c["out"]["channelNames"].hash()
		c["color"].setValue( IECore.Color4f( 1, 0.5, 0.25, 1 ) )
		h2 = c["out"]["channelNames"].hash()

		self.assertEqual( h1, h2 )

	def testSerialisationWithZeroAlpha( self ) :

		s = Gaffer.ScriptNode()
		s["c"] = GafferImage.Constant()
		s["c"]["color"].setValue( IECore.Color4f( 0, 1, 0, 0 ) )

		s2 = Gaffer.ScriptNode()
		s2.execute( s.serialise() )

		self.assertEqual( s2["c"]["color"].getValue(), IECore.Color4f( 0, 1, 0, 0 ) )

if __name__ == "__main__":
	unittest.main()
<commit_msg>ConstantTest : Fixed formatting and removed unneeded imports.<commit_after>##########################################################################
#
#  Copyright (c) 2012, John Haddon. All rights reserved.
#  Copyright (c) 2013, Image Engine Design Inc. All rights reserved.
#
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions are
#  met:
#
#      * Redistributions of source code must retain the above
#        copyright notice, this list of conditions and the following
#        disclaimer.
#
#      * Redistributions in binary form must reproduce the above
#        copyright notice, this list of conditions and the following
#        disclaimer in the documentation and/or other materials provided with
#        the distribution.
#
#      * Neither the name of John Haddon nor the names of
#        any other contributors to this software may be used to endorse or
#        promote products derived from this software without specific prior
#        written permission.
#
#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
#  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
#  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
#  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
#  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
#  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
#  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
#  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
#  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
#  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
#  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
##########################################################################

import unittest

import IECore

import Gaffer
import GafferImage

class ConstantTest( unittest.TestCase ) :

	def testDefaultFormatHash( self ) :

		s = Gaffer.ScriptNode()
		n = GafferImage.Constant()
		s.addChild( n )

		with s.context():
			h = n["out"].image().hash()
			n["color"][0].setValue( .5 )
			n["color"][1].setValue( .1 )
			n["color"][2].setValue( .8 )
			h2 = n["out"].image().hash()
			self.assertNotEqual( h, h2 )

	def testColourHash( self ) :

		# Check that the hash changes when the colour does.
		s = Gaffer.ScriptNode()
		n = GafferImage.Constant()
		s.addChild( n )

		with s.context():
			h = n["out"].image().hash()
			n["color"][0].setValue( .5 )
			n["color"][1].setValue( .1 )
			n["color"][2].setValue( .8 )
			h2 = n["out"].image().hash()
			self.assertNotEqual( h, h2 )

	def testFormatHash( self ) :

		# Check that the data hash doesn't change when the format does.
		c = GafferImage.Constant()
		c["format"].setValue( GafferImage.Format( 2048, 1156, 1. ) )
		h1 = c["out"].channelData( "R", IECore.V2i( 0 ) ).hash()
		c["format"].setValue( GafferImage.Format( 1920, 1080, 1. ) )
		h2 = c["out"].channelData( "R", IECore.V2i( 0 ) ).hash()
		self.assertEqual( h1, h2 )

	def testTileHashes( self ) :

		# Test that two tiles within the image have the same hash.
		c = GafferImage.Constant()
		c["format"].setValue( GafferImage.Format( 2048, 1156, 1. ) )
		c["color"][0].setValue( .5 )

		self.assertEqual(
			c["out"].channelDataHash( "R", IECore.V2i( 0 ) ),
			c["out"].channelDataHash( "R", IECore.V2i( GafferImage.ImagePlug().tileSize() ) ),
		)

	def testEnableBehaviour( self ) :

		c = GafferImage.Constant()
		self.assertTrue( c.enabledPlug().isSame( c["enabled"] ) )
		self.assertEqual( c.correspondingInput( c["out"] ), None )
		self.assertEqual( c.correspondingInput( c["color"] ), None )
		self.assertEqual( c.correspondingInput( c["format"] ), None )

	def testChannelNamesHash( self ) :

		c = GafferImage.Constant()
		h1 = c["out"]["channelNames"].hash()
		c["color"].setValue( IECore.Color4f( 1, 0.5, 0.25, 1 ) )
		h2 = c["out"]["channelNames"].hash()

		self.assertEqual( h1, h2 )

	def testSerialisationWithZeroAlpha( self ) :

		s = Gaffer.ScriptNode()
		s["c"] = GafferImage.Constant()
		s["c"]["color"].setValue( IECore.Color4f( 0, 1, 0, 0 ) )

		s2 = Gaffer.ScriptNode()
		s2.execute( s.serialise() )

		self.assertEqual( s2["c"]["color"].getValue(), IECore.Color4f( 0, 1, 0, 0 ) )

if __name__ == "__main__":
	unittest.main()
<|endoftext|>