<commit_before>import pytest
from textwrap import dedent

from ..compat import PY3, wraps
from ..interface import implements, InvalidImplementation, Interface, default

py3_only = pytest.mark.skipif(not PY3, reason="Python 3 Only")

def test_valid_implementation():

    class I(Interface):  # pragma: nocover

        def m0(self):
            pass

        def m1(self, x):
            pass

        def m2(self, y):
            pass

    class C(implements(I)):  # pragma: nocover
        def m0(self):
            pass

        def m1(self, x):
            pass

        def m2(self, y):
            pass

    for i in range(10):
        class C(C):
            pass

def test_implement_multiple_interfaces_correctly():

    class I1(Interface):  # pragma: nocover
        def i1_method(self, arg1):
            pass

        def shared(self, a, b, c):
            pass

    class I2(Interface):  # pragma: nocover
        def i2_method(self, arg2):
            pass

        def shared(self, a, b, c):
            pass

    class C(implements(I1), implements(I2)):  # pragma: nocover
        def i1_method(self, arg1):
            pass

        def i2_method(self, arg2):
            pass

        def shared(self, a, b, c):
            pass

    assert set(C.interfaces()) == set([I1, I2])

def generative_fixture(g):
    """
    Decorator for turning a generator into a "parameterized" fixture that emits
    the generated values.
    """
    fixture_values = list(g())

    @pytest.fixture(params=fixture_values)
    def _fixture(request):
        return request.param

    return _fixture

@generative_fixture
def combine_interfaces():

    def combine_with_multiple_types(*interfaces):
        return tuple(map(implements, interfaces))

    def combine_with_single_type(*interfaces):
        return (implements(*interfaces),)

    yield combine_with_multiple_types
    yield combine_with_single_type

def test_require_implement_all_interfaces(combine_interfaces):

    class I1(Interface):  # pragma: nocover
        def i1_method(self, arg1):
            pass

        def shared(self, a, b, c):
            pass

    class I2(Interface):  # pragma: nocover
        def i2_method(self, arg2):
            pass

        def shared(self, a, b, c):
            pass

    bases = combine_interfaces(I1, I2)

    with pytest.raises(InvalidImplementation):
        type('C', bases, {
            'i1_method': lambda self, arg1: None,
            'i2_method': lambda self, arg2: None,
        })

    with pytest.raises(InvalidImplementation):
        type('C', bases, {
            'i1_method': lambda self, arg1: None,
            'i2_method': lambda self, a, b, c: None,
        })

    with pytest.raises(InvalidImplementation):

        type('C', bases, {
            'i2_method': lambda self, arg2: None,
            'shared': lambda self, a, b, c: None,
        })

def test_missing_methods():

    class I(Interface):  # pragma: nocover

        def m0(self):
            pass

        def m1(self):
            pass

        def m2(self, x):
            pass

    try:
        class C(implements(I)):
            def m0(self):  # pragma: nocover
                pass
    except InvalidImplementation as e:
        actual = str(e)
        expected = dedent(
            """
            class C failed to implement interface I:

            The following methods of I were not implemented:
              - m1(self)
              - m2(self, x)"""
        )
        assert actual == expected
    else:
        pytest.fail("Should have raised InvalidImplementation.")  # pragma: nocover

def test_incompatible_methods():

    class I(Interface):  # pragma: nocover

        def m0(self):
            pass

        def m1(self, x):
            pass

        def m2(self, x):
            pass

    try:
        class C(implements(I)):  # pragma: nocover
            def m0(self):
                pass

            def m1(self, y):
                pass

            def m2(self, x, y):
                pass

    except InvalidImplementation as e:
        actual = str(e)
        expected = dedent(
            """
            class C failed to implement interface I:

            The following methods of I were implemented with invalid signatures:
              - m1(self, y) != m1(self, x)
              - m2(self, x, y) != m2(self, x)"""
        )
        assert actual == expected
    else:
        pytest.fail("Should have raised InvalidImplementation.")  # pragma: nocover

def test_fail_multiple_interfaces():

    class I(Interface):  # pragma: nocover
        def m0(self, a):
            pass

    class J(Interface):  # pragma: nocover
        def m1(self, a):
            pass

    try:
        class D(implements(I, J)):  # pragma: nocover
            def m1(self, z):  # incorrect signature
                pass
    except InvalidImplementation as e:
        actual = str(e)
        expected = dedent(
            """
            class D failed to implement interface I:

            The following methods of I were not implemented:
              - m0(self, a)

            class D failed to implement interface J:

            The following methods of J were implemented with invalid signatures:
              - m1(self, z) != m1(self, a)"""
        )
        assert actual == expected

def test_implements_memoization():

    class I(Interface):
        pass

    class OtherI(Interface):
        pass

    assert implements(I) is implements(I)
    assert implements(I) is not implements(OtherI)

def test_reject_invalid_interface():

    with pytest.raises(TypeError):
        implements()

    class NotAnInterface:
        pass

    with pytest.raises(TypeError):
        implements(NotAnInterface)

def test_generated_attributes():

    class IFace(Interface):  # pragma: nocover
        def method(self, a, b):
            pass

        def method2(self, a, b, c):
            pass

    class OtherIFace(Interface):  # pragma: nocover

        def method3(self, a, b, c):
            pass

    impl = implements(IFace, OtherIFace)
    assert impl.__name__ == "ImplementsIFace_OtherIFace"

    expected_doc = dedent(
        """\
        Implementation of IFace, OtherIFace.

        Methods
        -------
        IFace.method(self, a, b)
        IFace.method2(self, a, b, c)
        OtherIFace.method3(self, a, b, c)"""
    )
    assert impl.__doc__ == expected_doc

def test_cant_instantiate_interface():

    class I(Interface):
        pass

    with pytest.raises(TypeError):
        I()

def test_reject_non_callable_interface_field():

    with pytest.raises(TypeError) as e:
        class IFace(Interface):
            x = "not allowed"

def test_static_method():

    class I(Interface):
        @staticmethod
        def foo(a, b):  # pragma: nocover
            pass

    class my_staticmethod(staticmethod):
        pass

    class Impl(implements(I)):
        @my_staticmethod   # allow staticmethod subclasses
        def foo(a, b):  # pragma: nocover
            pass

    with pytest.raises(InvalidImplementation) as e:
        class Impl(implements(I)):
            @staticmethod
            def foo(self, a, b):  # pragma: nocover
                pass

    expected = dedent(
        """
        class Impl failed to implement interface I:

        The following methods of I were implemented with invalid signatures:
          - foo(self, a, b) != foo(a, b)"""
    )
    assert expected == str(e.value)

    with pytest.raises(InvalidImplementation) as e:
        class Impl(implements(I)):
            def foo(a, b):  # pragma: nocover
                pass

    expected = dedent(
        """
        class Impl failed to implement interface I:

        The following methods of I were implemented with incorrect types:
          - foo: 'function' is not a subtype of expected type 'staticmethod'"""
    )
    assert expected == str(e.value)

def test_class_method():

    class I(Interface):
        @classmethod
        def foo(cls, a, b):  # pragma: nocover
            pass

    class my_classmethod(classmethod):
        pass

    class Impl(implements(I)):
        @my_classmethod
        def foo(cls, a, b):  # pragma: nocover
            pass

    with pytest.raises(InvalidImplementation) as e:
        class Impl(implements(I)):
            @classmethod
            def foo(a, b):  # pragma: nocover
                pass

    expected = dedent(
        """
        class Impl failed to implement interface I:

        The following methods of I were implemented with invalid signatures:
          - foo(a, b) != foo(cls, a, b)"""
    )
    assert expected == str(e.value)

    with pytest.raises(InvalidImplementation) as e:
        class Impl(implements(I)):
            def foo(cls, a, b):  # pragma: nocover
                pass

    expected = dedent(
        """
        class Impl failed to implement interface I:

        The following methods of I were implemented with incorrect types:
          - foo: 'function' is not a subtype of expected type 'classmethod'"""
    )
    assert expected == str(e.value)

def test_property():

    class I(Interface):
        @property
        def foo(self):  # pragma: nocover
            pass

    class my_property(property):
        pass

    class Impl(implements(I)):
        @my_property
        def foo(self):  # pragma: nocover
            pass

    with pytest.raises(InvalidImplementation) as e:
        class Impl(implements(I)):
            def foo(self):  # pragma: nocover
                pass

    expected = dedent(
        """
        class Impl failed to implement interface I:

        The following methods of I were implemented with incorrect types:
          - foo: 'function' is not a subtype of expected type 'property'"""
    )
    assert expected == str(e.value)

    with pytest.raises(InvalidImplementation) as e:
        class Impl(implements(I)):
            @property
            def foo(self, a, b):  # pragma: nocover
                pass

    expected = dedent(
        """
        class Impl failed to implement interface I:

        The following methods of I were implemented with invalid signatures:
          - foo(self, a, b) != foo(self)"""
    )
    assert expected == str(e.value)

    with pytest.raises(InvalidImplementation) as e:
        class Impl(implements(I)):
            def foo(self, a, b):  # pragma: nocover
                pass

    expected = dedent(
        """
        class Impl failed to implement interface I:

        The following methods of I were implemented with incorrect types:
          - foo: 'function' is not a subtype of expected type 'property'

        The following methods of I were implemented with invalid signatures:
          - foo(self, a, b) != foo(self)"""
    )
    assert expected == str(e.value)

