twgOren / roroioc

IOC Injection for python
MIT License
0 stars 1 forks source link

Corrupted state after trying to arm a container with a resource that throws #16

Open avish opened 6 years ago

avish commented 6 years ago

When arming a container, if an attempt to access a provided resource raises an exception (e.g. because that resource is a property getter), some resources will have been integrated into the injection context while others will not be, and the arm operation will be considered failed, so cleanup will not take place.

Reproducing code:

import attr
from roro_ioc import create_ioc_container

@attr.s
class Context(object):
    @property
    def ok_resource(self):
        return "I'm not a resource, I'm a free man"

    @property
    def failing_resource(self):
        raise ValueError("Boom")

CONTAINER = create_ioc_container(Context)

try:
    with CONTAINER.arm(Context()):
        print "first arm fails halfway"
except Exception as ex:
    print "Recovered from exception: {} -- but arm state is corrupt.".format(ex)

with CONTAINER.arm(Context()):
    print "second arm fails due to assertion on corrupted state"

Can be fixed by updating _integrate_resources to first attempt to obtain all resources, then integrate them (so that any exceptions during resource access will abort the operation before any state has been changed).

def _integrate_resources(ioc_container, fast_retrieval_context, payload):
    if not hasattr(fast_retrieval_context, 'resources'):
        fast_retrieval_context.resources = []

    handles_to_resources = {
        get_fast_retrieval_resource_handle(ioc_container, resource_name): getattr(payload, resource_name)
        for resource_name in ioc_container.provides}

    max_handle = max(handles_to_resources)
    current_length = len(fast_retrieval_context.resources)
    if max_handle >= current_length:
        fast_retrieval_context.resources.extend([fast_retrieval_context] * (1 + max_handle - current_length))

    for handle, resource in handles_to_resources.iteritems():
        assert fast_retrieval_context.resources[handle] is fast_retrieval_context
        fast_retrieval_context.resources[handle] = resource