## Description

Fix `Task.handle_failure` not wrapping the exception correctly in case of unpicklealbe exceptions.

Other changes:
* add the traceback object too when wrapping an unpickleable exception
* added tests to ensure that `Task.handle_failure` and `Task.handle_retry` work as expected

This work is a continuation of the fixes I made in https://github.com/celery/celery/pull/8133.

<!-- Please describe your pull request.

NOTE: All patches should be made against main, not a maintenance branch like
3.1, 2.5, etc.  That is unless the bug is already fixed in main, but not in
that version series.

If it fixes a bug or resolves a feature request,
be sure to link to that issue via (Fixes #4412) for example.
-->

Fix `Task.handle_ignore` not wrapping exceptions properly. ## Description

Fix `Task.handle_failure` not wrapping the exception correctly in case of unpicklealbe exceptions.

Other changes:
* add the traceback object too when wrapping an unpickleable exception
* added tests to ensure that `Task.handle_failure` and `Task.handle_retry` work as expected

This work is a continuation of the fixes I made in https://github.com/celery/celery/pull/8133.

<!-- Please describe your pull request.

NOTE: All patches should be made against main, not a maintenance branch like
3.1, 2.5, etc.  That is unless the bug is already fixed in main, but not in
that version series.

If it fixes a bug or resolves a feature request,
be sure to link to that issue via (Fixes #4412) for example.
-->

import operator
from unittest.mock import Mock

from celery import signals
from celery.concurrency import solo
from celery.utils.functional import noop


class test_solo_TaskPool:

    def test_on_start(self):
        x = solo.TaskPool()
        x.on_start()

    def test_on_apply(self):
        x = solo.TaskPool()
        x.on_start()
        x.on_apply(operator.add, (2, 2), {}, noop, noop)

    def test_info(self):
        x = solo.TaskPool()
        x.on_start()
        assert x.info

    def test_on_worker_process_init_called(self):
        """Upon the initialization of a new solo worker pool a worker_process_init
        signal should be emitted"""
        on_worker_process_init = Mock()
        signals.worker_process_init.connect(on_worker_process_init)
        solo.TaskPool()
        assert on_worker_process_init.call_count == 1

import operator
from unittest.mock import Mock

from celery import signals
from celery.concurrency import solo
from celery.utils.functional import noop


class test_solo_TaskPool:

    def test_on_start(self):
        x = solo.TaskPool()
        x.on_start()

    def test_on_apply(self):
        x = solo.TaskPool()
        x.on_start()
        x.on_apply(operator.add, (2, 2), {}, noop, noop)

    def test_info(self):
        x = solo.TaskPool()
        x.on_start()
        assert x.info

    def test_on_worker_process_init_called(self):
        """Upon the initialization of a new solo worker pool a worker_process_init
        signal should be emitted"""
        on_worker_process_init = Mock()
        signals.worker_process_init.connect(on_worker_process_init)
        solo.TaskPool()
        assert on_worker_process_init.call_count == 1
