# Buggy code
def activity_stream_delete(sender, instance, **kwargs):
    if not activity_stream_enabled:
        return
    # TODO: remove deprecated_group conditional in 3.3
    # Skip recording any inventory source directly associated with a group.
    if isinstance(instance, InventorySource) and instance.deprecated_group:
        return
    # Inventory delete happens in the task system rather than request-response-cycle.
    # If we trigger this handler there we may fall into db-integrity-related race conditions.
    # So we add flag verification to prevent normal signal handling. This funciton will be
    # explicitly called with flag on in Inventory.schedule_deletion.
    changes = {}
    if isinstance(instance, Inventory):
        if not kwargs.get("inventory_delete_flag", False):
            return
        # Add additional data about child hosts / groups that will be deleted
        changes["coalesced_data"] = {
            "hosts_deleted": instance.hosts.count(),
            "groups_deleted": instance.groups.count(),
        }
    elif isinstance(instance, (Host, Group)) and instance.inventory.pending_deletion:
        return  # accounted for by inventory entry, above
    _type = type(instance)
    if getattr(_type, "_deferred", False):
        return
    changes.update(model_to_dict(instance, model_serializer_mapping()))
    object1 = camelcase_to_underscore(instance.__class__.__name__)
    if type(instance) == OAuth2AccessToken:
        changes["token"] = CENSOR_VALUE
    activity_entry = get_activity_stream_class()(
        operation="delete",
        changes=json.dumps(changes),
        object1=object1,
        actor=get_current_user_or_none(),
    )
    activity_entry.save()
    connection.on_commit(lambda: emit_activity_stream_change(activity_entry))


# Traceback (AttributeError)
Traceback (most recent call last): File "/var/lib/awx/venv/awx/lib64/python3.6/site-packages/awx/main/tasks.py", line 1308, in run args = self.build_args(self.instance, private_data_dir, passwords) File "/var/lib/awx/venv/awx/lib64/python3.6/site-packages/awx/main/tasks.py", line 2472, in build_args if getattr(settings, '%s_EXCLUDE_EMPTY_GROUPS' % src.upper()): File "/var/lib/awx/venv/awx/lib64/python3.6/site-packages/awx/conf/settings.py", line 511, in __getattr_without_cache__ return getattr(self._wrapped, name) File "/var/lib/awx/venv/awx/lib64/python3.6/site-packages/awx/conf/settings.py", line 418, in __getattr__ value = self._get_default(name) File "/var/lib/awx/venv/awx/lib64/python3.6/site-packages/awx/conf/settings.py", line 405, in _get_default return getattr(self.default_settings, name) AttributeError: 'Settings' object has no attribute '_EXCLUDE_EMPTY_GROUPS'

# Fixed code
def activity_stream_delete(sender, instance, **kwargs):
    if not activity_stream_enabled:
        return
    # Inventory delete happens in the task system rather than request-response-cycle.
    # If we trigger this handler there we may fall into db-integrity-related race conditions.
    # So we add flag verification to prevent normal signal handling. This funciton will be
    # explicitly called with flag on in Inventory.schedule_deletion.
    changes = {}
    if isinstance(instance, Inventory):
        if not kwargs.get("inventory_delete_flag", False):
            return
        # Add additional data about child hosts / groups that will be deleted
        changes["coalesced_data"] = {
            "hosts_deleted": instance.hosts.count(),
            "groups_deleted": instance.groups.count(),
        }
    elif isinstance(instance, (Host, Group)) and instance.inventory.pending_deletion:
        return  # accounted for by inventory entry, above
    _type = type(instance)
    if getattr(_type, "_deferred", False):
        return
    changes.update(model_to_dict(instance, model_serializer_mapping()))
    object1 = camelcase_to_underscore(instance.__class__.__name__)
    if type(instance) == OAuth2AccessToken:
        changes["token"] = CENSOR_VALUE
    activity_entry = get_activity_stream_class()(
        operation="delete",
        changes=json.dumps(changes),
        object1=object1,
        actor=get_current_user_or_none(),
    )
    activity_entry.save()
    connection.on_commit(lambda: emit_activity_stream_change(activity_entry))