Example #1
0
    def __init__(self):
        super(Domain, self).__init__()
        self._service_map = service_map = self._instantiate_and_map_services()

        # replace 'meta' with a variant for the instance (don't share self.__class__.meta)
        self.meta = self.__class__.meta.get_instance_metadata(
            service_map=service_map)

        # replace 'deps' with a ShadowPortArray which serves as proxy to the deps of internal services
        components = service_map.values()
        component_deps = [
            c.deps for c in components
            if isinstance(getattr(c, 'deps', None), (PortArray,
                                                     ShadowPortArray))
        ]
        discovered = AutoDiscoverConnections(components=components)
        self.deps = ShadowPortArray(arrays=component_deps,
                                    ignore_ports=discovered.satisfied_needs())

        # materialize connections between services
        wire_up_discovered_connections(discovered=discovered)
Example #2
0
    def test_SelfReferencingMadness_raised_if_component_attempts_to_satisfy_itself(
            self):
        class Mad(Service):
            """This isn't strictly speaking wrong, but cannot sensible participate in auto-wiring."""
            deps = Needs('attention')

            @provides
            def attention(self):
                return self.deps.attention()

        with self.assertRaisesRegexp(
                SelfReferencingMadness,
                '.* both needs and provides "attention".*'):
            AutoDiscoverConnections([Mad()])
Example #3
0
    def test_discovery_of_ports_from_a_collection_of_components_classes(self):
        # We need to be able to discover connects between uninstantiated components for visualisation and analysis
        A, B, C = self.get_services()
        discovered = AutoDiscoverConnections([A, B, C])

        self.assertEqual(['b1', 'c1', 'x', 'y'], discovered.get_needs())
        self.assertEqual(['a1', 'b1', 'b2', 'c1'], discovered.get_provides())
        self.assertEqual(['x', 'y'], discovered.unsatisfied_needs())
        self.assertItemsEqual([
            DiscoveredConnection(port_name='b1', consumer=A, provider=B),
            DiscoveredConnection(port_name='c1', consumer=B, provider=C)
        ], discovered.connections())

        self.assertEqual(A, discovered.get_provider('a1'))
        self.assertEqual(B, discovered.get_provider('b1'))
        self.assertEqual(B, discovered.get_provider('b2'))
        self.assertEqual(C, discovered.get_provider('c1'))

        with self.assertRaisesRegexp(UnknownPort, '"x1" is not a valid port'):
            self.assertEqual(C, discovered.get_provider('x1'))
Example #4
0
    def test_discovery_when_there_are_no_ports(self):
        class Empty(Service):
            pass

        empty_component = Empty()
        discovered = AutoDiscoverConnections([empty_component])

        self.assertEqual([], discovered.get_needs())
        self.assertEqual([], discovered.get_provides())
        self.assertEqual([], discovered.unsatisfied_needs())
        self.assertEqual([], list(discovered.connections()))
Example #5
0
    def test_discovery_of_ports_from_a_collection_of_components_instances(
            self):

        A, B, C = self.get_services()
        a = A()
        b = B()
        c = C()
        discovered = AutoDiscoverConnections([a, b, c])

        self.assertEqual(['b1', 'c1', 'x', 'y'], discovered.get_needs())
        self.assertEqual(['a1', 'b1', 'b2', 'c1'], discovered.get_provides())
        self.assertEqual(['x', 'y'], discovered.unsatisfied_needs())
        self.assertItemsEqual([
            DiscoveredConnection(port_name='b1', consumer=a, provider=b),
            DiscoveredConnection(port_name='c1', consumer=b, provider=c)
        ], discovered.connections())

        self.assertEqual(a, discovered.get_provider('a1'))
        self.assertEqual(b, discovered.get_provider('b1'))
        self.assertEqual(b, discovered.get_provider('b2'))
        self.assertEqual(c, discovered.get_provider('c1'))

        with self.assertRaisesRegexp(UnknownPort, '"x1" is not a valid port'):
            self.assertEqual(c, discovered.get_provider('x1'))
Example #6
0
    def test_DuplicateProviders_raised_when_more_than_one_component_provides_the_same_port(
            self):
        class X(Service):
            @provides
            def x(self):
                return 'X'

            @provides
            def y(self):
                return 'Y'

        class Axe(Service):
            @provides
            def x(self):
                return 'X'

        with self.assertRaisesRegexp(DuplicateProviders,
                                     'Duplicate providers for "x".*'):
            AutoDiscoverConnections([X(), Axe()])
Example #7
0
    def __new__(mcs, name, bases, state):
        if bases == (
                INeed,
                IProvide):  # This is the Domain class itself, not its subclass
            return type.__new__(mcs, name, bases, state)

        mcs.validate_overridden_attributes(attrs=state, class_name=name)

        if '__services__' not in state or not isinstance(
                state['__services__'], (list, tuple)):
            raise DomainDefinitionError(
                '{}.__services__ must be defined with a list of component classes'
                .format(name))
        else:
            service_classes = state['__services__']
            for service_class in service_classes:
                mcs._assert_is_compatible_class(name, service_class)

        discovered = AutoDiscoverConnections(service_classes)
        provides = state.get('__provides__', None)

        if provides is None or not isinstance(provides,
                                              (list, tuple, AutoProvide)):
            raise DomainDefinitionError(
                '{}.__provides__ must be defined with a list of port names'.
                format(name))
        else:
            if isinstance(provides,
                          AutoProvide):  # auto-discover provides ports
                auto_provider = provides
                provides_ports = auto_provider.filter(
                    discovered.get_provides())
            else:
                for port_name in provides:
                    if port_name not in discovered.get_provides():
                        msg = '"{}" listed in {}.__provides__ is not provided by any of the services'.format(
                            port_name,
                            name,
                        )
                        raise DomainDefinitionError(msg)
                provides_ports = provides

        # all unsatisfied deps are exposed as dependencies of the domain
        state['deps'] = deps = PortArray()
        for port_name in discovered.unsatisfied_needs():
            deps.add_port(port_name)

        # make a shadow copy of template_funcs. Used mainly for tracking intended interfaces for ports so we can
        # use for validation during testing. At some point we might use this for wiring-time checks too to ensure
        # compatibility between ports.
        deps._needs_template_funcs = template_funcs = {}
        for port in deps.get_ports():
            template_func = mcs._assert_providers_compatible_and_extract_template_func(
                providers=discovered._needs[port],
                port_name=port,
            )
            template_funcs[port] = template_func

        # declared 'provides' ports are registered and entry points created
        state['meta'] = meta = DomainProviderMetadata()
        for port in provides_ports:
            provider = discovered.get_provider(port_name=port)

            if not issubclass(provider, (Service, Domain)):
                msg = 'Port of non-service class ({}.{}) cannot be published on the domain'.format(
                    provider.__name__, port)
                raise DomainDefinitionError(msg)

            inherited_flags = provider.get_provider_flags(port)
            inherited_flags.pop('with_name',
                                None)  # don't inherit name-change flags
            meta.register_provider(port_name=port,
                                   service=provider,
                                   flags=inherited_flags)
            state[port] = generate_domain_method(port_name=port,
                                                 provider=provider)

        return type.__new__(mcs, name, bases, state)