<commit_before>from __future__ import absolute_import, division, print_function

import sys

from blaze.expr.scalar import (ScalarSymbol, scalar_coerce, Mult, eval_str,
                               Add, dshape, sin, exprify, cos, isnan, exp, log)
from blaze.compatibility import xfail, basestring, raises
from datetime import date, datetime

x = ScalarSymbol('x')
y = ScalarSymbol('y')

def test_basic():
    expr = (x + y) * 3

    assert eval(str(expr)).isidentical(expr)
    assert expr.isidentical(Mult(Add(ScalarSymbol('x'), ScalarSymbol('y')), 3))

def test_eval_str():
    expr = (x + y) * 3
    assert expr.eval_str() == '(x + y) * 3'

    assert eval_str(1) == '1'
    assert eval_str('Alice') == "'Alice'"

    print(eval_str(-x))
    assert eval_str(-x) == '-x'

def test_str():
    x = ScalarSymbol('x', 'real')

    assert str(x + 10) == 'x + 10'

def ishashable(x):
    try:
        hash(x)
        return True
    except:
        return False

def test_ScalarSymbol_is_hashable():
    assert ishashable(x)

def test_relationals():
    x = ScalarSymbol('x', 'real')
    for expr in [x < 1, x > 1, x == 1, x != 1, x <= 1, x >= 1]:
        assert expr.dshape == dshape('bool')
        assert eval(str(expr)) == expr

def test_numbers():
    x = ScalarSymbol('x', 'real')
    y = ScalarSymbol('x', 'int')
    for expr in [x + 1, x - 1, x * 1, x + y, x - y, x / y, x * y + x + y,
                 x**y, x**2, 2**x, x % 5, -x,
                 sin(x), cos(x ** 2), exp(log(y))]:
        assert expr.dshape == dshape('real')
        assert eval(str(expr)) == expr

    assert (-y).dshape == dshape('int')

@xfail(reason="TODO")
def test_neg_dshape_unsigned():
    y = ScalarSymbol('x', 'uint32')
    assert (-y).dshape == dshape('int32')

@xfail(reason="TODO")
def test_arithmetic_dshape_inference():
    x = ScalarSymbol('x', 'int')
    y = ScalarSymbol('y', 'int')
    assert (x + y).dshape == dshape('int')

def test_date_coercion():
    d = ScalarSymbol('d', 'date')
    expr = d < '2012-01-01'
    assert isinstance(expr.rhs, date)

def test_datetime_coercion():
    d = ScalarSymbol('d', 'datetime')
    expr = d > '2012-01-01T12:30:00'
    assert isinstance(expr.rhs, datetime)