def test_subclass_implements_additional_interface():

    class I1(Interface):
        def method1(self):  # pragma: nocover
            pass

    class I2(Interface):
        def method2(self):  # pragma: nocover
            pass

    class Impl1(implements(I1)):
        def method1(self):  # pragma: nocover
            pass

    with pytest.raises(InvalidImplementation) as e:
        class IncorrectImpl2(Impl1, implements(I2)):
            pass

    expected = dedent(
        """
        class IncorrectImpl2 failed to implement interface I2:

        The following methods of I2 were not implemented:
          - method2(self)"""
    )

    assert expected == str(e.value)

    with pytest.raises(InvalidImplementation) as e:
        class Implement2ButBreak1(Impl1, implements(I2)):

            def method1(self, x):  # pragma: nocover
                pass

            def method2(self):  # pragma: nocover
                pass

    expected = dedent(
        """
        class Implement2ButBreak1 failed to implement interface I1:

        The following methods of I1 were implemented with invalid signatures:
          - method1(self, x) != method1(self)"""
    )

    assert expected == str(e.value)

def test_default():

    class IFace(Interface):  # pragma: nocover

        def method1(self):
            pass

        def method2(self):
            pass

        @default
        def has_default(self):
            return self.method1() + self.method2()

    class C(implements(IFace)):  # pragma: nocover

        def method1(self):
            return 1

        def method2(self):
            return 2

    assert C().has_default() == 3

def test_conflicting_defaults():

    class IFace1(Interface):  # pragma: nocover

        def foo(self, a, b, c):
            pass

        @default
        def has_default(self, x):
            pass

    class IFace2(Interface):  # pragma: nocover

        def bar(self, x, y, z):
            pass

        @default
        def has_default(self, x):
            pass

    with pytest.raises(InvalidImplementation) as e:
        class Impl(implements(IFace1, IFace2)):  # pragma: nocover
            def foo(self, a, b, c):
                return a + b + c

            def bar(self, x, y, z):
                return x + y + z

    actual = str(e.value)
    expected = dedent(
        """
        class Impl received conflicting default implementations:

        The following interfaces provided default implementations for 'has_default':
          - IFace1
          - IFace2"""
    )
    assert actual == expected

def test_default_repr():

    @default
    def foo(a, b):  # pragma: nocover
        pass

    assert repr(foo) == "default({})".format(foo.implementation)

@py3_only
def test_default_warns_if_method_uses_non_interface_methods():  # pragma: nocover-py2

    with pytest.warns(UserWarning) as warns:

        class HasDefault(Interface):

            def interface_method(self, x):
                pass

            @default
            def foo(self, a, b):
                # Shouldn't cause warnings.
                self.default_method()
                self.default_classmethod()
                self.default_staticmethod()
                self.interface_method(a)

            @default
            def probably_broken_method_with_no_args():
                # This is a weird thing to do, but it shouldn't cause us to
                # crash because of missing a first parameter.
                pass

            @default
            @classmethod
            def probably_broken_classmethod_with_no_args():
                # This is a weird thing to do, but it shouldn't cause us to
                # crash because of missing a first parameter.
                pass

            @default
            @property
            def probably_broken_property_with_no_args():
                # This is a weird thing to do, but it shouldn't cause us to
                # crash because of missing a first parameter.
                pass

            @default
            @staticmethod
            def default_staticmethod():
                # Shouldn't cause warnings.
                global some_nonexistent_method
                return some_nonexistent_method()

            @default
            def default_method(self, x):
                foo = self.foo(1, 2)  # Should be fine.
                wut = self.not_in_interface(2, 3)  # Should cause a warning.
                self.setting_non_interface = 2  # Should cause a warning.
                return foo + wut

            @default
            @classmethod
            def default_classmethod(cls, x):
                return cls.class_bar(2, 3)  # Should cause a warning.

    messages = warns.list
    assert len(messages) == 2

    first = str(messages[0].message)
    expected_first = """\
Default implementation of HasDefault.default_classmethod uses non-interface attributes.

The following attributes may be accessed but are not part of the interface:
  - class_bar

Consider changing the implementation of default_classmethod or making these attributes part of HasDefault."""  # noqa
    assert first == expected_first

    second = str(messages[1].message)
    expected_second = """\
Default implementation of HasDefault.default_method uses non-interface attributes.

The following attributes may be accessed but are not part of the interface:
  - not_in_interface
  - setting_non_interface

Consider changing the implementation of default_method or making these attributes part of HasDefault."""  # noqa
    assert second == expected_second

def test_wrapped_implementation():
    class I(Interface):  # pragma: nocover
        def f(self, a, b, c):
            pass

    def wrapping_decorator(f):
        @wraps(f)
        def inner(*args, **kwargs):  # pragma: nocover
            pass

        return inner

    class C(implements(I)):  # pragma: nocover
        @wrapping_decorator
        def f(self, a, b, c):
            pass
<commit_msg>TST: add test for wrapped function with invalid signature<commit_after>import pytest
from textwrap import dedent

from ..compat import PY3, wraps
from ..interface import implements, InvalidImplementation, Interface, default

py3_only = pytest.mark.skipif(not PY3, reason="Python 3 Only")

def test_valid_implementation():

    class I(Interface):  # pragma: nocover

        def m0(self):
            pass

        def m1(self, x):
            pass

        def m2(self, y):
            pass

    class C(implements(I)):  # pragma: nocover
        def m0(self):
            pass

        def m1(self, x):
            pass

        def m2(self, y):
            pass

    for i in range(10):
        class C(C):
            pass

def test_implement_multiple_interfaces_correctly():

    class I1(Interface):  # pragma: nocover
        def i1_method(self, arg1):
            pass

        def shared(self, a, b, c):
            pass

    class I2(Interface):  # pragma: nocover
        def i2_method(self, arg2):
            pass

        def shared(self, a, b, c):
            pass

    class C(implements(I1), implements(I2)):  # pragma: nocover
        def i1_method(self, arg1):
            pass

        def i2_method(self, arg2):
            pass

        def shared(self, a, b, c):
            pass

    assert set(C.interfaces()) == set([I1, I2])

def generative_fixture(g):
    """
    Decorator for turning a generator into a "parameterized" fixture that emits
    the generated values.
    """
    fixture_values = list(g())

    @pytest.fixture(params=fixture_values)
    def _fixture(request):
        return request.param

    return _fixture

@generative_fixture
def combine_interfaces():

    def combine_with_multiple_types(*interfaces):
        return tuple(map(implements, interfaces))

    def combine_with_single_type(*interfaces):
        return (implements(*interfaces),)

    yield combine_with_multiple_types
    yield combine_with_single_type

def test_require_implement_all_interfaces(combine_interfaces):

    class I1(Interface):  # pragma: nocover
        def i1_method(self, arg1):
            pass

        def shared(self, a, b, c):
            pass

    class I2(Interface):  # pragma: nocover
        def i2_method(self, arg2):
            pass

        def shared(self, a, b, c):
            pass

    bases = combine_interfaces(I1, I2)

    with pytest.raises(InvalidImplementation):
        type('C', bases, {
            'i1_method': lambda self, arg1: None,
            'i2_method': lambda self, arg2: None,
        })

    with pytest.raises(InvalidImplementation):
        type('C', bases, {
            'i1_method': lambda self, arg1: None,
            'i2_method': lambda self, a, b, c: None,
        })

    with pytest.raises(InvalidImplementation):

        type('C', bases, {
            'i2_method': lambda self, arg2: None,
            'shared': lambda self, a, b, c: None,
        })

def test_missing_methods():

    class I(Interface):  # pragma: nocover

        def m0(self):
            pass

        def m1(self):
            pass

        def m2(self, x):
            pass

    try:
        class C(implements(I)):
            def m0(self):  # pragma: nocover
                pass
    except InvalidImplementation as e:
        actual = str(e)
        expected = dedent(
            """
            class C failed to implement interface I:

            The following methods of I were not implemented:
              - m1(self)
              - m2(self, x)"""
        )
        assert actual == expected
    else:
        pytest.fail("Should have raised InvalidImplementation.")  # pragma: nocover

