Python: как написать универсальный читатель файлов с плагинами формата - PullRequest
0 голосов
/ 30 марта 2011

Я пытаюсь написать универсальный читатель всех видов медицинских изображений, которые мы встречаем.Я подумал, давайте учиться у профессионалов и пошли имитировать, как PIL читает файлы («Библиотека изображений Python», Форматы).

Насколько я понимаю, в PIL есть открытая функция, котораязацикливает список возможных функций принятия.Когда кто-то работает, он использует соответствующую фабричную функцию для создания экземпляра соответствующего объекта.

Итак, я пошел на это, и мои (урезанные) усилия здесь:


pluginID = []     # list of all registered plugin IDs
OPEN = {}         # plugins have open and (maybe) accept functions as a tuple

_initialized = False

import os, sys

def moduleinit():
    '''Explicitly initializes the library.  This function 
    loads all available file format drivers.

    This routine has been lifted from PIL, the Python Image Library'''

    global _initialized
    global pluginID
    if _initialized:
        return 

    visited = {}

    directories = sys.path

    try:
        directories = directories + [os.path.dirname(__file__)]
    except NameError:
        pass

    # only check directories (including current, if present in the path)
    for directory in filter(isDirectory, directories):
        fullpath = os.path.abspath(directory)
        if visited.has_key(fullpath):
            continue
        for file in os.listdir(directory):
            if file[-19:] == "TestReaderPlugin.py":
                f, e = os.path.splitext(file)
                try:
                    sys.path.insert(0, directory)
                    try: # FIXME: this will not reload and hence pluginID 
                        # will be unpopulated leading to "cannot identify format"
                        __import__(f, globals(), locals(), [])
                    finally:
                        del sys.path[0]
                except ImportError:
                    print f, ":", sys.exc_value
        visited[fullpath] = None

    if OPEN:
        _initialized = True
        return 1

class Reader:
    '''Base class for image file format handlers.'''
    def __init__(self, fp=None, filename=None):

        self.filename = filename

        if isStringType(filename):
            import __builtin__
            self.fp = __builtin__.open(filename) # attempt opening

        # this may fail if not implemented
        self._open() # unimplemented in base class but provided by plugins

    def _open(self):
        raise NotImplementedError(
            "StubImageFile subclass must implement _open"
            )


# this is the generic open that tries to find the appropriate handler
def open(fp):
    '''Probe an image file

    Supposed to attempt all opening methods that are available. Each 
    of them is supposed to fail quickly if the filetype is invalid for its 
    respective format'''

    filename=fp

    moduleinit() # make sure we have access to all the plugins

    for i in pluginID:
        try:
            factory, accept = OPEN[i]
            if accept:
                fp = accept(fp)
                # accept is expected to either return None (if unsuccessful) 
                # or hand back a file handle to be used for opening                                 
                if fp:
                    fp.seek(0)  
                    return factory(fp, filename=filename) 
        except (SyntaxError, IndexError, TypeError): 
                pass # I suppose that factory is allowed to have these 
                # exceptions for problems that weren't caught with accept()
                # hence, they are simply ignored and we try the other plugins

    raise IOError("cannot identify format")

# --------------------------------------------------------------------
# Plugin registry

def register_open(id, factory, accept=None):
    pluginID.append(id)
    OPEN[id] = factory, accept

# --------------------------------------------------------------------
# Internal:

# type stuff
from types import  StringType

def isStringType(t):
    return isinstance(t, StringType)

def isDirectory(f):
    '''Checks if an object is a string, and that it points to a directory'''
    return isStringType(f) and os.path.isdir(f)

Важноеза кулисами происходит регистрация всех плагинов формата при первой попытке открыть файл (moduleinit).Каждый подходящий плагин должен находиться в доступном пути и называться * TestReaderPlugin.py.Он будет (динамически) импортирован.Каждый модуль плагина должен вызывать register_open для предоставления идентификатора, метода для создания файла и функции accept для проверки файлов-кандидатов.

Пример плагина будет выглядеть следующим образом:


import TestReader

def _accept(filename):
    fp=open(filename,"r")
    # we made it here, so let's just accept this format
    return fp

class exampleTestReader(TestReader.Reader):
    format='example'

    def _open(self):
        self.data = self.fp.read()

TestReader.register_open('example', exampleTestReader, accept=_accept)

TestReader.open () - это функция, которую будет использовать пользователь:

import TestReader
a=TestReader.open(filename) # easy

Итак, в чем проблема?Во-первых, я все еще нахожусь в поисках питонского пути.Это оно?Мои причины сомневаться в том, что магия на этапе moduleinit выглядит грязной.Копируется прямо из PIL.Основная проблема: если вы перезагрузите (TestReader), все это перестанет работать, потому что ID инициализируется как [], но плагины не будут перезагружены.

Существуют ли более эффективные способы настройки универсального считывателя, который
1. допускает простой вызов open (имя файла) для всех форматов и
2. требует предоставления только красиво инкапсулированных плагинов для любого форматахочешь.
3. работает на перезагрузках?

1 Ответ

0 голосов
/ 30 марта 2011

Некоторые рекомендации:

  1. Используйте понятие "заглянуть" в буфер, чтобы проверить, есть ли данные, которые вы могли бы понять.
  2. Знание имени импортера - это то, что пользователь не хочет знать (что, если у вас есть 100 импортеров) использовать «фасадный» интерфейс medicimage.open (filepath)
  3. Чтобы выполнить перезагрузку, вам нужно реализовать немного логики, есть примеры того, как этого достичь
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...