class TestExprify(object):
    dtypes = {'x': 'int', 'y': 'real', 'z': 'int32'}
    x = ScalarSymbol('x', 'int')
    y = ScalarSymbol('y', 'real')
    z = ScalarSymbol('z', 'int32')
    name = ScalarSymbol('name', 'string')

    def test_basic_arithmetic(self):
        assert exprify('x + y', self.dtypes).isidentical(self.x + self.y)

        other = isnan(sin(x) + y)
        assert exprify('isnan(sin(x) + y)', self.dtypes).isidentical(other)

        # parsed as a Num in Python 2 and a UnaryOp in Python 3
        assert exprify('-1', {}) == -1

        # parsed as UnaryOp(op=USub(), operand=1)
        assert exprify('-x', self.dtypes).isidentical(-self.x)

        assert exprify('-x + y', self.dtypes).isidentical(-self.x + self.y)

        other = self.x * self.y + self.z
        assert exprify('x * y + z', self.dtypes).isidentical(other)
        assert exprify('x ** y', self.dtypes).isidentical(self.x ** self.y)

        other = self.x / self.y / self.z + 1
        assert exprify('x / y / z + 1', self.dtypes).isidentical(other)

        other = self.x / self.y % self.z + 2 ** self.y
        assert exprify('x / y % z + 2 ** y', self.dtypes).isidentical(other)

        assert exprify('x // y', self.dtypes).isidentical(self.x // self.y)
        assert exprify('1 // y // x', self.dtypes).isidentical(
            1 // self.y // self.x)

    def test_comparison(self):
        other = (self.x == 1) | (self.x == 2)
        assert exprify('(x == 1) | (x == 2)', self.dtypes).isidentical(other)

    def test_literal_string_compare(self):
        other = self.name == "Alice"
        result = exprify('name == "Alice"', {'name': 'string'})
        assert isinstance(result.rhs, basestring)
        assert result.isidentical(other)

    def test_literal_int_compare(self):
        other = self.x == 1
        result = exprify('x == 1', self.dtypes)
        assert isinstance(result.rhs, int)
        assert result.isidentical(other)

    def test_literal_float_compare(self):
        other = self.y == 1.0
        result = exprify('y == 1.0', self.dtypes)
        assert isinstance(result.rhs, float)
        assert result.isidentical(other)

    def test_failing_exprify(self):
        dtypes = {'x': 'int', 'y': 'real', 'z': 'int32'}

        with raises(AssertionError):
            exprify('x < y < z', dtypes)

        with raises(NotImplementedError):
            exprify('os.listdir()', {})

        with raises(NotImplementedError):
            exprify('os.listdir()', {'os': 'int', 'os.listdir': 'real'})

        with raises(ValueError):
            exprify('__x + __y', {'__x': 'int', '__y': 'real'})

        with raises(NotImplementedError):
            exprify('y if x else y', dtypes)

        with raises(NotImplementedError):
            exprify('lambda x, y: x + y', dtypes)

        if sys.version_info < (2, 7):
            # dict and set comprehensions are not implemented in Python < 2.7
            error = SyntaxError
        else:
            # and we don't allow them in versions that do
            error = NotImplementedError

        with raises(error):
            exprify('{x: y for z in y}', dtypes)

        with raises(error):
            exprify('{x for z in y}', dtypes)

        with raises(NotImplementedError):
            exprify('[x for z in y]', dtypes)

        with raises(NotImplementedError):
            exprify('(x for y in z)', dtypes)

    def test_scope(self):
        dtypes = {'sin': 'int'}
        with raises(ValueError):
            exprify('sin + 1', dtypes)

        with raises(TypeError):
            sin + 1

def test_scalar_coerce():
    assert scalar_coerce('int', 1) == 1
    assert scalar_coerce('int', '1') == 1
    assert scalar_coerce('{x: int}', '1') == 1
    with raises(TypeError):
        scalar_coerce('{x: int, y: int}', '1')
    with raises(TypeError):
        scalar_coerce('3 * int', '1')

    assert scalar_coerce('date', 'Jan 1st, 2012') == date(2012, 1, 1)
    assert scalar_coerce('datetime', 'Jan 1st, 2012 12:00:00') == \
            datetime(2012, 1, 1, 12, 0, 0)

    with raises(ValueError):
        scalar_coerce('date', 'Jan 1st, 2012 12:00:00')
<commit_msg>Modularize tests<commit_after>from __future__ import absolute_import, division, print_function

import sys

from blaze.expr.scalar import (ScalarSymbol, scalar_coerce, Mult, eval_str,
                               Add, dshape, sin, exprify, cos, isnan, exp, log)
from blaze.compatibility import xfail, basestring, raises
from datetime import date, datetime

x = ScalarSymbol('x')
y = ScalarSymbol('y')

def test_basic():
    expr = (x + y) * 3

    assert eval(str(expr)).isidentical(expr)
    assert expr.isidentical(Mult(Add(ScalarSymbol('x'), ScalarSymbol('y')), 3))

def test_eval_str():
    expr = (x + y) * 3
    assert expr.eval_str() == '(x + y) * 3'

    assert eval_str(1) == '1'
    assert eval_str('Alice') == "'Alice'"

    print(eval_str(-x))
    assert eval_str(-x) == '-x'

def test_str():
    x = ScalarSymbol('x', 'real')

    assert str(x + 10) == 'x + 10'

def ishashable(x):
    try:
        hash(x)
        return True
    except:
        return False

def test_ScalarSymbol_is_hashable():
    assert ishashable(x)

def test_relationals():
    x = ScalarSymbol('x', 'real')
    for expr in [x < 1, x > 1, x == 1, x != 1, x <= 1, x >= 1]:
        assert expr.dshape == dshape('bool')
        assert eval(str(expr)) == expr

def test_numbers():
    x = ScalarSymbol('x', 'real')
    y = ScalarSymbol('x', 'int')
    for expr in [x + 1, x - 1, x * 1, x + y, x - y, x / y, x * y + x + y,
                 x**y, x**2, 2**x, x % 5, -x,
                 sin(x), cos(x ** 2), exp(log(y))]:
        assert expr.dshape == dshape('real')
        assert eval(str(expr)) == expr

    assert (-y).dshape == dshape('int')

@xfail(reason="TODO")
def test_neg_dshape_unsigned():
    y = ScalarSymbol('x', 'uint32')
    assert (-y).dshape == dshape('int32')

@xfail(reason="TODO")
def test_arithmetic_dshape_inference():
    x = ScalarSymbol('x', 'int')
    y = ScalarSymbol('y', 'int')
    assert (x + y).dshape == dshape('int')

def test_date_coercion():
    d = ScalarSymbol('d', 'date')
    expr = d < '2012-01-01'
    assert isinstance(expr.rhs, date)

def test_datetime_coercion():
    d = ScalarSymbol('d', 'datetime')
    expr = d > '2012-01-01T12:30:00'
    assert isinstance(expr.rhs, datetime)

class TestExprify(object):
    dtypes = {'x': 'int', 'y': 'real', 'z': 'int32'}
    x = ScalarSymbol('x', 'int')
    y = ScalarSymbol('y', 'real')
    z = ScalarSymbol('z', 'int32')
    name = ScalarSymbol('name', 'string')

    def test_basic_arithmetic(self):
        assert exprify('x + y', self.dtypes).isidentical(self.x + self.y)

        other = isnan(sin(x) + y)
        assert exprify('isnan(sin(x) + y)', self.dtypes).isidentical(other)

        # parsed as a Num in Python 2 and a UnaryOp in Python 3
        assert exprify('-1', {}) == -1

        # parsed as UnaryOp(op=USub(), operand=1)
        assert exprify('-x', self.dtypes).isidentical(-self.x)

        assert exprify('-x + y', self.dtypes).isidentical(-self.x + self.y)

        other = self.x * self.y + self.z
        assert exprify('x * y + z', self.dtypes).isidentical(other)
        assert exprify('x ** y', self.dtypes).isidentical(self.x ** self.y)

        other = self.x / self.y / self.z + 1
        assert exprify('x / y / z + 1', self.dtypes).isidentical(other)

        other = self.x / self.y % self.z + 2 ** self.y
        assert exprify('x / y % z + 2 ** y', self.dtypes).isidentical(other)

        assert exprify('x // y', self.dtypes).isidentical(self.x // self.y)
        assert exprify('1 // y // x', self.dtypes).isidentical(
            1 // self.y // self.x)

    def test_comparison(self):
        other = (self.x == 1) | (self.x == 2)
        assert exprify('(x == 1) | (x == 2)', self.dtypes).isidentical(other)

    def test_literal_string_compare(self):
        other = self.name == "Alice"
        result = exprify('name == "Alice"', {'name': 'string'})
        assert isinstance(result.rhs, basestring)
        assert result.isidentical(other)

    def test_literal_int_compare(self):
        other = self.x == 1
        result = exprify('x == 1', self.dtypes)
        assert isinstance(result.rhs, int)
        assert result.isidentical(other)

    def test_literal_float_compare(self):
        other = self.y == 1.0
        result = exprify('y == 1.0', self.dtypes)
        assert isinstance(result.rhs, float)
        assert result.isidentical(other)

    def test_failing_exprify(self):
        dtypes = self.dtypes

        with raises(AssertionError):
            exprify('x < y < z', dtypes)

        with raises(NotImplementedError):
            exprify('os.listdir()', {})

        with raises(NotImplementedError):
            exprify('os.listdir()', {'os': 'int', 'os.listdir': 'real'})

        with raises(ValueError):
            exprify('__x + __y', {'__x': 'int', '__y': 'real'})

        with raises(NotImplementedError):
            exprify('y if x else y', dtypes)

    def test_functiondef_fail(self):
        dtypes = self.dtypes
        with raises(NotImplementedError):
            exprify('lambda x, y: x + y', dtypes)

        with raises(SyntaxError):
            exprify('def f(x): return x', dtypes={'x': 'int'})

    def test_comprehensions_fail(self):
        dtypes = self.dtypes

        if sys.version_info < (2, 7):
            # dict and set comprehensions are not implemented in Python < 2.7
            error = SyntaxError
        else:
            # and we don't allow them in versions that do
            error = NotImplementedError

        with raises(error):
            exprify('{x: y for z in y}', dtypes)

        with raises(error):
            exprify('{x for z in y}', dtypes)

        with raises(NotImplementedError):
            exprify('[x for z in y]', dtypes)

        with raises(NotImplementedError):
            exprify('(x for y in z)', dtypes)

    def test_boolop_fails(self):
        dtypes = self.dtypes

        with raises(NotImplementedError):
            exprify('x or y', dtypes)

        with raises(NotImplementedError):
            exprify('x and y', dtypes)

        with raises(NotImplementedError):
            exprify('not x', dtypes)

    def test_scope(self):
        dtypes = {'sin': 'int'}
        with raises(ValueError):
            exprify('sin + 1', dtypes)

        with raises(TypeError):
            sin + 1

def test_scalar_coerce():
    assert scalar_coerce('int', 1) == 1
    assert scalar_coerce('int', '1') == 1
    assert scalar_coerce('{x: int}', '1') == 1
    with raises(TypeError):
        scalar_coerce('{x: int, y: int}', '1')
    with raises(TypeError):
        scalar_coerce('3 * int', '1')

    assert scalar_coerce('date', 'Jan 1st, 2012') == date(2012, 1, 1)
    assert (scalar_coerce('datetime', 'Jan 1st, 2012 12:00:00') ==
            datetime(2012, 1, 1, 12, 0, 0))

    with raises(ValueError):
        scalar_coerce('date', 'Jan 1st, 2012 12:00:00')
<|endoftext|>


<commit_before># Copyright (c) 2015, Palo Alto Networks
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

# Author: Brian Torres-Gil <btorres-gil@paloaltonetworks.com>

"""Update users on the firewall from logs in Splunk

About this script
-----------------
User-ID is a mechanism in the firewall that maps users to IP addresses.
These User to IP mappings can be updated using many methods including from
Active Directory or by sending syslogs to a firewall from a Radius or other
authentication server.

Many organizations send authentication logs to Splunk, so it is natural for
Splunk to communicate these authentication events to the firewalls so their
User to IP mappings are always up-to-date.

There are two methods to synchronize authentication events from Splunk to the firewall:

  Method 1:  Forward logs from Splunk to the User-ID firewall.

  Method 2:  Use this script to update the firewall using its API.

Method 1 is preferred because it is more efficient.  However, Method 2 is
useful in cases where the user and the IP are not in the same logs. Splunk
can correlate the user to the IP before passing the mapping to the firewall
via API.

This script supports connection to a firewall or to Panorama.
"""

#########################################################
# Do NOT modify anything below this line unless you are
# certain of the ramifications of the changes
#########################################################

import sys  # for system params and sys.exit()
import os

libpath = os.path.dirname(os.path.abspath(__file__))
sys.path[:0] = [os.path.join(libpath, 'lib')]
import common
import environment

logger = common.logging.getLogger().getChild('panUserUpdate')

try:
    if environment.run_by_splunk():
        import splunk.Intersplunk  # so you can interact with Splunk
        import splunk.entity as entity  # for splunk config info

    libpath = os.path.dirname(os.path.abspath(__file__))
    sys.path[:0] = [os.path.join(libpath, 'lib')]
    sys.path[:0] = [os.path.join(libpath, 'lib', 'pan-python', 'lib')]
    sys.path[:0] = [os.path.join(libpath, 'lib', 'pandevice')]
    import pandevice
    from pandevice.panorama import Panorama
    from pandevice.firewall import Firewall
    import pan.xapi

    from common import log

except Exception as e:
    # Handle exception to produce logs to python.log
    common.exit_with_error(e)

def main_splunk():
    # Get arguments
    args, kwargs = splunk.Intersplunk.getKeywordsAndOptions()

    # Enable debugging by passing 'debug=yes' as an argument of
    # the command on the Splunk searchbar.

    debug = common.check_debug(kwargs)

    # kwargs contains important parameters.
    # parameters from splunk searchbar include:
    #   action
    #   device
    #   panorama
    #   serial
    #   vsys
    #   user_field
    #   ip_field
    #   debug

    # Verify required args were passed to command
    log(debug, "Determining if required arguments are present")
    if 'device' not in kwargs and 'panorama' not in kwargs:
        common.exit_with_error("Missing required command argument: device or panorama", 3)
    if 'panorama' in kwargs and 'serial' not in kwargs:
        common.exit_with_error("Found 'panorama' arguments, but missing 'serial' argument", 3)

    # Assign defaults to fields that aren't specified
    action = kwargs['action'] if 'action' in kwargs else "login"
    vsys = kwargs['vsys'] if 'vsys' in kwargs else "vsys1"
    ip_field = kwargs['ip_field'] if 'ip_field' in kwargs else "src_ip"
    user_field = kwargs['tag_field'] if 'tag_field' in kwargs else "user"

    # Determine if device hostname or serial was provided as argument or should be pulled from entries
    log(debug, "Determining how firewalls should be contacted based on arguments")
    use_panorama = False
    hostname = None
    serial = None
    if "device" in kwargs:
        hostname = kwargs['device']
    elif "panorama" in kwargs:
        use_panorama = True
        hostname = kwargs['panorama']
        serial = kwargs['serial']
    else:
        common.exit_with_error("Missing required command argument: device or panorama", 3)
    log(debug, "Use Panorama: %s" % use_panorama)
    log(debug, "VSys: %s" % vsys)
    log(debug, "Hostname: %s" % hostname)
    if use_panorama and serial is not None:
        log(debug, "Device Serial: %s" % serial)

    # Results contains the data from the search results and settings
    # contains the sessionKey that we can use to talk to Splunk
    results, unused1, settings = splunk.Intersplunk.getOrganizedResults()
    # Get the sessionKey
    sessionKey = settings['sessionKey']

    log(debug, "Begin get API key")
    # Get the API key from the Splunk store or from the device at hostname if no apikey is stored
    apikey = common.apikey(sessionKey, hostname, debug)

    # Create the connection to the firewall or Panorama
    panorama = None
    if use_panorama:
        # For Panorama, create the Panorama object, and the firewall object
        panorama = Panorama(hostname, api_key=apikey)
        firewall = Firewall(panorama=panorama, serial=serial, vsys=vsys)
        firewall.userid.batch_start()
    else:
        # No Panorama, so just create the firewall object
        firewall = Firewall(hostname, api_key=apikey, vsys=vsys)
        firewall.userid.batch_start()

    # Collect all the ip addresses and tags into firewall batch requests
    for result in results:

        ## Find the tag (if a tag_field was specified)

        try:
            this_user = result[user_field]
        except KeyError as e:
            result['status'] = "ERROR: Unable to determine user from field: %s" % user_field
            continue

        ## Find the IP

        try:
            this_ip = result[ip_field]
        except KeyError as e:
            result['status'] = "ERROR: Unable to determine ip from field: %s" % ip_field

        ## Create a request in the batch user-id update for the firewall
        ## No API call to the firewall happens until all batch requests are created.

        if action == "login":
            log(debug, "Login event on firewall %s: %s - %s" % (firewall, this_ip, this_user))
            firewall.userid.login(this_user, this_ip)
        else:
            log(debug, "Logout event on firewall %s: %s - %s" % (firewall, this_ip, this_user))
            firewall.userid.logout(this_user, this_ip)

        result['status'] = "Submitted successfully"

    ## Make the API calls to the User-ID API of each firewall

    try:
        firewall.userid.batch_end()

    except pan.xapi.PanXapiError as e:
        common.exit_with_error(str(e))

    except Exception as e:
        common.exit_with_error(str(e))

    # output results
    splunk.Intersplunk.outputResults(results)

def main_cli():
    raise NotImplementedError

if __name__ == "__main__":
    if environment.run_by_splunk():
        try:
            main_splunk()
        except Exception as e:
            common.exit_with_error(e)
    else:
        main_cli()
<commit_msg>Change tag to user in panUserUpdate.py.  Fixes #16<commit_after># Copyright (c) 2015, Palo Alto Networks
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

# Author: Brian Torres-Gil <btorres-gil@paloaltonetworks.com>

"""Update users on the firewall from logs in Splunk

About this script
-----------------
User-ID is a mechanism in the firewall that maps users to IP addresses.
These User to IP mappings can be updated using many methods including from
Active Directory or by sending syslogs to a firewall from a Radius or other
authentication server.

Many organizations send authentication logs to Splunk, so it is natural for
Splunk to communicate these authentication events to the firewalls so their
User to IP mappings are always up-to-date.

There are two methods to synchronize authentication events from Splunk to the firewall:

  Method 1:  Forward logs from Splunk to the User-ID firewall.

  Method 2:  Use this script to update the firewall using its API.

Method 1 is preferred because it is more efficient.  However, Method 2 is
useful in cases where the user and the IP are not in the same logs. Splunk
can correlate the user to the IP before passing the mapping to the firewall
via API.

This script supports connection to a firewall or to Panorama.
"""

#########################################################
# Do NOT modify anything below this line unless you are
# certain of the ramifications of the changes
#########################################################

import sys  # for system params and sys.exit()
import os

libpath = os.path.dirname(os.path.abspath(__file__))
sys.path[:0] = [os.path.join(libpath, 'lib')]
import common
import environment

logger = common.logging.getLogger().getChild('panUserUpdate')

try:
    if environment.run_by_splunk():
        import splunk.Intersplunk  # so you can interact with Splunk
        import splunk.entity as entity  # for splunk config info

    libpath = os.path.dirname(os.path.abspath(__file__))
    sys.path[:0] = [os.path.join(libpath, 'lib')]
    sys.path[:0] = [os.path.join(libpath, 'lib', 'pan-python', 'lib')]
    sys.path[:0] = [os.path.join(libpath, 'lib', 'pandevice')]
    import pandevice
    from pandevice.panorama import Panorama
    from pandevice.firewall import Firewall
    import pan.xapi

    from common import log

except Exception as e:
    # Handle exception to produce logs to python.log
    common.exit_with_error(e)

def main_splunk():
    # Get arguments
    args, kwargs = splunk.Intersplunk.getKeywordsAndOptions()

    # Enable debugging by passing 'debug=yes' as an argument of
    # the command on the Splunk searchbar.

    debug = common.check_debug(kwargs)

    # kwargs contains important parameters.
    # parameters from splunk searchbar include:
    #   action
    #   device
    #   panorama
    #   serial
    #   vsys
    #   user_field
    #   ip_field
    #   debug

    # Verify required args were passed to command
    log(debug, "Determining if required arguments are present")
    if 'device' not in kwargs and 'panorama' not in kwargs:
        common.exit_with_error("Missing required command argument: device or panorama", 3)
    if 'panorama' in kwargs and 'serial' not in kwargs:
        common.exit_with_error("Found 'panorama' arguments, but missing 'serial' argument", 3)

    # Assign defaults to fields that aren't specified
    action = kwargs['action'] if 'action' in kwargs else "login"
    vsys = kwargs['vsys'] if 'vsys' in kwargs else "vsys1"
    ip_field = kwargs['ip_field'] if 'ip_field' in kwargs else "src_ip"
    user_field = kwargs['user_field'] if 'user_field' in kwargs else "user"

    # Determine if device hostname or serial was provided as argument or should be pulled from entries
    log(debug, "Determining how firewalls should be contacted based on arguments")
    use_panorama = False
    hostname = None
    serial = None
    if "device" in kwargs:
        hostname = kwargs['device']
    elif "panorama" in kwargs:
        use_panorama = True
        hostname = kwargs['panorama']
        serial = kwargs['serial']
    else:
        common.exit_with_error("Missing required command argument: device or panorama", 3)
    log(debug, "Use Panorama: %s" % use_panorama)
    log(debug, "VSys: %s" % vsys)
    log(debug, "Hostname: %s" % hostname)
    if use_panorama and serial is not None:
        log(debug, "Device Serial: %s" % serial)

    # Results contains the data from the search results and settings
    # contains the sessionKey that we can use to talk to Splunk
    results, unused1, settings = splunk.Intersplunk.getOrganizedResults()
    # Get the sessionKey
    sessionKey = settings['sessionKey']

    log(debug, "Begin get API key")
    # Get the API key from the Splunk store or from the device at hostname if no apikey is stored
    apikey = common.apikey(sessionKey, hostname, debug)

    # Create the connection to the firewall or Panorama
    panorama = None
    if use_panorama:
        # For Panorama, create the Panorama object, and the firewall object
        panorama = Panorama(hostname, api_key=apikey)
        firewall = Firewall(panorama=panorama, serial=serial, vsys=vsys)
        firewall.userid.batch_start()
    else:
        # No Panorama, so just create the firewall object
        firewall = Firewall(hostname, api_key=apikey, vsys=vsys)
        firewall.userid.batch_start()

    # Collect all the ip addresses and users into firewall batch requests
    for result in results:

        ## Find the user (if a user_field was specified)

        try:
            this_user = result[user_field]
        except KeyError as e:
            result['status'] = "ERROR: Unable to determine user from field: %s" % user_field
            continue

        ## Find the IP

        try:
            this_ip = result[ip_field]
        except KeyError as e:
            result['status'] = "ERROR: Unable to determine ip from field: %s" % ip_field

        ## Create a request in the batch user-id update for the firewall
        ## No API call to the firewall happens until all batch requests are created.

        if action == "login":
            log(debug, "Login event on firewall %s: %s - %s" % (firewall, this_ip, this_user))
            firewall.userid.login(this_user, this_ip)
        else:
            log(debug, "Logout event on firewall %s: %s - %s" % (firewall, this_ip, this_user))
            firewall.userid.logout(this_user, this_ip)

        result['status'] = "Submitted successfully"

    ## Make the API calls to the User-ID API of each firewall

    try:
        firewall.userid.batch_end()

    except pan.xapi.PanXapiError as e:
        common.exit_with_error(str(e))

    except Exception as e:
        common.exit_with_error(str(e))

    # output results
    splunk.Intersplunk.outputResults(results)

def main_cli():
    raise NotImplementedError

if __name__ == "__main__":
    if environment.run_by_splunk():
        try:
            main_splunk()
        except Exception as e:
            common.exit_with_error(e)
    else:
        main_cli()
<|endoftext|>


<commit_before>"""Msgpack handling in the event loop pipeline."""
import logging

from msgpack import Packer, Unpacker

from ..compat import unicode_errors_default

logger = logging.getLogger(__name__)
debug, info, warn = (logger.debug, logger.info, logger.warning,)

class MsgpackStream(object):

    """Two-way msgpack stream that wraps a event loop byte stream.

    This wraps the event loop interface for reading/writing bytes and
    exposes an interface for reading/writing msgpack documents.
    """

    def __init__(self, event_loop):
        """Wrap `event_loop` on a msgpack-aware interface."""
        self.loop = event_loop
        self._packer = Packer(unicode_errors=unicode_errors_default)
        self._unpacker = Unpacker()
        self._message_cb = None

    def threadsafe_call(self, fn):
        """Wrapper around `BaseEventLoop.threadsafe_call`."""
        self.loop.threadsafe_call(fn)

    def send(self, msg):
        """Queue `msg` for sending to Nvim."""
        debug('sent %s', msg)
        self.loop.send(self._packer.pack(msg))

    def run(self, message_cb):
        """Run the event loop to receive messages from Nvim.

        While the event loop is running, `message_cb` will be called whenever
        a message has been successfully parsed from the input stream.
        """
        self._message_cb = message_cb
        self.loop.run(self._on_data)
        self._message_cb = None

    def stop(self):
        """Stop the event loop."""
        self.loop.stop()

    def close(self):
        """Close the event loop."""
        self.loop.close()

    def _on_data(self, data):
        self._unpacker.feed(data)
        while True:
            try:
                debug('waiting for message...')
                msg = next(self._unpacker)
                debug('received message: %s', msg)
                self._message_cb(msg)
            except StopIteration:
                debug('unpacker needs more data...')
                break
<commit_msg>Set unicode_errors in Unpacker<commit_after>"""Msgpack handling in the event loop pipeline."""
import logging

from msgpack import Packer, Unpacker

from ..compat import unicode_errors_default

logger = logging.getLogger(__name__)
debug, info, warn = (logger.debug, logger.info, logger.warning,)

class MsgpackStream(object):

    """Two-way msgpack stream that wraps a event loop byte stream.

    This wraps the event loop interface for reading/writing bytes and
    exposes an interface for reading/writing msgpack documents.
    """

    def __init__(self, event_loop):
        """Wrap `event_loop` on a msgpack-aware interface."""
        self.loop = event_loop
        self._packer = Packer(unicode_errors=unicode_errors_default)
        self._unpacker = Unpacker(unicode_errors=unicode_errors_default)
        self._message_cb = None

    def threadsafe_call(self, fn):
        """Wrapper around `BaseEventLoop.threadsafe_call`."""
        self.loop.threadsafe_call(fn)

    def send(self, msg):
        """Queue `msg` for sending to Nvim."""
        debug('sent %s', msg)
        self.loop.send(self._packer.pack(msg))

    def run(self, message_cb):
        """Run the event loop to receive messages from Nvim.

        While the event loop is running, `message_cb` will be called whenever
        a message has been successfully parsed from the input stream.
        """
        self._message_cb = message_cb
        self.loop.run(self._on_data)
        self._message_cb = None

    def stop(self):
        """Stop the event loop."""
        self.loop.stop()

    def close(self):
        """Close the event loop."""
        self.loop.close()

    def _on_data(self, data):
        self._unpacker.feed(data)
        while True:
            try:
                debug('waiting for message...')
                msg = next(self._unpacker)
                debug('received message: %s', msg)
                self._message_cb(msg)
            except StopIteration:
                debug('unpacker needs more data...')
                break
<|endoftext|>


<commit_before>"""Test that importing modules in C works as expected."""

from __future__ import print_function

from distutils.version import StrictVersion
import os, time
import platform

import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil

class CModulesTestCase(TestBase):

    mydir = TestBase.compute_mydir(__file__)

    @skipIfFreeBSD
    @expectedFailureDarwin('http://llvm.org/pr24302')
    @expectedFailureAll(oslist=["linux"], bugnumber="http://llvm.org/pr23456 'fopen' has unknown return type")
    @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24489: Name lookup not working correctly on Windows")
    def test_expr(self):
        if platform.system() == "Darwin" and platform.release() < StrictVersion('12.0.0'):
            self.skipTest()

        self.build()
        exe = os.path.join(os.getcwd(), "a.out")
        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)

        # Break inside the foo function which takes a bar_ptr argument.
        lldbutil.run_break_set_by_file_and_line (self, "main.c", self.line, num_expected_locations=1, loc_exact=True)

        self.runCmd("run", RUN_SUCCEEDED)

        # The stop reason of the thread should be breakpoint.
        self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
            substrs = ['stopped',
                       'stop reason = breakpoint'])

        # The breakpoint should have a hit count of 1.
        self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE,
            substrs = [' resolved, hit count = 1'])

        self.expect("expr @import Darwin; 3", VARIABLES_DISPLAYED_CORRECTLY,
            substrs = ["int", "3"])

        self.expect("expr *fopen(\"/dev/zero\", \"w\")", VARIABLES_DISPLAYED_CORRECTLY,
            substrs = ["FILE", "_close", "__sclose"])

        self.expect("expr *myFile", VARIABLES_DISPLAYED_CORRECTLY,
            substrs = ["a", "5", "b", "9"])

        self.expect("expr MIN((uint64_t)2, (uint64_t)3)", VARIABLES_DISPLAYED_CORRECTLY,
            substrs = ["uint64_t", "2"])

        self.expect("expr stdin", VARIABLES_DISPLAYED_CORRECTLY,
            substrs = ["(FILE *)", "0x"])

    def setUp(self):
        # Call super's setUp().
        TestBase.setUp(self)
        # Find the line number to break inside main().
        self.line = line_number('main.c', '// Set breakpoint 0 here.')
<commit_msg>Fixed and re-enabled the Clang modules testcase.<commit_after>"""Test that importing modules in C works as expected."""

from __future__ import print_function

from distutils.version import StrictVersion
import os, time
import platform

import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil

class CModulesTestCase(TestBase):

    mydir = TestBase.compute_mydir(__file__)

    @skipIfFreeBSD
    @expectedFailureAll(oslist=["linux"], bugnumber="http://llvm.org/pr23456 'fopen' has unknown return type")
    @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24489: Name lookup not working correctly on Windows")
    def test_expr(self):
        if platform.system() == "Darwin" and platform.release() < StrictVersion('12.0.0'):
            self.skipTest()

        self.build()
        exe = os.path.join(os.getcwd(), "a.out")
        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)

        # Break inside the foo function which takes a bar_ptr argument.
        lldbutil.run_break_set_by_file_and_line (self, "main.c", self.line, num_expected_locations=1, loc_exact=True)

        self.runCmd("run", RUN_SUCCEEDED)

        # The stop reason of the thread should be breakpoint.
        self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
            substrs = ['stopped',
                       'stop reason = breakpoint'])

        # The breakpoint should have a hit count of 1.
        self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE,
            substrs = [' resolved, hit count = 1'])

        self.expect("expr -l objc++ -- @import Darwin; 3", VARIABLES_DISPLAYED_CORRECTLY,
            substrs = ["int", "3"])

        self.expect("expr *fopen(\"/dev/zero\", \"w\")", VARIABLES_DISPLAYED_CORRECTLY,
            substrs = ["FILE", "_close"])

        self.expect("expr *myFile", VARIABLES_DISPLAYED_CORRECTLY,
            substrs = ["a", "5", "b", "9"])

        self.expect("expr MIN((uint64_t)2, (uint64_t)3)", VARIABLES_DISPLAYED_CORRECTLY,
            substrs = ["uint64_t", "2"])

        self.expect("expr stdin", VARIABLES_DISPLAYED_CORRECTLY,
            substrs = ["(FILE *)", "0x"])

    def setUp(self):
        # Call super's setUp().
        TestBase.setUp(self)
        # Find the line number to break inside main().
        self.line = line_number('main.c', '// Set breakpoint 0 here.')