def test_incompatible_methods():

    class I(Interface):  # pragma: nocover

        def m0(self):
            pass

        def m1(self, x):
            pass

        def m2(self, x):
            pass

    try:
        class C(implements(I)):  # pragma: nocover
            def m0(self):
                pass

            def m1(self, y):
                pass

            def m2(self, x, y):
                pass

    except InvalidImplementation as e:
        actual = str(e)
        expected = dedent(
            """
            class C failed to implement interface I:

            The following methods of I were implemented with invalid signatures:
              - m1(self, y) != m1(self, x)
              - m2(self, x, y) != m2(self, x)"""
        )
        assert actual == expected
    else:
        pytest.fail("Should have raised InvalidImplementation.")  # pragma: nocover

def test_fail_multiple_interfaces():

    class I(Interface):  # pragma: nocover
        def m0(self, a):
            pass

    class J(Interface):  # pragma: nocover
        def m1(self, a):
            pass

    try:
        class D(implements(I, J)):  # pragma: nocover
            def m1(self, z):  # incorrect signature
                pass
    except InvalidImplementation as e:
        actual = str(e)
        expected = dedent(
            """
            class D failed to implement interface I:

            The following methods of I were not implemented:
              - m0(self, a)

            class D failed to implement interface J:

            The following methods of J were implemented with invalid signatures:
              - m1(self, z) != m1(self, a)"""
        )
        assert actual == expected

def test_implements_memoization():

    class I(Interface):
        pass

    class OtherI(Interface):
        pass

    assert implements(I) is implements(I)
    assert implements(I) is not implements(OtherI)

def test_reject_invalid_interface():

    with pytest.raises(TypeError):
        implements()

    class NotAnInterface:
        pass

    with pytest.raises(TypeError):
        implements(NotAnInterface)

def test_generated_attributes():

    class IFace(Interface):  # pragma: nocover
        def method(self, a, b):
            pass

        def method2(self, a, b, c):
            pass

    class OtherIFace(Interface):  # pragma: nocover

        def method3(self, a, b, c):
            pass

    impl = implements(IFace, OtherIFace)
    assert impl.__name__ == "ImplementsIFace_OtherIFace"

    expected_doc = dedent(
        """\
        Implementation of IFace, OtherIFace.

        Methods
        -------
        IFace.method(self, a, b)
        IFace.method2(self, a, b, c)
        OtherIFace.method3(self, a, b, c)"""
    )
    assert impl.__doc__ == expected_doc

def test_cant_instantiate_interface():

    class I(Interface):
        pass

    with pytest.raises(TypeError):
        I()

def test_reject_non_callable_interface_field():

    with pytest.raises(TypeError) as e:
        class IFace(Interface):
            x = "not allowed"

def test_static_method():

    class I(Interface):
        @staticmethod
        def foo(a, b):  # pragma: nocover
            pass

    class my_staticmethod(staticmethod):
        pass

    class Impl(implements(I)):
        @my_staticmethod   # allow staticmethod subclasses
        def foo(a, b):  # pragma: nocover
            pass

    with pytest.raises(InvalidImplementation) as e:
        class Impl(implements(I)):
            @staticmethod
            def foo(self, a, b):  # pragma: nocover
                pass

    expected = dedent(
        """
        class Impl failed to implement interface I:

        The following methods of I were implemented with invalid signatures:
          - foo(self, a, b) != foo(a, b)"""
    )
    assert expected == str(e.value)

    with pytest.raises(InvalidImplementation) as e:
        class Impl(implements(I)):
            def foo(a, b):  # pragma: nocover
                pass

    expected = dedent(
        """
        class Impl failed to implement interface I:

        The following methods of I were implemented with incorrect types:
          - foo: 'function' is not a subtype of expected type 'staticmethod'"""
    )
    assert expected == str(e.value)

def test_class_method():

    class I(Interface):
        @classmethod
        def foo(cls, a, b):  # pragma: nocover
            pass

    class my_classmethod(classmethod):
        pass

    class Impl(implements(I)):
        @my_classmethod
        def foo(cls, a, b):  # pragma: nocover
            pass

    with pytest.raises(InvalidImplementation) as e:
        class Impl(implements(I)):
            @classmethod
            def foo(a, b):  # pragma: nocover
                pass

    expected = dedent(
        """
        class Impl failed to implement interface I:

        The following methods of I were implemented with invalid signatures:
          - foo(a, b) != foo(cls, a, b)"""
    )
    assert expected == str(e.value)

    with pytest.raises(InvalidImplementation) as e:
        class Impl(implements(I)):
            def foo(cls, a, b):  # pragma: nocover
                pass

    expected = dedent(
        """
        class Impl failed to implement interface I:

        The following methods of I were implemented with incorrect types:
          - foo: 'function' is not a subtype of expected type 'classmethod'"""
    )
    assert expected == str(e.value)

def test_property():

    class I(Interface):
        @property
        def foo(self):  # pragma: nocover
            pass

    class my_property(property):
        pass

    class Impl(implements(I)):
        @my_property
        def foo(self):  # pragma: nocover
            pass

    with pytest.raises(InvalidImplementation) as e:
        class Impl(implements(I)):
            def foo(self):  # pragma: nocover
                pass

    expected = dedent(
        """
        class Impl failed to implement interface I:

        The following methods of I were implemented with incorrect types:
          - foo: 'function' is not a subtype of expected type 'property'"""
    )
    assert expected == str(e.value)

    with pytest.raises(InvalidImplementation) as e:
        class Impl(implements(I)):
            @property
            def foo(self, a, b):  # pragma: nocover
                pass

    expected = dedent(
        """
        class Impl failed to implement interface I:

        The following methods of I were implemented with invalid signatures:
          - foo(self, a, b) != foo(self)"""
    )
    assert expected == str(e.value)

    with pytest.raises(InvalidImplementation) as e:
        class Impl(implements(I)):
            def foo(self, a, b):  # pragma: nocover
                pass

    expected = dedent(
        """
        class Impl failed to implement interface I:

        The following methods of I were implemented with incorrect types:
          - foo: 'function' is not a subtype of expected type 'property'

        The following methods of I were implemented with invalid signatures:
          - foo(self, a, b) != foo(self)"""
    )
    assert expected == str(e.value)

def test_subclass_implements_additional_interface():

    class I1(Interface):
        def method1(self):  # pragma: nocover
            pass

    class I2(Interface):
        def method2(self):  # pragma: nocover
            pass

    class Impl1(implements(I1)):
        def method1(self):  # pragma: nocover
            pass

    with pytest.raises(InvalidImplementation) as e:
        class IncorrectImpl2(Impl1, implements(I2)):
            pass

    expected = dedent(
        """
        class IncorrectImpl2 failed to implement interface I2:

        The following methods of I2 were not implemented:
          - method2(self)"""
    )

    assert expected == str(e.value)

    with pytest.raises(InvalidImplementation) as e:
        class Implement2ButBreak1(Impl1, implements(I2)):

            def method1(self, x):  # pragma: nocover
                pass

            def method2(self):  # pragma: nocover
                pass

    expected = dedent(
        """
        class Implement2ButBreak1 failed to implement interface I1:

        The following methods of I1 were implemented with invalid signatures:
          - method1(self, x) != method1(self)"""
    )

    assert expected == str(e.value)

def test_default():

    class IFace(Interface):  # pragma: nocover

        def method1(self):
            pass

        def method2(self):
            pass

        @default
        def has_default(self):
            return self.method1() + self.method2()

    class C(implements(IFace)):  # pragma: nocover

        def method1(self):
            return 1

        def method2(self):
            return 2

    assert C().has_default() == 3

def test_conflicting_defaults():

    class IFace1(Interface):  # pragma: nocover

        def foo(self, a, b, c):
            pass

        @default
        def has_default(self, x):
            pass

    class IFace2(Interface):  # pragma: nocover

        def bar(self, x, y, z):
            pass

        @default
        def has_default(self, x):
            pass

    with pytest.raises(InvalidImplementation) as e:
        class Impl(implements(IFace1, IFace2)):  # pragma: nocover
            def foo(self, a, b, c):
                return a + b + c

            def bar(self, x, y, z):
                return x + y + z

    actual = str(e.value)
    expected = dedent(
        """
        class Impl received conflicting default implementations:

        The following interfaces provided default implementations for 'has_default':
          - IFace1
          - IFace2"""
    )
    assert actual == expected

def test_default_repr():

    @default
    def foo(a, b):  # pragma: nocover
        pass

    assert repr(foo) == "default({})".format(foo.implementation)

