Травление локальных классов завершается ошибкой поиска атрибутов

#python #pickle

Вопрос:

Рассмотрим следующий тестовый код, выполняемый с python -m unittest discover :

 #!/usr/bin/python
"""
Tests for the mappings module.
"""

#Unittest module.
import unittest

#Module to test.
import eeep.lib.collections.mappings as mappings

#Standard library imports.
import pickle


class TestPicklingPointInstances(unittest.TestCase):
    """Test pickling of Point instances."""

    @classmethod
    def setUpClass(cls):
        cls.Point = mappings.makeRecord(["x", "y"],
                                        name = "Point",
                                        doc = "The Point record class.",
                                        qualname = "TestPicklingPointInstances.Point",
                                        module = "eeep.tests.lib.collections.test_mappings")

    def setUp(self):
        self.point = self.Point(x = 0, y = 1)

    def testClassAttributes(self):
        self.assertEqual(self.Point.__qualname__, "TestPicklingPointInstances.Point")
        self.assertEqual(self.Point.__module__, "eeep.tests.lib.collections.test_mappings")    

    def testPickling(self):
        self.assertEqual(self.point, pickle.loads(pickle.dumps(self.point)))

#Run tests.
if __name__ == "__main__":
    unittest.main()
 

Выше приведен код тестирования для фабрики классов makeRecord . Точные детали на самом деле не имеют значения, но это фабрика классов перед классами, напоминающими ваниль namedtuple . Поскольку это класс, который создается во время выполнения, чтобы сделать его экземпляры доступными для маринования, он реализует __getstate__ и __setstate__ . Чтобы охватить случаи, когда класс не находится на уровне модуля, makeRecord имеет qualname и module параметры. Фабрика подбирает их и устанавливает магические атрибуты __qualname__ и __module__ . Это проверено, testClassAttributes и тест проходит с честью. Все остальные функциональные тесты (опущенные для краткости) также проходят. Пока все хорошо.

Однако, когда дело доходит до самого маринования, последний тест заканчивается (путь к файлу переписан с родительским корнем проекта, замененным корнем):

 Traceback (most recent call last):
  File "root/eeep/tests/lib/collections/test_mappings.py", line 173, in testPickling
    self.assertEqual(self.point, pickle.loads(pickle.dumps(self.point)))
_pickle.PicklingError: Can't pickle <class 'eeep.tests.lib.collections.test_mappings.TestPicklingPointInstances.Point'>: attribute lookup TestPicklingPointInstances.Point on eeep.tests.lib.collections.test_mappings failed
 

Я не понимаю, что здесь происходит. Похоже, что при поиске класса механизм травления пытается найти TestPicklingPointInstances.Point (правильный) в eeep.tests.lib.collections.test_mappings (правильно; это также соответствует текущему тестируемому файлу-здесь, по-моему, нет проблем с самостоятельной ссылкой). Файл доступен для импорта (есть __init__.py вход eeep/tests/lib/collections ). Должно быть, я где-то что-то упускаю, скорее всего, во взаимодействии между травильным и импортным оборудованием, но я просто не могу понять, что именно.