<|endoftext|>


<commit_before>""" Tools for doing common subexpression elimination.
"""
from __future__ import print_function, division

import difflib

from sympy.core import Basic, Mul, Add, Pow, sympify, Tuple
from sympy.core.singleton import S
from sympy.core.basic import preorder_traversal
from sympy.core.function import _coeff_isneg
from sympy.core.compatibility import iterable, xrange
from sympy.utilities.iterables import numbered_symbols, \
    sift, topological_sort, ordered

from . import cse_opts

# (preprocessor, postprocessor) pairs which are commonly useful. They should
# each take a sympy expression and return a possibly transformed expression.
# When used in the function ``cse()``, the target expressions will be transformed
# by each of the preprocessor functions in order. After the common
# subexpressions are eliminated, each resulting expression will have the
# postprocessor functions transform them in *reverse* order in order to undo the
# transformation if necessary. This allows the algorithm to operate on
# a representation of the expressions that allows for more optimization
# opportunities.
# ``None`` can be used to specify no transformation for either the preprocessor or
# postprocessor.

cse_optimizations = list(cse_opts.default_optimizations)

# sometimes we want the output in a different format; non-trivial
# transformations can be put here for users
# ===============================================================

def reps_toposort(r):
    """Sort replacements `r` so (k1, v1) appears before (k2, v2)
    if k2 is in v1's free symbols. This orders items in the
    way that cse returns its results (hence, in order to use the
    replacements in a substitution option it would make sense
    to reverse the order).

    Examples
    ========
    >>> from sympy.simplify.cse_main import reps_toposort
    >>> from sympy.abc import x, y
    >>> from sympy import Eq
    >>> for l, r in reps_toposort([(x, y + 1), (y, 2)]):
    ...     print(Eq(l, r))
    ...
    y == 2
    x == y + 1

    """
    r = sympify(r)
    E = []
    for c1, (k1, v1) in enumerate(r):
        for c2, (k2, v2) in enumerate(r):
            if k1 in v2.free_symbols:
                E.append((c1, c2))
    return [r[i] for i in topological_sort((range(len(r)), E))]