@py3_only
def test_default_warns_if_method_uses_non_interface_methods():  # pragma: nocover-py2

    with pytest.warns(UserWarning) as warns:

        class HasDefault(Interface):

            def interface_method(self, x):
                pass

            @default
            def foo(self, a, b):
                # Shouldn't cause warnings.
                self.default_method()
                self.default_classmethod()
                self.default_staticmethod()
                self.interface_method(a)

            @default
            def probably_broken_method_with_no_args():
                # This is a weird thing to do, but it shouldn't cause us to
                # crash because of missing a first parameter.
                pass

            @default
            @classmethod
            def probably_broken_classmethod_with_no_args():
                # This is a weird thing to do, but it shouldn't cause us to
                # crash because of missing a first parameter.
                pass

            @default
            @property
            def probably_broken_property_with_no_args():
                # This is a weird thing to do, but it shouldn't cause us to
                # crash because of missing a first parameter.
                pass

            @default
            @staticmethod
            def default_staticmethod():
                # Shouldn't cause warnings.
                global some_nonexistent_method
                return some_nonexistent_method()

            @default
            def default_method(self, x):
                foo = self.foo(1, 2)  # Should be fine.
                wut = self.not_in_interface(2, 3)  # Should cause a warning.
                self.setting_non_interface = 2  # Should cause a warning.
                return foo + wut

            @default
            @classmethod
            def default_classmethod(cls, x):
                return cls.class_bar(2, 3)  # Should cause a warning.

    messages = warns.list
    assert len(messages) == 2

    first = str(messages[0].message)
    expected_first = """\
Default implementation of HasDefault.default_classmethod uses non-interface attributes.

The following attributes may be accessed but are not part of the interface:
  - class_bar

Consider changing the implementation of default_classmethod or making these attributes part of HasDefault."""  # noqa
    assert first == expected_first

    second = str(messages[1].message)
    expected_second = """\
Default implementation of HasDefault.default_method uses non-interface attributes.

The following attributes may be accessed but are not part of the interface:
  - not_in_interface
  - setting_non_interface

Consider changing the implementation of default_method or making these attributes part of HasDefault."""  # noqa
    assert second == expected_second

def test_wrapped_implementation():
    class I(Interface):  # pragma: nocover
        def f(self, a, b, c):
            pass

    def wrapping_decorator(f):
        @wraps(f)
        def inner(*args, **kwargs):  # pragma: nocover
            pass

        return inner

    class C(implements(I)):  # pragma: nocover
        @wrapping_decorator
        def f(self, a, b, c):
            pass

def test_wrapped_implementation_incompatible():
    class I(Interface):  # pragma: nocover
        def f(self, a, b, c):
            pass

    def wrapping_decorator(f):
        @wraps(f)
        def inner(*args, **kwargs):  # pragma: nocover
            pass

        return inner

    with pytest.raises(InvalidImplementation) as e:
        class C(implements(I)):  # pragma: nocover
            @wrapping_decorator
            def f(self, a, b):  # missing ``c``
                pass

    actual_message = str(e.value)
    expected_message = dedent(
        """
        class C failed to implement interface I:

        The following methods of I were implemented with invalid signatures:
          - f(self, a, b) != f(self, a, b, c)"""
    )

    assert actual_message == expected_message
<|endoftext|>


<commit_before>from decimal import Decimal
from datetime import timedelta

from django.core.management.base import BaseCommand
from django.utils import timezone
from dateutil.relativedelta import *

from silver.models import (Customer, MeteredFeatureUnitsLog, Invoice,
                           DocumentEntry, Proforma)

class Command(BaseCommand):
    help = 'Generates the billing documents (Invoices, Proformas).'

    def _get_plan_price_and_proration_status(self, subscription):
        now = timezone.now().date()
        intervals = {
            'year': {'years': -subscription.plan.interval_count},
            'month': {'month': -subscription.plan.interval_count},
            'week': {'wee': -subscription.plan.interval_count},
            'day': {'day': -subscription.plan.interval_count},
        }
        # This will be UTC, which implies a max difference of 27 hours ~= 1 day
        interval_len = relativedelta(**intervals[subscription.plan.interval])

        if subscription.last_billing_date:
            # Full interval value
            return subscription.plan.amount, False
        else:
            # Proration
            days_in_interval = (now - (now + interval_len)).days
            days_since_subscription_start = (now - subscription.start_date).days
            percent = 100.0 * days_since_subscription_start / days_in_interval
            return Decimal(percent) * subscription.plan.amount, True

    def _add_plan(self, subscription, invoice=None, proforma=None):
        interval = '%sly' % subscription.plan.interval

        if not subscription.last_billing_date:
            # First time bill
            start_date = subscription.start_date
            end_date = timzone.now().date()
        else:
            intervals = {
                'year': {'years': subscription.plan.interval_count},
                'month': {'month': subscription.plan.interval_count},
                'week': {'wee': subscription.plan.interval_count},
                'day': {'day': subscription.plan.interval_count},
            }
            interval_len = relativedelta(**intervals[subscription.plan.interval])

            start_date = subscription.last_billing_date
            end_date = subscription.last_billing_date + interval_len

        composed_description = "{plan_name} {interval} plan subscription"\
                               " ({start_date} - {end_date})".format(
                                    plan_name=subscription.plan.name,
                                    interval=interval, start_date=start_date,
                                    end_date=end_date)
        description = subscription.description or composed_description

        if not subscription.on_trial:
            unit_price, prorated = self._get_price()
        else:
            unit_price, prorated = Decimal('0.00'), False

        unit = '%ss' % subscription.plan.interval
        DocumentEntry.objects.create(
            invoice=invoice, proforma=proforma, description=description,
            unit=unit, unit_price=unit_price, quantity=Decimal('1.00'),
            product_code=subscription.plan.product_code, prorated=prorated,
            start_date=start_date, end_date=end_date
        )

    def _add_metered_features(self, subscription, invoice=None, proforma=None):
        if subscription.last_billing_date:
            start_date = subscription.last_billing_date
        else:
            start_date = subscription.start_date

        for mf in subscription.plan.metered_features.all():
            criteria = {
                'metered_feature': mf,
                'subscription': subscription,
                'start_date__gte': start_date,
            }

            consumed_mf_log = MeteredFeatureUnitsLog.objects.filter(**criteria)
            for log_item in consumed_mf_log:
                total_units = max(0, log_item.consumed_units - mf.included_units)
                unit_price = Decimal('0.00') if subscription.on_trial else mf.price_per_unit
                DocumentEntry.objects.create(
                    invoice=invoice, proforma=proforma, description=mf.name,
                    unit=mf.unit, unit_price=unit_price, quantity=total_units,
                    product_code=mf.product_code
                )

    def _add_plan_entry(self, document, subscription):
        if subscription.plan.provider_flow == 'proforma':
            plan_entry_args = {'proforma': document}
        else:
            plan_entry_args = {'invoice': document}

        plan_entry_args.update({'subscription': subscription})
        self._add_plan(**plan_entry_args)

    def _add_mf_entries(self, document, subscription):
        if subscription.plan.provider_flow == 'proforma':
            mf_entry_args = {'proforma': document}
        else:
            mf_entry_args = {'invoice': document}

        mf_entry_args.update({'subscription': subscription})
        self._add_metered_features(**mf_entry_args)

    def _create_document(self, provider, customer, subscription):
        provider_flow = subscription.plan.provider_flow
        DocumentModel = Proforma if provider_flow == 'proforma' else Invoice

        delta = timedelta(days=customer.payment_due_days)
        due_date = timezone.now().date() + delta
        document = DocumentModel.objects.create(
            provider=subscription.plan.provider, customer=customer,
            due_date=due_date, subscription=subscription
        )

        return document

    def _print_status_to_stdout(self, subscription, created=False):
        if subscription.plan.provider_flow == 'proforma':
            doc_name = 'Proforma'
        else:
            doc_name = 'Invoice'
        action = 'Generating' if created else 'Updating'
        msg = '{action} {doc_name} for {subscription}.'.format(
            action=action, doc_name=doc_name, subscription=subscription)

        self.stdout.write(msg)

    def handle(self, *args, **options):
        for customer in Customer.objects.all():
            if customer.consolidated_billing:

                # Intermediary document for each provider
                document_per_provider = {}

                # Default doc state (issued, draft) for each provider
                default_doc_state = {}

                for subscription in customer.subscriptions.all():
                    if not subscription.should_be_billed:
                        continue

                    provider = subscription.plan.provider

                    default_doc_state[provider] = provider.default_document_state
                    if provider in document_per_provider:
                        document = document_per_provider[provider]
                        self._print_status_to_stdout(subscription,
                                                     created=False)
                    else:
                        document = self._create_document(provider, customer,
                                                         subscription)
                        document_per_provider[provider] = document
                        self._print_status_to_stdout(subscription,
                                                     created=True)

                    # Add plan to invoice/proforma
                    self._add_plan_entry(document, subscription)
                    # Add mf units to proforma/invoice
                    self._add_mf_entries(document, subscription)

                for provider, document in document_per_provider.iteritems():
                    if default_doc_state[provider] == 'issued':
                        document.issue()
                        document.save()
            else:
                # Generate an invoice for each subscription
                for subscription in customer.subscriptions.all():
                    if not subscription.should_be_billed:
                        continue

                    provider = subscription.plan.provider
                    document = self._create_document(provider, customer,
                                                     subscription)
                    self._print_status_to_stdout(subscription, created=True)

                    # Add plan to invoice/proforma
                    self._add_plan_entry(document, subscription)
                    # Add mf units to proforma/invoice
                    self._add_mf_entries(document, subscription)

                    if provider.default_document_state == 'issued':
                        document.issue()
                        document.save()
