Шаг 193.
Python: сборник рецептов. Модули и пакеты. Загрузка модулей с удаленного компьютера с использованием хуков импортирования

    На этом шаге мы рассмотрим несколько способов решения этой задачи.

Задача

    Вы хотите настроить инструкцию импортирования Python, чтобы она могла прозрачно загружать модули с удаленного компьютера.

Решение

    Во-первых, сразу отметим важность соблюдения правил безопасности. Идея, которая обсуждается в этом рецепте, станет ужасной без некого дополнительного слоя аутентификации и безопасности. Цель этого рецепта - глубоко погрузиться во внутренний механизм работы инструкции import. Если вы заставите этот рецепт работать и поймете внутреннюю механику, то получите серьезный фундамент знаний для приспособления import под практически любые нужды. Имея это в виду, давайте продолжим!

    В сердце этого рецепта лежит желание расширить функциональность инструкции import. Есть несколько подходов к тому, как можно это сделать, но для наглядности начнем с создания такого каталога с кодом Python:

testcode/
    spam.py
    fib.py
    grok/
        __init__.py
        blah.py

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

# spam.py 
print("I'm spam")

def hello(name):
    print('Hello %s' % name)

# fib.py 
print("I'm fib")

def fib(n):
    if n < 2:
        return 1
    else:
        return fib(n-1) + fib(n-2)

# grok/__init__.py
print("I'm grok.__init__")

# grok/blah.py 
print("I'm grok.blah")

    Наша цель - разрешить удаленный доступ к этим файлам, как к модулям. Вероятно, наиболее простой способ сделать это - опубликовать их на веб-сервере. Просто зайдите в каталог testcode и запустите Python таким образом:

bash % cd testcode
bash % python3 -m http.server 15000
Serving HTTP on 0.0.0.0 port 15000 ...

    Оставим этот сервер в запущенном состоянии и отдельно запустим интерпретатор Python. Убедитесь, что вы имеете доступ к удаленным файлам, используя urllib. Например:

>>> from urllib.request import urlopen
>>> u = urlopen('http://localhost:15000/fib.py')
>>> data = u.read().decode('utf-8')
>>> print(data)
# fib.py 
print("I'm fib")

def fib(n):
    if n < 2:
        return 1
    else:
        return fib(n-1) + fib(n-2)
>>>

    Загрузка исходного кода с сервера - это база для оставшейся части этого рецепта. Если конкретно, то вместо ручного получения файла с исходным кодом с использованием urlopen() мы переделаем инструкцию import так, что она будет делать это "за кулисами".

    Первый подход к загрузке удаленного модуля - создать явную функцию загрузки, которая будет этим заниматься. Например:

import imp
import urllib.request
import sys

def load_module(url):
    u = urllib.request.urlopen(url)
    source = u.read().decode('utf-8')
    mod = sys.modules.setdefault(url, imp.new_module(url))
    code = compile(source, url, 'exec')
    mod.__file__ = url
    mod.__package__ = ''
    exec(code, mod.__dict__)
    return mod

    Эта функция просто загружает исходный код, компилирует его в объект кода, используя compile(), а затем выполняет словарь созданного объекта модуля. Вот как вы могли бы использовать эту функцию:

>>> fib = load_module('http://localhost:15000/fib.py')
I'm fib
>>> fib.fib(10)
89
>>> spam = load_module('http://localhost:15000/spam.py')
I'm spam
>>> spam.hello('Guido')
Hello Guido 
>>> fib
<module 'http://localhost:15000/fib.py' from 'http://localhost:15000/fib.py'>
>>> spam
<module 'http://localhost:15000/spam.py' from 'http://localhost:15000/spam.py'>
>>>

    Как вы можете видеть, это работает для простых модулей. Однако функция не подключена к обычной инструкции import, а расширение кода для поддержки более продвинутых конструкций, таких как пакеты, потребует дополнительной работы.

    На следующем шаге мы продолжим изучение этого вопроса.




Предыдущий шаг Содержание Следующий шаг