def cse_separate(r, e):
    """Move expressions that are in the form (symbol, expr) out of the
    expressions and sort them into the replacements using the reps_toposort.

    Examples
    ========
    >>> from sympy.simplify.cse_main import cse_separate
    >>> from sympy.abc import x, y, z
    >>> from sympy import cos, exp, cse, Eq, symbols
    >>> x0, x1 = symbols('x:2')
    >>> eq = (x + 1 + exp((x + 1)/(y + 1)) + cos(y + 1))
    >>> cse([eq, Eq(x, z + 1), z - 2], postprocess=cse_separate) in [
    ... [[(x0, y + 1), (x, z + 1), (x1, x + 1)],
    ...  [x1 + exp(x1/x0) + cos(x0), z - 2]],
    ... [[(x1, y + 1), (x, z + 1), (x0, x + 1)],
    ...  [x0 + exp(x0/x1) + cos(x1), z - 2]]]
    ...
    True
    """
    d = sift(e, lambda w: w.is_Equality and w.lhs.is_Symbol)
    r = r + [w.args for w in d[True]]
    e = d[False]
    return [reps_toposort(r), e]

# ====end of cse postprocess idioms===========================

def preprocess_for_cse(expr, optimizations):
    """ Preprocess an expression to optimize for common subexpression
    elimination.

    Parameters
    ----------
    expr : sympy expression
        The target expression to optimize.
    optimizations : list of (callable, callable) pairs
        The (preprocessor, postprocessor) pairs.

    Returns
    -------
    expr : sympy expression
        The transformed expression.
    """
    for pre, post in optimizations:
        if pre is not None:
            expr = pre(expr)
    return expr