<commit_msg>Fix relativedelta<commit_after>from decimal import Decimal
from datetime import timedelta

from django.core.management.base import BaseCommand
from django.utils import timezone
from dateutil.relativedelta import *

from silver.models import (Customer, MeteredFeatureUnitsLog, Invoice,
                           DocumentEntry, Proforma)

class Command(BaseCommand):
    help = 'Generates the billing documents (Invoices, Proformas).'

    def _get_plan_price_and_proration_status(self, subscription):
        now = timezone.now().date()
        intervals = {
            'year': {'years': -subscription.plan.interval_count},
            'month': {'month': -subscription.plan.interval_count},
            'week': {'wee': -subscription.plan.interval_count},
            'day': {'day': -subscription.plan.interval_count},
        }
        # This will be UTC, which implies a max difference of 27 hours ~= 1 day
        interval_len = relativedelta(**intervals[subscription.plan.interval])

        if subscription.last_billing_date:
            # Full interval value
            return subscription.plan.amount, False
        else:
            # Proration
            days_in_interval = (now - (now + interval_len)).days
            days_since_subscription_start = (now - subscription.start_date).days
            percent = 100.0 * days_since_subscription_start / days_in_interval
            return Decimal(percent) * subscription.plan.amount, True

    def _add_plan(self, subscription, invoice=None, proforma=None):
        interval = '%sly' % subscription.plan.interval

        if not subscription.last_billing_date:
            # First time bill
            start_date = subscription.start_date
            end_date = timzone.now().date()
        else:
            intervals = {
                'year': {'years': +subscription.plan.interval_count},
                'month': {'month': +subscription.plan.interval_count},
                'week': {'wee': +subscription.plan.interval_count},
                'day': {'day': +subscription.plan.interval_count},
            }
            interval_len = relativedelta(**intervals[subscription.plan.interval])

            start_date = subscription.last_billing_date
            end_date = subscription.last_billing_date + interval_len

        composed_description = "{plan_name} {interval} plan subscription"\
                               " ({start_date} - {end_date})".format(
                                    plan_name=subscription.plan.name,
                                    interval=interval, start_date=start_date,
                                    end_date=end_date)
        description = subscription.description or composed_description

        if not subscription.on_trial:
            unit_price, prorated = self._get_price()
        else:
            unit_price, prorated = Decimal('0.00'), False

        unit = '%ss' % subscription.plan.interval
        DocumentEntry.objects.create(
            invoice=invoice, proforma=proforma, description=description,
            unit=unit, unit_price=unit_price, quantity=Decimal('1.00'),
            product_code=subscription.plan.product_code, prorated=prorated,
            start_date=start_date, end_date=end_date
        )

    def _add_metered_features(self, subscription, invoice=None, proforma=None):
        if subscription.last_billing_date:
            start_date = subscription.last_billing_date
        else:
            start_date = subscription.start_date

        for mf in subscription.plan.metered_features.all():
            criteria = {
                'metered_feature': mf,
                'subscription': subscription,
                'start_date__gte': start_date,
            }

            consumed_mf_log = MeteredFeatureUnitsLog.objects.filter(**criteria)
            for log_item in consumed_mf_log:
                total_units = max(0, log_item.consumed_units - mf.included_units)
                unit_price = Decimal('0.00') if subscription.on_trial else mf.price_per_unit
                DocumentEntry.objects.create(
                    invoice=invoice, proforma=proforma, description=mf.name,
                    unit=mf.unit, unit_price=unit_price, quantity=total_units,
                    product_code=mf.product_code
                )

    def _add_plan_entry(self, document, subscription):
        if subscription.plan.provider_flow == 'proforma':
            plan_entry_args = {'proforma': document}
        else:
            plan_entry_args = {'invoice': document}

        plan_entry_args.update({'subscription': subscription})
        self._add_plan(**plan_entry_args)

    def _add_mf_entries(self, document, subscription):
        if subscription.plan.provider_flow == 'proforma':
            mf_entry_args = {'proforma': document}
        else:
            mf_entry_args = {'invoice': document}

        mf_entry_args.update({'subscription': subscription})
        self._add_metered_features(**mf_entry_args)

    def _create_document(self, provider, customer, subscription):
        provider_flow = subscription.plan.provider_flow
        DocumentModel = Proforma if provider_flow == 'proforma' else Invoice

        delta = timedelta(days=customer.payment_due_days)
        due_date = timezone.now().date() + delta
        document = DocumentModel.objects.create(
            provider=subscription.plan.provider, customer=customer,
            due_date=due_date, subscription=subscription
        )

        return document

    def _print_status_to_stdout(self, subscription, created=False):
        if subscription.plan.provider_flow == 'proforma':
            doc_name = 'Proforma'
        else:
            doc_name = 'Invoice'
        action = 'Generating' if created else 'Updating'
        msg = '{action} {doc_name} for {subscription}.'.format(
            action=action, doc_name=doc_name, subscription=subscription)

        self.stdout.write(msg)

    def handle(self, *args, **options):
        for customer in Customer.objects.all():
            if customer.consolidated_billing:

                # Intermediary document for each provider
                document_per_provider = {}

                # Default doc state (issued, draft) for each provider
                default_doc_state = {}

                for subscription in customer.subscriptions.all():
                    if not subscription.should_be_billed:
                        continue

                    provider = subscription.plan.provider

                    default_doc_state[provider] = provider.default_document_state
                    if provider in document_per_provider:
                        document = document_per_provider[provider]
                        self._print_status_to_stdout(subscription,
                                                     created=False)
                    else:
                        document = self._create_document(provider, customer,
                                                         subscription)
                        document_per_provider[provider] = document
                        self._print_status_to_stdout(subscription,
                                                     created=True)

                    # Add plan to invoice/proforma
                    self._add_plan_entry(document, subscription)
                    # Add mf units to proforma/invoice
                    self._add_mf_entries(document, subscription)

                for provider, document in document_per_provider.iteritems():
                    if default_doc_state[provider] == 'issued':
                        document.issue()
                        document.save()
            else:
                # Generate an invoice for each subscription
                for subscription in customer.subscriptions.all():
                    if not subscription.should_be_billed:
                        continue

                    provider = subscription.plan.provider
                    document = self._create_document(provider, customer,
                                                     subscription)
                    self._print_status_to_stdout(subscription, created=True)

                    # Add plan to invoice/proforma
                    self._add_plan_entry(document, subscription)
                    # Add mf units to proforma/invoice
                    self._add_mf_entries(document, subscription)

                    if provider.default_document_state == 'issued':
                        document.issue()
                        document.save()
<|endoftext|>


<commit_before>#
# Copyright (C) 2020 Grakn Labs
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# 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 General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
#

load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")

def graknlabs_build_tools():
    git_repository(
        name = "graknlabs_build_tools",
        remote = "https://github.com/graknlabs/build-tools",
        commit = "ac0b4fb387ce791189938467c0f40fd6fbd15499",  # sync-marker: do not remove this comment, this is used for sync-dependencies by @graknlabs_build_tools
    )

def graknlabs_common():
    git_repository(
        name = "graknlabs_common",
        remote = "https://github.com/graknlabs/common",
        commit = "dfa7ce734d7a50741b63aa909835678a469f73ed",  # sync-marker: do not remove this comment, this is used for sync-dependencies by @graknlabs_common
    )

def graknlabs_graql():
    git_repository(
        name = "graknlabs_graql",
        remote = "https://github.com/graknlabs/graql",
        commit = "4524e58e3d33b7ae38afe022c8126617f4aa19aa",  # sync-marker: do not remove this comment, this is used for sync-dependencies by @graknlabs_graql
    )

def graknlabs_protocol():
    git_repository(
        name = "graknlabs_protocol",
        remote = "https://github.com/graknlabs/protocol",
        commit = "1c0090c847e29fab57e18d9ecd90398515ded67a",  # sync-marker: do not remove this comment, this is used for sync-dependencies by @graknlabs_protocol
    )

def graknlabs_client_java():
    git_repository(
        name = "graknlabs_client_java",
        remote = "https://github.com/graknlabs/client-java",
        commit = "4df3911d06e37508c21880e74fa5a545a8b65f37",
    )

def graknlabs_console():
    git_repository(
        name = "graknlabs_console",
        remote = "https://github.com/graknlabs/console",
        tag = "1.0.3",
    )

def graknlabs_benchmark():
    git_repository(
        name = "graknlabs_benchmark",
        remote = "https://github.com/graknlabs/benchmark.git",
        commit = "78869c68a2b6073917f7d6ee085ffd0b0d6a29b0",  # keep in sync with protocol changes
    )

def graknlabs_simulation():
    git_repository(
        name = "graknlabs_simulation",
        remote = "https://github.com/graknlabs/simulation",
        commit = "33ead5a0258be5bd76318d62ebdbacbb1edf6bed", # sync-marker: do not remove this comment, this is used for sync-dependencies by @graknlabs_protocol
    )

def graknlabs_verification():
    git_repository(
        name = "graknlabs_verification",
        remote = "git@github.com:graknlabs/verification.git",
        commit = "22e20f955884c94d0667001e13e346cb80ebd92d",  # sync-marker: do not remove this comment, this is used for sync-dependencies by @graknlabs_verification
    )

def graknlabs_grabl_tracing():
    git_repository(
        name = "graknlabs_grabl_tracing",
        remote = "https://github.com/graknlabs/grabl-tracing",
        commit = "42f507d6b973cbc87d18a27ee83121c791295184", # sync-marker: do not remove this comment, this is used for sync-dependencies by @graknlabs_protocol
    )
<commit_msg>//ci:sync-dependencies: graknlabs/build-tools@fb6e02f<commit_after>#
# Copyright (C) 2020 Grakn Labs
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# 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 General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
#

load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")

def graknlabs_build_tools():
    git_repository(
        name = "graknlabs_build_tools",
        remote = "https://github.com/graknlabs/build-tools",
        commit = "fb6e02f964e8f1742febe788ba3dbbcc17551cb0",  # sync-marker: do not remove this comment, this is used for sync-dependencies by @graknlabs_build_tools
    )

def graknlabs_common():
    git_repository(
        name = "graknlabs_common",
        remote = "https://github.com/graknlabs/common",
        commit = "9e590131790cd6a79326a31dbe687d77ea629194",  # sync-marker: do not remove this comment, this is used for sync-dependencies by @graknlabs_common
    )

def graknlabs_graql():
    git_repository(
        name = "graknlabs_graql",
        remote = "https://github.com/graknlabs/graql",
        commit = "4d2a3afd0b8e5c8ed43677610e74780ecaaacba2",  # sync-marker: do not remove this comment, this is used for sync-dependencies by @graknlabs_graql
    )

def graknlabs_protocol():
    git_repository(
        name = "graknlabs_protocol",
        remote = "https://github.com/graknlabs/protocol",
        commit = "7af1e68d30335db509d220dcdc2f050177101227",  # sync-marker: do not remove this comment, this is used for sync-dependencies by @graknlabs_protocol
    )

def graknlabs_client_java():
    git_repository(
        name = "graknlabs_client_java",
        remote = "https://github.com/graknlabs/client-java",
        commit = "4df3911d06e37508c21880e74fa5a545a8b65f37",
    )

def graknlabs_console():
    git_repository(
        name = "graknlabs_console",
        remote = "https://github.com/graknlabs/console",
        tag = "1.0.3",
    )

def graknlabs_benchmark():
    git_repository(
        name = "graknlabs_benchmark",
        remote = "https://github.com/graknlabs/benchmark.git",
        commit = "78869c68a2b6073917f7d6ee085ffd0b0d6a29b0",  # keep in sync with protocol changes
    )

def graknlabs_simulation():
    git_repository(
        name = "graknlabs_simulation",
        remote = "https://github.com/graknlabs/simulation",
        commit = "33ead5a0258be5bd76318d62ebdbacbb1edf6bed", # sync-marker: do not remove this comment, this is used for sync-dependencies by @graknlabs_protocol
    )

def graknlabs_verification():
    git_repository(
        name = "graknlabs_verification",
        remote = "git@github.com:graknlabs/verification.git",
        commit = "22e20f955884c94d0667001e13e346cb80ebd92d",  # sync-marker: do not remove this comment, this is used for sync-dependencies by @graknlabs_verification
    )

def graknlabs_grabl_tracing():
    git_repository(
        name = "graknlabs_grabl_tracing",
        remote = "https://github.com/graknlabs/grabl-tracing",
        commit = "42f507d6b973cbc87d18a27ee83121c791295184", # sync-marker: do not remove this comment, this is used for sync-dependencies by @graknlabs_protocol
    )
<|endoftext|>


<commit_before>import py2js
import py2js.compiler
import ast
import inspect
from py2js.compiler import JSError