def postprocess_for_cse(expr, optimizations):
    """ Postprocess an expression after common subexpression elimination to
    return the expression to canonical sympy form.

    Parameters
    ----------
    expr : sympy expression
        The target expression to transform.
    optimizations : list of (callable, callable) pairs, optional
        The (preprocessor, postprocessor) pairs.  The postprocessors will be
        applied in reversed order to undo the effects of the preprocessors
        correctly.

    Returns
    -------
    expr : sympy expression
        The transformed expression.
    """
    if optimizations is None:
        optimizations = cse_optimizations
    for pre, post in reversed(optimizations):
        if post is not None:
            expr = post(expr)
    return expr

def opt_cse(exprs):
    """Find optimization opportunities in Adds, Muls, Pows and negative
    coefficient Muls"""
    from sympy.matrices import Matrix

    opt_subs = dict()

    adds = set()
    muls = set()

    seen_subexp = set()
    def _find_opts(expr):

        if expr.is_Atom:
           return

        if iterable(expr):
            list(map(_find_opts, expr))
            return

        if expr in seen_subexp:
            return expr
        seen_subexp.add(expr)

        list(map(_find_opts, expr.args))

        if _coeff_isneg(expr):
            neg_expr = -expr
            if not neg_expr.is_Atom:
                opt_subs[expr] = Mul(S.NegativeOne, neg_expr, evaluate=False)
                seen_subexp.add(neg_expr)
                expr = neg_expr

        if expr.is_Mul:
            muls.add(expr)

        elif expr.is_Add:
            adds.add(expr)

        elif expr.is_Pow:
            if _coeff_isneg(expr.exp):
                opt_subs[expr] = Pow(Pow(expr.base, -expr.exp), S.NegativeOne, evaluate=False)

    for e in exprs:
        if isinstance(e, Basic):
            _find_opts(e)

    ## Process Adds and commutative Muls

    def _match_common_args(Func, funcs):
        funcs = list(ordered(funcs))

        func_args = [set(e.args) for e in funcs]
        for i in xrange(len(func_args)):
            for j in xrange(i + 1, len(func_args)):
                com_args = func_args[i].intersection(func_args[j])
                if len(com_args) > 1:
                    com_func = Func(*com_args)

                    # for all sets, replace the common symbols by the function over them,
                    # to allow recursive matches

                    diff_i = func_args[i].difference(com_args)
                    func_args[i] = diff_i | set([com_func])
                    if diff_i:
                        opt_subs[funcs[i]] = Func(Func(*diff_i), com_func, evaluate=False)

                    diff_j = func_args[j].difference(com_args)
                    func_args[j] = diff_j | set([com_func])
                    opt_subs[funcs[j]] = Func(Func(*diff_j), com_func, evaluate=False)

                    for k in xrange(j + 1, len(func_args)):
                        if not com_args.difference(func_args[k]):
                            diff_k = func_args[k].difference(com_args)
                            func_args[k] = diff_k | set([com_func])
                            opt_subs[funcs[k]] = Func(Func(*diff_k), com_func, evaluate=False)

    # split muls into commutative
    comutative_muls = set()
    for m in muls:
        c, nc = m.args_cnc(cset=True)
        if c:
            c_mul = Mul(*c)
            if nc:
                opt_subs[m] = Mul(c_mul, Mul(*nc), evaluate=False)
            if len(c) > 1:
                comutative_muls.add(c_mul)

    _match_common_args(Add, adds)
    _match_common_args(Mul, comutative_muls)

    return opt_subs

def tree_cse(exprs, symbols, opt_subs=None):
    """Perform blind CSE on expression tree, taking opt_subs into account"""
    from sympy.matrices import Matrix

    if opt_subs is None:
        opt_subs = dict()

    ## Find repeated sub-expressions

    to_eliminate = set()

    seen_subexp = set()
    def _find_repeated(expr):
        if expr.is_Atom:
           return

        if iterable(expr):
            args = expr

        else:
            if expr in seen_subexp:
                to_eliminate.add(expr)
                return

            seen_subexp.add(expr)

            if expr in opt_subs:
                expr = opt_subs[expr]

            args = expr.args

        list(map(_find_repeated, args))

    for e in exprs:
        if isinstance(e, Basic):
            _find_repeated(e)

    ## Rebuild tree

    replacements = []

    subs = dict()
    def _rebuild(expr):

        if expr.is_Atom:
            return expr

        if iterable(expr):
            new_args = [_rebuild(arg) for arg in expr]
            return type(expr)(*new_args)

        if expr in subs:
            return subs[expr]

        orig_expr = expr
        if expr in opt_subs:
            expr = opt_subs[expr]

        Func, args = type(expr), expr.args

        # Parse Muls and Adds arguments by order
        if Func is Mul:
            c, nc = expr.args_cnc()
            args = list(ordered(c)) + nc
        elif Func is Add:
            args = list(ordered(args))

        new_args = list(map(_rebuild, args))
        if new_args != args:
            new_expr = Func(*new_args)
        else:
            new_expr = expr

        if orig_expr in to_eliminate:
            sym = next(symbols)
            subs[orig_expr] = sym
            replacements.append((sym, new_expr))
            return sym

        else:
            return new_expr

    reduced_exprs = []
    for e in exprs:
        if isinstance(e, Basic):
            reduced_e =_rebuild(e)
        else:
            reduced_e = e
        reduced_exprs.append(reduced_e)

    return replacements, reduced_exprs

def cse(exprs, symbols=None, optimizations=None, postprocess=None):
    """ Perform common subexpression elimination on an expression.

    Parameters
    ==========

    exprs : list of sympy expressions, or a single sympy expression
        The expressions to reduce.
    symbols : infinite iterator yielding unique Symbols
        The symbols used to label the common subexpressions which are pulled
        out. The ``numbered_symbols`` generator is useful. The default is a
        stream of symbols of the form "x0", "x1", etc. This must be an infinite
        iterator.
    optimizations : list of (callable, callable) pairs, optional
        The (preprocessor, postprocessor) pairs. If not provided,
        ``sympy.simplify.cse.cse_optimizations`` is used.
    postprocess : a function which accepts the two return values of cse and
        returns the desired form of output from cse, e.g. if you want the
        replacements reversed the function might be the following lambda:
        lambda r, e: return reversed(r), e

    Returns
    =======

    replacements : list of (Symbol, expression) pairs
        All of the common subexpressions that were replaced. Subexpressions
        earlier in this list might show up in subexpressions later in this list.
    reduced_exprs : list of sympy expressions
        The reduced expressions with all of the replacements above.
    """
    from sympy.matrices import Matrix

    if symbols is None:
        symbols = numbered_symbols()
    else:
        # In case we get passed an iterable with an __iter__ method instead of
        # an actual iterator.
        symbols = iter(symbols)

    if optimizations is None:
        # Pull out the default here just in case there are some weird
        # manipulations of the module-level list in some other thread.
        optimizations = list(cse_optimizations)

    # Handle the case if just one expression was passed.
    if isinstance(exprs, Basic):
        exprs = [exprs]

    # Preprocess the expressions to give us better optimization opportunities.
    reduced_exprs = [preprocess_for_cse(e, optimizations) for e in exprs]

    # Find other optimization opportunities.
    opt_subs = opt_cse(reduced_exprs)

    # Main CSE algorithm.
    replacements, reduced_exprs = tree_cse(reduced_exprs, symbols, opt_subs)

    # Postprocess the expressions to return the expressions to canonical form.
    for i, (sym, subtree) in enumerate(replacements):
        subtree = postprocess_for_cse(subtree, optimizations)
        replacements[i] = (sym, subtree)
    reduced_exprs = [postprocess_for_cse(e, optimizations)
        for e in reduced_exprs]

    if isinstance(exprs, Matrix):
        reduced_exprs = [Matrix(exprs.rows, exprs.cols, reduced_exprs)]
    if postprocess is None:
        return replacements, reduced_exprs
    return postprocess(replacements, reduced_exprs)
<commit_msg>Use expr.func instead of type(expr)<commit_after>""" Tools for doing common subexpression elimination.
"""
from __future__ import print_function, division

import difflib

from sympy.core import Basic, Mul, Add, Pow, sympify, Tuple
from sympy.core.singleton import S
from sympy.core.basic import preorder_traversal
from sympy.core.function import _coeff_isneg
from sympy.core.compatibility import iterable, xrange
from sympy.utilities.iterables import numbered_symbols, \
    sift, topological_sort, ordered

from . import cse_opts

# (preprocessor, postprocessor) pairs which are commonly useful. They should
# each take a sympy expression and return a possibly transformed expression.
# When used in the function ``cse()``, the target expressions will be transformed
# by each of the preprocessor functions in order. After the common
# subexpressions are eliminated, each resulting expression will have the
# postprocessor functions transform them in *reverse* order in order to undo the
# transformation if necessary. This allows the algorithm to operate on
# a representation of the expressions that allows for more optimization
# opportunities.
# ``None`` can be used to specify no transformation for either the preprocessor or
# postprocessor.

cse_optimizations = list(cse_opts.default_optimizations)

# sometimes we want the output in a different format; non-trivial
# transformations can be put here for users
# ===============================================================