class Compiler(py2js.compiler.BaseCompiler):

    def visit_Module(self, node):
        module = []

        for stmt in node.body:
            module.extend(self.visit(stmt))

        return module

    def visit_FunctionDef(self, node):
        raise JSError("Javascript compiler does not support function definitions")

    def visit_ClassDef(self, node):
        raise JSError("Javascript compiler does not support class definitions")

    def visit_Return(self, node):
        if node.value is not None:
            return ["return %s;" % self.visit(node.value)]
        else:
            return ["return;"]

    def visit_Delete(self, node):

        return ["delete %s;" % ", ".join(map(self.visit, node.targets))]

    def visit_AssignSimple(self, left, right):
        target = left
        value  = right
        if isinstance(target, (ast.Tuple, ast.List)):
            js = ["var __dummy%d__ = %s;" % (self.dummy_index, value)]

            for i, target in enumerate(target.elts):
                var = self.visit(target)
                declare = ""
                if isinstance(target, ast.Name):
                    if not (var in self._scope):
                        self._scope.append(var)
                        declare = "var "
                js.append("%s%s = __dummy%d__[%d];" % (declare, var, self.dummy_index, i))

            self.dummy_index += 1
        elif isinstance(target, ast.Subscript) and isinstance(target.slice, ast.Index):
            # found index assignment
            if isinstance(target.slice, ast.Str):
                i = self.visit(target.slice)
            else:
                i = '"%s"' % self.visit(target.slice)
            js = ["%s[%s] = %s;" % (self.visit(target.value), self.visit(target.slice), value)]
        elif isinstance(target, ast.Subscript) and isinstance(target.slice, ast.Slice):
            raise JSError("Javascript does not support slice assignments")
        else:
            var = self.visit(target)
            if isinstance(target, ast.Name):
                if not (var in self._scope):
                    self._scope.append(var)
                    declare = "var "
                else:
                    declare = ""
                js = ["%s%s = %s;" % (declare, var, value)]
            elif isinstance(target, ast.Attribute):
                js = ["%s.%s = %s;" % (self.visit(target.value), str(target.attr), value)]
            else:
                raise JSError("Unsupported assignment type")
        return js

    def visit_AugAssign(self, node):
        # TODO: Make sure that all the logic in Assign also works in AugAssign
        target = self.visit(node.target)
        value = self.visit(node.value)

        if isinstance(node.op, ast.Pow):
            return ["%s = Math.pow(%s, %s);" % (target, target, value)]
        if isinstance(node.op, ast.FloorDiv):
            return ["%s = Math.floor((%s)/(%s));" % (target, target, value)]

        return ["%s %s= %s;" % (target, self.get_binary_op(node), value)]

    def visit_For(self, node):
        if not isinstance(node.target, ast.Name):
            raise JSError("argument decomposition in 'for' loop is not supported")

        js = []

        for_target = self.visit(node.target)
        for_iter = self.visit(node.iter)

        iter_dummy = self.new_dummy()
        orelse_dummy = self.new_dummy()
        exc_dummy = self.new_dummy()

        js.append("var %s = iter(%s);" % (iter_dummy, for_iter))
        js.append("var %s = false;" % orelse_dummy)
        js.append("while (1) {")
        js.append("    var %s;" % for_target)
        js.append("    try {")
        js.append("        %s = %s.next();" % (for_target, iter_dummy))
        js.append("    } catch (%s) {" % exc_dummy)
        js.append("        if (isinstance(%s, py_builtins.StopIteration)) {" % exc_dummy)
        js.append("            %s = true;" % orelse_dummy)
        js.append("            break;")
        js.append("        } else {")
        js.append("            throw %s;" % exc_dummy)
        js.append("        }")
        js.append("    }")

        for stmt in node.body:
            js.extend(self.indent(self.visit(stmt)))

        js.append("}")

        if node.orelse:
            js.append("if (%s) {" % orelse_dummy)

            for stmt in node.orelse:
                js.extend(self.indent(self.visit(stmt)))

            js.append("}")

        return js

    def visit_While(self, node):
        js = []

        if not node.orelse:
            js.append("while (%s) {" % self.visit(node.test))
        else:
            orelse_dummy = self.new_dummy()

            js.append("var %s = false;" % orelse_dummy)
            js.append("while (1) {");
            js.append("    if (!(%s)) {" % self.visit(node.test))
            js.append("        %s = true;" % orelse_dummy)
            js.append("        break;")
            js.append("    }")

        for stmt in node.body:
            js.extend(self.indent(self.visit(stmt)))

        js.append("}")

        if node.orelse:
            js.append("if (%s) {" % orelse_dummy)

            for stmt in node.orelse:
                js.extend(self.indent(self.visit(stmt)))

            js.append("}")

        return js

    def visit_If(self, node):
        js = ["if (%s) {" % self.visit(node.test)]

        for stmt in node.body:
            js.extend(self.indent(self.visit(stmt)))

        if node.orelse:
            js.append("} else {")

            for stmt in node.orelse:
                js.extend(self.indent(self.visit(stmt)))

        return js + ["}"]

    def _visit_With(self, node):
        pass

    def _visit_Raise(self, node):
        pass

    def _visit_TryExcept(self, node):
        pass

    def _visit_TryFinally(self, node):
        pass

    def visit_Assert(self, node):
        test = self.visit(node.test)

        if node.msg is not None:
            return ["assert(%s, %s);" % (test, self.visit(node.msg))]
        else:
            return ["assert(%s);" % test]

    def _visit_Import(self, node):
        pass

    def _visit_ImportFrom(self, node):
        pass

    def _visit_Exec(self, node):
        pass

    def visit_Global(self, node):
        self._scope.extend(node.names)
        return []

    def visit_Expr(self, node):
        return [self.visit(node.value) + ";"]

    def visit_Pass(self, node):
        return ["/* pass */"]

    def visit_Break(self, node):
        return ["break;"]

    def visit_Continue(self, node):
        return ["continue;"]

    def visit_arguments(self, node):
        return ", ".join([self.visit(arg) for arg in node.args])

    def visit_Lambda(self, node):
        return "function(%s) {%s}" % (self.visit(node.args), self.visit(node.body))

    def visit_BoolOp(self, node):
        return self.get_bool_op(node).join([ "(%s)" % self.visit(val) for val in node.values ])

    def visit_UnaryOp(self, node):
        return "%s(%s)" % (self.get_unary_op(node), self.visit(node.operand))

    def visit_BinOp(self, node):
        if isinstance(node.op, ast.Mod) and isinstance(node.left, ast.Str):
            left = self.visit(node.left)
            if isinstance(node.right, (ast.Tuple, ast.List)):
                right = self.visit(node.right)
                return "vsprintf(%s, %s)" % (left, right)
            else:
                right = self.visit(node.right)
                return "sprintf(%s, %s)" % (left, right)
        left = self.visit(node.left)
        right = self.visit(node.right)

        if isinstance(node.op, ast.Pow):
            return "Math.pow(%s, %s)" % (left, right)
        if isinstance(node.op, ast.FloorDiv):
            return "Math.floor((%s)/(%s))" % (left, right)

        return "(%s) %s (%s)" % (left, self.get_binary_op(node), right)

    def visit_Compare(self, node):
        assert len(node.ops) == 1
        assert len(node.comparators) == 1
        op = node.ops[0]
        comp = node.comparators[0]
        if isinstance(op, ast.In):
            return "%s.__contains__(%s)" % (self.visit(comp), self.visit(node.left))
        elif isinstance(op, ast.NotIn):
            return "!(%s.__contains__(%s))" % (self.visit(comp), self.visit(node.left))
        elif isinstance(op, ast.Eq):
            return "(%s) === (%s)" % (self.visit(node.left), self.visit(comp))
        elif isinstance(op, ast.NotEq):
            return "(%s) !== (%s)" % (self.visit(node.left), self.visit(comp))
        else:
            return "%s %s %s" % (self.visit(node.left), self.get_comparison_op(op), self.visit(comp))

    def visit_Name(self, node):
        id = node.id
        try:
            id = self.name_map[id]
        except KeyError:
            pass

        if id in self.builtin:
            id = "py_builtins." + id;

        #~ if id in self._classes:
            #~ id = '_' + id;

        return id

    def visit_Num(self, node):
        return str(node.n)

    def visit_Str(self, node):
        # Uses the Python builtin repr() of a string and the strip string type
        # from it. This is to ensure Javascriptness, even when they use things
        # like b"\\x00" or u"\\u0000".
        return '"%s"' % repr(node.s).lstrip("urb")[1:-1]

    def visit_Call(self, node):
        func = self.visit(node.func)
        #~ if func in self._class_names:
            #~ func = 'new '+func
        if node.keywords:
            keywords = []
            for kw in node.keywords:
                keywords.append("%s: %s" % (kw.arg, self.visit(kw.value)))
            keywords = "{" + ", ".join(keywords) + "}"
            js_args = ", ".join([ self.visit(arg) for arg in node.args ])
            return "%s.args([%s], %s)" % (func, js_args,
                    keywords)
        else:
            if node.starargs is not None:
                raise JSError("star arguments are not supported")

            if node.kwargs is not None:
                raise JSError("keyword arguments are not supported")

            js_args = ", ".join([ self.visit(arg) for arg in node.args ])

            return "%s(%s)" % (func, js_args)

    def visit_Raise(self, node):
        assert node.inst is None
        assert node.tback is None
        return ["throw %s;" % self.visit(node.type)]

    def visit_Print(self, node):
        assert node.dest is None
        assert node.nl
        values = [self.visit(v) for v in node.values]
        values = ", ".join(values)
        return ["py_builtins.print(%s);" % values]

    def visit_Attribute(self, node):
        return "%s.%s" % (self.visit(node.value), node.attr)

    def visit_Tuple(self, node):
        els = [self.visit(e) for e in node.elts]
        return "[%s]" % (", ".join(els))

    def visit_Dict(self, node):
        els = []
        for k, v in zip(node.keys, node.values):
            if isinstance(k, ast.Name):
                els.append('"%s": %s' % (self.visit(k), self.visit(v)))
            else:
                els.append("%s: %s" % (self.visit(k), self.visit(v)))
        return "{%s}" % (", ".join(els))

    def visit_List(self, node):
        els = [self.visit(e) for e in node.elts]
        return "[%s]" % (", ".join(els))

    def visit_Slice(self, node):
        if node.step:
            raise JSError("Javascript does not support slicing in steps")

        if node.lower and node.upper:
            return ".slice(%s, %s)" % (self.visit(node.lower), self.visit(node.upper))

        if node.lower:
            return "[%s]" % (self.visit(node.lower))

        if node.upper:
            return ".slice(0, %s)" % (self.visit(node.upper))

        raise NotImplementedError("Slice")

    def visit_Subscript(self, node):
#        print node.value, node.slice
        if isinstance(node.slice, ast.Index):
            return "%s[%s]" % (self.visit(node.value), self.visit(node.slice))
        else:
            return "%s%s" % (self.visit(node.value), self.visit(node.slice))

    def visit_Index(self, node):
        return self.visit(node.value)

<commit_msg> * Changed javascript compiler to use new_dummy() instead of manually changing index var (found by pylint)<commit_after>import py2js
import py2js.compiler
import ast
import inspect
from py2js.compiler import JSError

class Compiler(py2js.compiler.BaseCompiler):

    def visit_Module(self, node):
        module = []

        for stmt in node.body:
            module.extend(self.visit(stmt))

        return module

    def visit_FunctionDef(self, node):
        raise JSError("Javascript compiler does not support function definitions")

    def visit_ClassDef(self, node):
        raise JSError("Javascript compiler does not support class definitions")

    def visit_Return(self, node):
        if node.value is not None:
            return ["return %s;" % self.visit(node.value)]
        else:
            return ["return;"]

    def visit_Delete(self, node):

        return ["delete %s;" % ", ".join(map(self.visit, node.targets))]

    def visit_AssignSimple(self, left, right):
        target = left
        value  = right
        if isinstance(target, (ast.Tuple, ast.List)):
            part = self.new_dummy()
            js = ["var %s = %s;" % (part, value)]

            for i, target in enumerate(target.elts):
                var = self.visit(target)
                declare = ""
                if isinstance(target, ast.Name):
                    if not (var in self._scope):
                        self._scope.append(var)
                        declare = "var "
                js.append("%s%s = %s[%d];" % (declare, var, part, i))
        elif isinstance(target, ast.Subscript) and isinstance(target.slice, ast.Index):
            # found index assignment
            if isinstance(target.slice, ast.Str):
                i = self.visit(target.slice)
            else:
                i = '"%s"' % self.visit(target.slice)
            js = ["%s[%s] = %s;" % (self.visit(target.value), self.visit(target.slice), value)]
        elif isinstance(target, ast.Subscript) and isinstance(target.slice, ast.Slice):
            raise JSError("Javascript does not support slice assignments")
        else:
            var = self.visit(target)
            if isinstance(target, ast.Name):
                if not (var in self._scope):
                    self._scope.append(var)
                    declare = "var "
                else:
                    declare = ""
                js = ["%s%s = %s;" % (declare, var, value)]
            elif isinstance(target, ast.Attribute):
                js = ["%s.%s = %s;" % (self.visit(target.value), str(target.attr), value)]
            else:
                raise JSError("Unsupported assignment type")
        return js

    def visit_AugAssign(self, node):
        # TODO: Make sure that all the logic in Assign also works in AugAssign
        target = self.visit(node.target)
        value = self.visit(node.value)

        if isinstance(node.op, ast.Pow):
            return ["%s = Math.pow(%s, %s);" % (target, target, value)]
        if isinstance(node.op, ast.FloorDiv):
            return ["%s = Math.floor((%s)/(%s));" % (target, target, value)]

        return ["%s %s= %s;" % (target, self.get_binary_op(node), value)]

    def visit_For(self, node):
        if not isinstance(node.target, ast.Name):
            raise JSError("argument decomposition in 'for' loop is not supported")

        js = []

        for_target = self.visit(node.target)
        for_iter = self.visit(node.iter)

        iter_dummy = self.new_dummy()
        orelse_dummy = self.new_dummy()
        exc_dummy = self.new_dummy()

        js.append("var %s = iter(%s);" % (iter_dummy, for_iter))
        js.append("var %s = false;" % orelse_dummy)
        js.append("while (1) {")
        js.append("    var %s;" % for_target)
        js.append("    try {")
        js.append("        %s = %s.next();" % (for_target, iter_dummy))
        js.append("    } catch (%s) {" % exc_dummy)
        js.append("        if (isinstance(%s, py_builtins.StopIteration)) {" % exc_dummy)
        js.append("            %s = true;" % orelse_dummy)
        js.append("            break;")
        js.append("        } else {")
        js.append("            throw %s;" % exc_dummy)
        js.append("        }")
        js.append("    }")

        for stmt in node.body:
            js.extend(self.indent(self.visit(stmt)))

        js.append("}")

        if node.orelse:
            js.append("if (%s) {" % orelse_dummy)

            for stmt in node.orelse:
                js.extend(self.indent(self.visit(stmt)))

            js.append("}")

        return js

    def visit_While(self, node):
        js = []

        if not node.orelse:
            js.append("while (%s) {" % self.visit(node.test))
        else:
            orelse_dummy = self.new_dummy()

            js.append("var %s = false;" % orelse_dummy)
            js.append("while (1) {");
            js.append("    if (!(%s)) {" % self.visit(node.test))
            js.append("        %s = true;" % orelse_dummy)
            js.append("        break;")
            js.append("    }")

        for stmt in node.body:
            js.extend(self.indent(self.visit(stmt)))

        js.append("}")

        if node.orelse:
            js.append("if (%s) {" % orelse_dummy)

            for stmt in node.orelse:
                js.extend(self.indent(self.visit(stmt)))

            js.append("}")

        return js

    def visit_If(self, node):
        js = ["if (%s) {" % self.visit(node.test)]

        for stmt in node.body:
            js.extend(self.indent(self.visit(stmt)))

        if node.orelse:
            js.append("} else {")

            for stmt in node.orelse:
                js.extend(self.indent(self.visit(stmt)))

        return js + ["}"]

    def _visit_With(self, node):
        pass

    def _visit_Raise(self, node):
        pass

    def _visit_TryExcept(self, node):
        pass

    def _visit_TryFinally(self, node):
        pass

    def visit_Assert(self, node):
        test = self.visit(node.test)

        if node.msg is not None:
            return ["assert(%s, %s);" % (test, self.visit(node.msg))]
        else:
            return ["assert(%s);" % test]

    def _visit_Import(self, node):
        pass

    def _visit_ImportFrom(self, node):
        pass

    def _visit_Exec(self, node):
        pass

    def visit_Global(self, node):
        self._scope.extend(node.names)
        return []

    def visit_Expr(self, node):
        return [self.visit(node.value) + ";"]

    def visit_Pass(self, node):
        return ["/* pass */"]

    def visit_Break(self, node):
        return ["break;"]

    def visit_Continue(self, node):
        return ["continue;"]

    def visit_arguments(self, node):
        return ", ".join([self.visit(arg) for arg in node.args])

    def visit_Lambda(self, node):
        return "function(%s) {%s}" % (self.visit(node.args), self.visit(node.body))

    def visit_BoolOp(self, node):
        return self.get_bool_op(node).join([ "(%s)" % self.visit(val) for val in node.values ])

    def visit_UnaryOp(self, node):
        return "%s(%s)" % (self.get_unary_op(node), self.visit(node.operand))

    def visit_BinOp(self, node):
        if isinstance(node.op, ast.Mod) and isinstance(node.left, ast.Str):
            left = self.visit(node.left)
            if isinstance(node.right, (ast.Tuple, ast.List)):
                right = self.visit(node.right)
                return "vsprintf(%s, %s)" % (left, right)
            else:
                right = self.visit(node.right)
                return "sprintf(%s, %s)" % (left, right)
        left = self.visit(node.left)
        right = self.visit(node.right)

        if isinstance(node.op, ast.Pow):
            return "Math.pow(%s, %s)" % (left, right)
        if isinstance(node.op, ast.FloorDiv):
            return "Math.floor((%s)/(%s))" % (left, right)

        return "(%s) %s (%s)" % (left, self.get_binary_op(node), right)

    def visit_Compare(self, node):
        assert len(node.ops) == 1
        assert len(node.comparators) == 1
        op = node.ops[0]
        comp = node.comparators[0]
        if isinstance(op, ast.In):
            return "%s.__contains__(%s)" % (self.visit(comp), self.visit(node.left))
        elif isinstance(op, ast.NotIn):
            return "!(%s.__contains__(%s))" % (self.visit(comp), self.visit(node.left))
        elif isinstance(op, ast.Eq):
            return "(%s) === (%s)" % (self.visit(node.left), self.visit(comp))
        elif isinstance(op, ast.NotEq):
            return "(%s) !== (%s)" % (self.visit(node.left), self.visit(comp))
        else:
            return "%s %s %s" % (self.visit(node.left), self.get_comparison_op(op), self.visit(comp))

    def visit_Name(self, node):
        id = node.id
        try:
            id = self.name_map[id]
        except KeyError:
            pass

        if id in self.builtin:
            id = "py_builtins." + id;

        #~ if id in self._classes:
            #~ id = '_' + id;

        return id

    def visit_Num(self, node):
        return str(node.n)

    def visit_Str(self, node):
        # Uses the Python builtin repr() of a string and the strip string type
        # from it. This is to ensure Javascriptness, even when they use things
        # like b"\\x00" or u"\\u0000".
        return '"%s"' % repr(node.s).lstrip("urb")[1:-1]

    def visit_Call(self, node):
        func = self.visit(node.func)
        #~ if func in self._class_names:
            #~ func = 'new '+func
        if node.keywords:
            keywords = []
            for kw in node.keywords:
                keywords.append("%s: %s" % (kw.arg, self.visit(kw.value)))
            keywords = "{" + ", ".join(keywords) + "}"
            js_args = ", ".join([ self.visit(arg) for arg in node.args ])
            return "%s.args([%s], %s)" % (func, js_args,
                    keywords)
        else:
            if node.starargs is not None:
                raise JSError("star arguments are not supported")

            if node.kwargs is not None:
                raise JSError("keyword arguments are not supported")

            js_args = ", ".join([ self.visit(arg) for arg in node.args ])

            return "%s(%s)" % (func, js_args)

    def visit_Raise(self, node):
        assert node.inst is None
        assert node.tback is None
        return ["throw %s;" % self.visit(node.type)]

    def visit_Print(self, node):
        assert node.dest is None
        assert node.nl
        values = [self.visit(v) for v in node.values]
        values = ", ".join(values)
        return ["py_builtins.print(%s);" % values]

    def visit_Attribute(self, node):
        return "%s.%s" % (self.visit(node.value), node.attr)

    def visit_Tuple(self, node):
        els = [self.visit(e) for e in node.elts]
        return "[%s]" % (", ".join(els))

    def visit_Dict(self, node):
        els = []
        for k, v in zip(node.keys, node.values):
            if isinstance(k, ast.Name):
                els.append('"%s": %s' % (self.visit(k), self.visit(v)))
            else:
                els.append("%s: %s" % (self.visit(k), self.visit(v)))
        return "{%s}" % (", ".join(els))

    def visit_List(self, node):
        els = [self.visit(e) for e in node.elts]
        return "[%s]" % (", ".join(els))

    def visit_Slice(self, node):
        if node.step:
            raise JSError("Javascript does not support slicing in steps")

        if node.lower and node.upper:
            return ".slice(%s, %s)" % (self.visit(node.lower), self.visit(node.upper))

        if node.lower:
            return "[%s]" % (self.visit(node.lower))

        if node.upper:
            return ".slice(0, %s)" % (self.visit(node.upper))

        raise NotImplementedError("Slice")

    def visit_Subscript(self, node):
#        print node.value, node.slice
        if isinstance(node.slice, ast.Index):
            return "%s[%s]" % (self.visit(node.value), self.visit(node.slice))
        else:
            return "%s%s" % (self.visit(node.value), self.visit(node.slice))

    def visit_Index(self, node):
        return self.visit(node.value)

<|endoftext|>