def reps_toposort(r):
    """Sort replacements `r` so (k1, v1) appears before (k2, v2)
    if k2 is in v1's free symbols. This orders items in the
    way that cse returns its results (hence, in order to use the
    replacements in a substitution option it would make sense
    to reverse the order).

    Examples
    ========
    >>> from sympy.simplify.cse_main import reps_toposort
    >>> from sympy.abc import x, y
    >>> from sympy import Eq
    >>> for l, r in reps_toposort([(x, y + 1), (y, 2)]):
    ...     print(Eq(l, r))
    ...
    y == 2
    x == y + 1

    """
    r = sympify(r)
    E = []
    for c1, (k1, v1) in enumerate(r):
        for c2, (k2, v2) in enumerate(r):
            if k1 in v2.free_symbols:
                E.append((c1, c2))
    return [r[i] for i in topological_sort((range(len(r)), E))]

def cse_separate(r, e):
    """Move expressions that are in the form (symbol, expr) out of the
    expressions and sort them into the replacements using the reps_toposort.

    Examples
    ========
    >>> from sympy.simplify.cse_main import cse_separate
    >>> from sympy.abc import x, y, z
    >>> from sympy import cos, exp, cse, Eq, symbols
    >>> x0, x1 = symbols('x:2')
    >>> eq = (x + 1 + exp((x + 1)/(y + 1)) + cos(y + 1))
    >>> cse([eq, Eq(x, z + 1), z - 2], postprocess=cse_separate) in [
    ... [[(x0, y + 1), (x, z + 1), (x1, x + 1)],
    ...  [x1 + exp(x1/x0) + cos(x0), z - 2]],
    ... [[(x1, y + 1), (x, z + 1), (x0, x + 1)],
    ...  [x0 + exp(x0/x1) + cos(x1), z - 2]]]
    ...
    True
    """
    d = sift(e, lambda w: w.is_Equality and w.lhs.is_Symbol)
    r = r + [w.args for w in d[True]]
    e = d[False]
    return [reps_toposort(r), e]

# ====end of cse postprocess idioms===========================

def preprocess_for_cse(expr, optimizations):
    """ Preprocess an expression to optimize for common subexpression
    elimination.

    Parameters
    ----------
    expr : sympy expression
        The target expression to optimize.
    optimizations : list of (callable, callable) pairs
        The (preprocessor, postprocessor) pairs.

    Returns
    -------
    expr : sympy expression
        The transformed expression.
    """
    for pre, post in optimizations:
        if pre is not None:
            expr = pre(expr)
    return expr

def postprocess_for_cse(expr, optimizations):
    """ Postprocess an expression after common subexpression elimination to
    return the expression to canonical sympy form.

    Parameters
    ----------
    expr : sympy expression
        The target expression to transform.
    optimizations : list of (callable, callable) pairs, optional
        The (preprocessor, postprocessor) pairs.  The postprocessors will be
        applied in reversed order to undo the effects of the preprocessors
        correctly.

    Returns
    -------
    expr : sympy expression
        The transformed expression.
    """
    if optimizations is None:
        optimizations = cse_optimizations
    for pre, post in reversed(optimizations):
        if post is not None:
            expr = post(expr)
    return expr

def opt_cse(exprs):
    """Find optimization opportunities in Adds, Muls, Pows and negative
    coefficient Muls"""
    from sympy.matrices import Matrix

    opt_subs = dict()

    adds = set()
    muls = set()

    seen_subexp = set()
    def _find_opts(expr):

        if expr.is_Atom:
           return

        if iterable(expr):
            list(map(_find_opts, expr))
            return

        if expr in seen_subexp:
            return expr
        seen_subexp.add(expr)

        list(map(_find_opts, expr.args))

        if _coeff_isneg(expr):
            neg_expr = -expr
            if not neg_expr.is_Atom:
                opt_subs[expr] = Mul(S.NegativeOne, neg_expr, evaluate=False)
                seen_subexp.add(neg_expr)
                expr = neg_expr

        if expr.is_Mul:
            muls.add(expr)

        elif expr.is_Add:
            adds.add(expr)

        elif expr.is_Pow:
            if _coeff_isneg(expr.exp):
                opt_subs[expr] = Pow(Pow(expr.base, -expr.exp), S.NegativeOne, evaluate=False)

    for e in exprs:
        if isinstance(e, Basic):
            _find_opts(e)

    ## Process Adds and commutative Muls

    def _match_common_args(Func, funcs):
        funcs = list(ordered(funcs))

        func_args = [set(e.args) for e in funcs]
        for i in xrange(len(func_args)):
            for j in xrange(i + 1, len(func_args)):
                com_args = func_args[i].intersection(func_args[j])
                if len(com_args) > 1:
                    com_func = Func(*com_args)

                    # for all sets, replace the common symbols by the function over them,
                    # to allow recursive matches

                    diff_i = func_args[i].difference(com_args)
                    func_args[i] = diff_i | set([com_func])
                    if diff_i:
                        opt_subs[funcs[i]] = Func(Func(*diff_i), com_func, evaluate=False)

                    diff_j = func_args[j].difference(com_args)
                    func_args[j] = diff_j | set([com_func])
                    opt_subs[funcs[j]] = Func(Func(*diff_j), com_func, evaluate=False)

                    for k in xrange(j + 1, len(func_args)):
                        if not com_args.difference(func_args[k]):
                            diff_k = func_args[k].difference(com_args)
                            func_args[k] = diff_k | set([com_func])
                            opt_subs[funcs[k]] = Func(Func(*diff_k), com_func, evaluate=False)

    # split muls into commutative
    comutative_muls = set()
    for m in muls:
        c, nc = m.args_cnc(cset=True)
        if c:
            c_mul = Mul(*c)
            if nc:
                opt_subs[m] = Mul(c_mul, Mul(*nc), evaluate=False)
            if len(c) > 1:
                comutative_muls.add(c_mul)

    _match_common_args(Add, adds)
    _match_common_args(Mul, comutative_muls)

    return opt_subs

def tree_cse(exprs, symbols, opt_subs=None):
    """Perform blind CSE on expression tree, taking opt_subs into account"""
    from sympy.matrices import Matrix

    if opt_subs is None:
        opt_subs = dict()

    ## Find repeated sub-expressions

    to_eliminate = set()

    seen_subexp = set()
    def _find_repeated(expr):
        if expr.is_Atom:
           return

        if iterable(expr):
            args = expr

        else:
            if expr in seen_subexp:
                to_eliminate.add(expr)
                return

            seen_subexp.add(expr)

            if expr in opt_subs:
                expr = opt_subs[expr]

            args = expr.args

        list(map(_find_repeated, args))

    for e in exprs:
        if isinstance(e, Basic):
            _find_repeated(e)

    ## Rebuild tree

    replacements = []

    subs = dict()
    def _rebuild(expr):

        if expr.is_Atom:
            return expr

        if iterable(expr):
            new_args = [_rebuild(arg) for arg in expr]
            return expr.func(*new_args)

        if expr in subs:
            return subs[expr]

        orig_expr = expr
        if expr in opt_subs:
            expr = opt_subs[expr]

        # Parse Muls and Adds arguments by order
        if expr.is_Mul:
            c, nc = expr.args_cnc()
            args = list(ordered(c)) + nc
        elif expr.is_Add:
            args = list(ordered(expr.args))
        else:
            args = expr.args

        new_args = list(map(_rebuild, args))
        if new_args != args:
            new_expr = expr.func(*new_args)
        else:
            new_expr = expr

        if orig_expr in to_eliminate:
            sym = next(symbols)
            subs[orig_expr] = sym
            replacements.append((sym, new_expr))
            return sym

        else:
            return new_expr

    reduced_exprs = []
    for e in exprs:
        if isinstance(e, Basic):
            reduced_e =_rebuild(e)
        else:
            reduced_e = e
        reduced_exprs.append(reduced_e)

    return replacements, reduced_exprs

def cse(exprs, symbols=None, optimizations=None, postprocess=None):
    """ Perform common subexpression elimination on an expression.

    Parameters
    ==========

    exprs : list of sympy expressions, or a single sympy expression
        The expressions to reduce.
    symbols : infinite iterator yielding unique Symbols
        The symbols used to label the common subexpressions which are pulled
        out. The ``numbered_symbols`` generator is useful. The default is a
        stream of symbols of the form "x0", "x1", etc. This must be an infinite
        iterator.
    optimizations : list of (callable, callable) pairs, optional
        The (preprocessor, postprocessor) pairs. If not provided,
        ``sympy.simplify.cse.cse_optimizations`` is used.
    postprocess : a function which accepts the two return values of cse and
        returns the desired form of output from cse, e.g. if you want the
        replacements reversed the function might be the following lambda:
        lambda r, e: return reversed(r), e

    Returns
    =======

    replacements : list of (Symbol, expression) pairs
        All of the common subexpressions that were replaced. Subexpressions
        earlier in this list might show up in subexpressions later in this list.
    reduced_exprs : list of sympy expressions
        The reduced expressions with all of the replacements above.
    """
    from sympy.matrices import Matrix

    if symbols is None:
        symbols = numbered_symbols()
    else:
        # In case we get passed an iterable with an __iter__ method instead of
        # an actual iterator.
        symbols = iter(symbols)

    if optimizations is None:
        # Pull out the default here just in case there are some weird
        # manipulations of the module-level list in some other thread.
        optimizations = list(cse_optimizations)

    # Handle the case if just one expression was passed.
    if isinstance(exprs, Basic):
        exprs = [exprs]

    # Preprocess the expressions to give us better optimization opportunities.
    reduced_exprs = [preprocess_for_cse(e, optimizations) for e in exprs]

    # Find other optimization opportunities.
    opt_subs = opt_cse(reduced_exprs)

    # Main CSE algorithm.
    replacements, reduced_exprs = tree_cse(reduced_exprs, symbols, opt_subs)

    # Postprocess the expressions to return the expressions to canonical form.
    for i, (sym, subtree) in enumerate(replacements):
        subtree = postprocess_for_cse(subtree, optimizations)
        replacements[i] = (sym, subtree)
    reduced_exprs = [postprocess_for_cse(e, optimizations)
        for e in reduced_exprs]

    if isinstance(exprs, Matrix):
        reduced_exprs = [Matrix(exprs.rows, exprs.cols, reduced_exprs)]
    if postprocess is None:
        return replacements, reduced_exprs
    return postprocess(replacements, reduced_exprs)
<|endoftext|>


<commit_before><commit_msg>fix unit test<commit_after><|endoftext|>


<commit_before>"""For packets received by the datapath and sent to the controller."""

# System imports
from enum import IntEnum

from pyof.foundation.base import GenericMessage
from pyof.foundation.basic_types import (
    BinaryData, Pad, UBInt8, UBInt16, UBInt32, UBInt64)
from pyof.v0x04.common.flow_match import Match
from pyof.v0x04.common.header import Header, Type

# Third-party imports

__all__ = ('PacketIn', 'PacketInReason')

# Enums

class PacketInReason(IntEnum):
    """Reason why this packet is being sent to the controller."""

    #: matching flow (table-miss flow entry).
    OFPR_NO_MATCH = 0
    #: Action explicitly output to controller.
    OFPR_ACTION = 1
    #: Packet has invalid TTL.
    OFPR_INVALID_TTL = 2

# Classes

class PacketIn(GenericMessage):
    """Packet received on port (datapath -> controller)."""

    #: :class:`~pyof.v0x04.common.header.Header`: OpenFlow Header
    header = Header(message_type=Type.OFPT_PACKET_IN)
    #: ID assigned by datapath.
    buffer_id = UBInt32()
    #: Full length of frame.
    total_len = UBInt16()
    #: Reason packet is being sent (one of OFPR_*),
    reason = UBInt8(enum_ref=PacketInReason)
    #: ID of the table that was looked up.
    table_id = UBInt8()
    #: Cookie of the flow entry that was looked up.
    cookie = UBInt64()
    #: Packet metadata. Variable size.
    match = Match()
    #: Align to 64 bit + 16 bit
    pad = Pad(2)
    #: Ethernet frame whose length is inferred from header.length.
    #: The padding bytes preceding the Ethernet frame ensure that the IP
    #: header (if any) following the Ethernet header is 32-bit aligned.
    data = BinaryData()

    def __init__(self, xid=None, buffer_id=None, total_len=None, reason=None,
                 table_id=None, cookie=None, match=None, data=b''):
        """Assign parameters to object attributes.

        Args:
            xid (int): Header's xid.
            buffer_id (int): ID assigned by datapath.
            total_len (int): Full length of frame.
            reason (~pyof.v0x04.asynchronous.packet_in.PacketInReason):
                The reason why the packet is being sent
            table_id (int): ID of the table that was looked up
            cookie (int): Cookie of the flow entry that was looked up
            match (:class:`~pyof.v0x04.common.flow_match.Match`):
                Packet metadata with variable size.
            data (bytes): Ethernet frame, halfway through 32-bit word, so the
                IP header is 32-bit aligned. The amount of data is inferred
                from the length field in the header. Because of padding,
                offsetof(struct ofp_packet_in, data) ==
                sizeof(struct ofp_packet_in) - 2.
        """
        super().__init__(xid)
        self.buffer_id = buffer_id
        self.total_len = total_len
        self.reason = reason
        self.table_id = table_id
        self.cookie = cookie
        self.match = match
        self.data = data
<commit_msg>[v0x04] Add 'in_port' property to PacketIn<commit_after>"""For packets received by the datapath and sent to the controller."""

# System imports
from enum import IntEnum

from pyof.foundation.base import GenericMessage
from pyof.foundation.basic_types import (
    BinaryData, Pad, UBInt8, UBInt16, UBInt32, UBInt64)
from pyof.v0x04.common.flow_match import Match, OxmOfbMatchField
from pyof.v0x04.common.header import Header, Type

# Third-party imports

__all__ = ('PacketIn', 'PacketInReason')

# Enums

class PacketInReason(IntEnum):
    """Reason why this packet is being sent to the controller."""

    #: matching flow (table-miss flow entry).
    OFPR_NO_MATCH = 0
    #: Action explicitly output to controller.
    OFPR_ACTION = 1
    #: Packet has invalid TTL.
    OFPR_INVALID_TTL = 2

# Classes

class PacketIn(GenericMessage):
    """Packet received on port (datapath -> controller)."""

    #: :class:`~pyof.v0x04.common.header.Header`: OpenFlow Header
    header = Header(message_type=Type.OFPT_PACKET_IN)
    #: ID assigned by datapath.
    buffer_id = UBInt32()
    #: Full length of frame.
    total_len = UBInt16()
    #: Reason packet is being sent (one of OFPR_*),
    reason = UBInt8(enum_ref=PacketInReason)
    #: ID of the table that was looked up.
    table_id = UBInt8()
    #: Cookie of the flow entry that was looked up.
    cookie = UBInt64()
    #: Packet metadata. Variable size.
    match = Match()
    #: Align to 64 bit + 16 bit
    pad = Pad(2)
    #: Ethernet frame whose length is inferred from header.length.
    #: The padding bytes preceding the Ethernet frame ensure that the IP
    #: header (if any) following the Ethernet header is 32-bit aligned.
    data = BinaryData()

    def __init__(self, xid=None, buffer_id=None, total_len=None, reason=None,
                 table_id=None, cookie=None, match=None, data=b''):
        """Assign parameters to object attributes.

        Args:
            xid (int): Header's xid.
            buffer_id (int): ID assigned by datapath.
            total_len (int): Full length of frame.
            reason (~pyof.v0x04.asynchronous.packet_in.PacketInReason):
                The reason why the packet is being sent
            table_id (int): ID of the table that was looked up
            cookie (int): Cookie of the flow entry that was looked up
            match (:class:`~pyof.v0x04.common.flow_match.Match`):
                Packet metadata with variable size.
            data (bytes): Ethernet frame, halfway through 32-bit word, so the
                IP header is 32-bit aligned. The amount of data is inferred
                from the length field in the header. Because of padding,
                offsetof(struct ofp_packet_in, data) ==
                sizeof(struct ofp_packet_in) - 2.
        """
        super().__init__(xid)
        self.buffer_id = buffer_id
        self.total_len = total_len
        self.reason = reason
        self.table_id = table_id
        self.cookie = cookie
        self.match = match
        self.data = data

    @property
    def in_port(self):
        """Retrieve the 'in_port' that generated the PacketIn.

        This method will look for the OXM_TLV with type OFPXMT_OFB_IN_PORT on
        the `oxm_match_fields` field from `match` field and return its value,
        if the OXM exists.

        Returns:
            The integer number of the 'in_port' that generated the PacketIn if
            it exists. Otherwise return None.

        """
        return self.match.get_field(OxmOfbMatchField.OFPXMT_OFB_IN_PORT)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
# Copyright 2014 OpenMarket Ltd
#
# Licensed 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.

from twisted.internet import defer

from ._base import BaseHandler

from synapse.api.errors import SynapseError, AuthError

import logging

from collections import namedtuple

logger = logging.getLogger(__name__)

# A tiny object useful for storing a user's membership in a room, as a mapping
# key
RoomMember = namedtuple("RoomMember", ("room_id", "user"))

class TypingNotificationHandler(BaseHandler):
    def __init__(self, hs):
        super(TypingNotificationHandler, self).__init__(hs)

        self.homeserver = hs

        self.clock = hs.get_clock()

        self.federation = hs.get_replication_layer()

        self.federation.register_edu_handler("m.typing", self._recv_edu)

        self._member_typing_until = {} # clock time we expect to stop
        self._member_typing_timer = {} # deferreds to manage theabove

        # map room IDs to serial numbers
        self._room_serials = {}
        self._latest_room_serial = 0
        # map room IDs to sets of users currently typing
        self._room_typing = {}

    def tearDown(self):
        """Cancels all the pending timers.
        Normally this shouldn't be needed, but it's required from unit tests
        to avoid a "Reactor was unclean" warning."""
        for t in self._member_typing_timer.values():
            self.clock.cancel_call_later(t)

    @defer.inlineCallbacks
    def started_typing(self, target_user, auth_user, room_id, timeout):
        if not target_user.is_mine:
            raise SynapseError(400, "User is not hosted on this Home Server")

        if target_user != auth_user:
            raise AuthError(400, "Cannot set another user's typing state")

        yield self.auth.check_joined_room(room_id, target_user.to_string())

        logger.debug(
            "%s has started typing in %s", target_user.to_string(), room_id
        )

        until = self.clock.time_msec() + timeout
        member = RoomMember(room_id=room_id, user=target_user)

        was_present = member in self._member_typing_until

        if member in self._member_typing_timer:
            self.clock.cancel_call_later(self._member_typing_timer[member])

        self._member_typing_until[member] = until
        self._member_typing_timer[member] = self.clock.call_later(
            timeout / 1000, lambda: self._stopped_typing(member)
        )

        if was_present:
            # No point sending another notification
            defer.returnValue(None)

        yield self._push_update(
            room_id=room_id,
            user=target_user,
            typing=True,
        )

    @defer.inlineCallbacks
    def stopped_typing(self, target_user, auth_user, room_id):
        if not target_user.is_mine:
            raise SynapseError(400, "User is not hosted on this Home Server")

        if target_user != auth_user:
            raise AuthError(400, "Cannot set another user's typing state")

        yield self.auth.check_joined_room(room_id, target_user.to_string())

        logger.debug(
            "%s has stopped typing in %s", target_user.to_string(), room_id
        )

        member = RoomMember(room_id=room_id, user=target_user)

        yield self._stopped_typing(member)

    @defer.inlineCallbacks
    def _stopped_typing(self, member):
        if member not in self._member_typing_until:
            # No point
            defer.returnValue(None)

        yield self._push_update(
            room_id=member.room_id,
            user=member.user,
            typing=False,
        )

        del self._member_typing_until[member]

        self.clock.cancel_call_later(self._member_typing_timer[member])
        del self._member_typing_timer[member]

    @defer.inlineCallbacks
    def _push_update(self, room_id, user, typing):
        localusers = set()
        remotedomains = set()

        rm_handler = self.homeserver.get_handlers().room_member_handler
        yield rm_handler.fetch_room_distributions_into(
            room_id, localusers=localusers, remotedomains=remotedomains,
            ignore_user=user
        )

        if localusers:
            self._push_update_local(
                room_id=room_id,
                user=user,
                typing=typing
            )

        deferreds = []
        for domain in remotedomains:
            deferreds.append(self.federation.send_edu(
                destination=domain,
                edu_type="m.typing",
                content={
                    "room_id": room_id,
                    "user_id": user.to_string(),
                    "typing": typing,
                },
            ))

        yield defer.DeferredList(deferreds, consumeErrors=False)

    @defer.inlineCallbacks
    def _recv_edu(self, origin, content):
        room_id = content["room_id"]
        user = self.homeserver.parse_userid(content["user_id"])

        localusers = set()

        rm_handler = self.homeserver.get_handlers().room_member_handler
        yield rm_handler.fetch_room_distributions_into(
            room_id, localusers=localusers
        )

        if localusers:
            self._push_update_local(
                room_id=room_id,
                user=user,
                typing=content["typing"]
            )

    def _push_update_local(self, room_id, user, typing):
        if room_id not in self._room_serials:
            self._room_serials[room_id] = 0
            self._room_typing[room_id] = set()

        room_set = self._room_typing[room_id]
        if typing:
            room_set.add(user)
        elif user in room_set:
            room_set.remove(user)

        self._latest_room_serial += 1
        self._room_serials[room_id] = self._latest_room_serial

        self.notifier.on_new_user_event(rooms=[room_id])

class TypingNotificationEventSource(object):
    def __init__(self, hs):
        self.hs = hs
        self._handler = None

    def handler(self):
        # Avoid cyclic dependency in handler setup
        if not self._handler:
            self._handler = self.hs.get_handlers().typing_notification_handler
        return self._handler

    def _make_event_for(self, room_id):
        typing = self.handler()._room_typing[room_id]
        return {
            "type": "m.typing",
            "room_id": room_id,
            "typing": [u.to_string() for u in typing],
        }

    def get_new_events_for_user(self, user, from_key, limit):
        from_key = int(from_key)
        handler = self.handler()

        events = []
        for room_id in handler._room_serials:
            if handler._room_serials[room_id] <= from_key:
                continue

            # TODO: check if user is in room
            events.append(self._make_event_for(room_id))

        return (events, handler._latest_room_serial)

    def get_current_key(self):
        return self.handler()._latest_room_serial

    def get_pagination_rows(self, user, pagination_config, key):
        return ([], pagination_config.from_key)
<commit_msg>When users leave rooms mark them as no longer typing in them<commit_after># -*- coding: utf-8 -*-
# Copyright 2014 OpenMarket Ltd
#
# Licensed 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.

from twisted.internet import defer

from ._base import BaseHandler

from synapse.api.errors import SynapseError, AuthError

import logging

from collections import namedtuple

logger = logging.getLogger(__name__)

# A tiny object useful for storing a user's membership in a room, as a mapping
# key
RoomMember = namedtuple("RoomMember", ("room_id", "user"))

class TypingNotificationHandler(BaseHandler):
    def __init__(self, hs):
        super(TypingNotificationHandler, self).__init__(hs)

        self.homeserver = hs

        self.clock = hs.get_clock()

        self.federation = hs.get_replication_layer()

        self.federation.register_edu_handler("m.typing", self._recv_edu)

        hs.get_distributor().observe("user_left_room", self.user_left_room)

        self._member_typing_until = {} # clock time we expect to stop
        self._member_typing_timer = {} # deferreds to manage theabove

        # map room IDs to serial numbers
        self._room_serials = {}
        self._latest_room_serial = 0
        # map room IDs to sets of users currently typing
        self._room_typing = {}

    def tearDown(self):
        """Cancels all the pending timers.
        Normally this shouldn't be needed, but it's required from unit tests
        to avoid a "Reactor was unclean" warning."""
        for t in self._member_typing_timer.values():
            self.clock.cancel_call_later(t)

    @defer.inlineCallbacks
    def started_typing(self, target_user, auth_user, room_id, timeout):
        if not target_user.is_mine:
            raise SynapseError(400, "User is not hosted on this Home Server")

        if target_user != auth_user:
            raise AuthError(400, "Cannot set another user's typing state")

        yield self.auth.check_joined_room(room_id, target_user.to_string())

        logger.debug(
            "%s has started typing in %s", target_user.to_string(), room_id
        )

        until = self.clock.time_msec() + timeout
        member = RoomMember(room_id=room_id, user=target_user)

        was_present = member in self._member_typing_until

        if member in self._member_typing_timer:
            self.clock.cancel_call_later(self._member_typing_timer[member])

        self._member_typing_until[member] = until
        self._member_typing_timer[member] = self.clock.call_later(
            timeout / 1000, lambda: self._stopped_typing(member)
        )

        if was_present:
            # No point sending another notification
            defer.returnValue(None)

        yield self._push_update(
            room_id=room_id,
            user=target_user,
            typing=True,
        )

    @defer.inlineCallbacks
    def stopped_typing(self, target_user, auth_user, room_id):
        if not target_user.is_mine:
            raise SynapseError(400, "User is not hosted on this Home Server")

        if target_user != auth_user:
            raise AuthError(400, "Cannot set another user's typing state")

        yield self.auth.check_joined_room(room_id, target_user.to_string())

        logger.debug(
            "%s has stopped typing in %s", target_user.to_string(), room_id
        )

        member = RoomMember(room_id=room_id, user=target_user)

        yield self._stopped_typing(member)

    @defer.inlineCallbacks
    def user_left_room(self, user, room_id):
        if user.is_mine:
            member = RoomMember(room_id=room_id, user=user)
            yield self._stopped_typing(member)

    @defer.inlineCallbacks
    def _stopped_typing(self, member):
        if member not in self._member_typing_until:
            # No point
            defer.returnValue(None)

        yield self._push_update(
            room_id=member.room_id,
            user=member.user,
            typing=False,
        )

        del self._member_typing_until[member]

        self.clock.cancel_call_later(self._member_typing_timer[member])
        del self._member_typing_timer[member]

    @defer.inlineCallbacks
    def _push_update(self, room_id, user, typing):
        localusers = set()
        remotedomains = set()

        rm_handler = self.homeserver.get_handlers().room_member_handler
        yield rm_handler.fetch_room_distributions_into(
            room_id, localusers=localusers, remotedomains=remotedomains,
            ignore_user=user
        )

        if localusers:
            self._push_update_local(
                room_id=room_id,
                user=user,
                typing=typing
            )

        deferreds = []
        for domain in remotedomains:
            deferreds.append(self.federation.send_edu(
                destination=domain,
                edu_type="m.typing",
                content={
                    "room_id": room_id,
                    "user_id": user.to_string(),
                    "typing": typing,
                },
            ))

        yield defer.DeferredList(deferreds, consumeErrors=False)

    @defer.inlineCallbacks
    def _recv_edu(self, origin, content):
        room_id = content["room_id"]
        user = self.homeserver.parse_userid(content["user_id"])

        localusers = set()

        rm_handler = self.homeserver.get_handlers().room_member_handler
        yield rm_handler.fetch_room_distributions_into(
            room_id, localusers=localusers
        )

        if localusers:
            self._push_update_local(
                room_id=room_id,
                user=user,
                typing=content["typing"]
            )

    def _push_update_local(self, room_id, user, typing):
        if room_id not in self._room_serials:
            self._room_serials[room_id] = 0
            self._room_typing[room_id] = set()

        room_set = self._room_typing[room_id]
        if typing:
            room_set.add(user)
        elif user in room_set:
            room_set.remove(user)

        self._latest_room_serial += 1
        self._room_serials[room_id] = self._latest_room_serial

        self.notifier.on_new_user_event(rooms=[room_id])

class TypingNotificationEventSource(object):
    def __init__(self, hs):
        self.hs = hs
        self._handler = None

    def handler(self):
        # Avoid cyclic dependency in handler setup
        if not self._handler:
            self._handler = self.hs.get_handlers().typing_notification_handler
        return self._handler

    def _make_event_for(self, room_id):
        typing = self.handler()._room_typing[room_id]
        return {
            "type": "m.typing",
            "room_id": room_id,
            "typing": [u.to_string() for u in typing],
        }

    def get_new_events_for_user(self, user, from_key, limit):
        from_key = int(from_key)
        handler = self.handler()

        events = []
        for room_id in handler._room_serials:
            if handler._room_serials[room_id] <= from_key:
                continue

            # TODO: check if user is in room
            events.append(self._make_event_for(room_id))

        return (events, handler._latest_room_serial)

    def get_current_key(self):
        return self.handler()._latest_room_serial

    def get_pagination_rows(self, user, pagination_config, key):
        return ([], pagination_config.from_key)
<|endoftext|>