numpy.distutils руководство пользователя#

Предупреждение

numpy.distutils устарел и будет удален для Python >= 3.12. Подробнее см. Статус numpy.distutils и рекомендации по миграции

Структура SciPy#

В настоящее время проект SciPy состоит из двух пакетов:

  • NumPy — предоставляет пакеты, такие как:

    • numpy.distutils - расширение для Python distutils

    • numpy.f2py - инструмент для привязки кодов Fortran/C к Python

    • numpy._core - будущая замена пакетов Numeric и numarray

    • numpy.lib - дополнительные служебные функции

    • numpy.testing - инструменты для модульного тестирования в стиле numpy

    • и т.д.

  • SciPy — коллекция научных инструментов для Python.

Цель этого документа — описать, как добавлять новые инструменты в SciPy.

Требования к пакетам SciPy#

SciPy состоит из пакетов Python, называемых пакетами SciPy, которые доступны пользователям Python через scipy пространство имён. Каждый пакет SciPy может содержать другие пакеты SciPy. И так далее. Поэтому дерево каталогов SciPy представляет собой дерево пакетов с произвольной глубиной и шириной. Любой пакет SciPy может зависеть от пакетов NumPy, но зависимость от других пакетов SciPy должна быть минимальной или нулевой.

Пакет SciPy содержит, в дополнение к своим исходным файлам, следующие файлы и директории:

  • setup.py — создание скрипта

  • __init__.py — инициализатор пакета

  • tests/ — каталог модульных тестов

Их содержимое описано ниже.

The setup.py файл#

Чтобы добавить пакет Python в SciPy, его скрипт сборки (setup.py) должен соответствовать определённым требованиям. Наиболее важное требование — чтобы пакет определял configuration(parent_package='',top_path=None) функция которая возвращает словарь, подходящий для передачи в numpy.distutils.core.setup(..). Чтобы упростить построение этого словаря, numpy.distutils.misc_util предоставляет Configuration класс, описанный ниже.

Пример пакета SciPy на чистом Python#

Ниже приведен пример минимального setup.py файл для чистого пакета SciPy:

#!/usr/bin/env python3
def configuration(parent_package='',top_path=None):
    from numpy.distutils.misc_util import Configuration
    config = Configuration('mypackage',parent_package,top_path)
    return config

if __name__ == "__main__":
    from numpy.distutils.core import setup
    #setup(**configuration(top_path='').todict())
    setup(configuration=configuration)

Аргументы configuration функция указывает имя родительского пакета SciPy (parent_package) и расположение каталога основного setup.py скрипт (top_path). Эти аргументы, вместе с именем текущего пакета, должны быть переданы в Configuration конструктор.

The Configuration конструктор имеет четвертый необязательный аргумент, package_path, который можно использовать, когда файлы пакета расположены в другом месте, чем каталог setup.py файл.

Оставшиеся Configuration аргументы — это все именованные аргументы, которые будут использованы для инициализации атрибутов Configuration экземпляра. Обычно эти ключевые слова совпадают с теми, которые setup(..) функция ожидает, например, packages, ext_modules, data_files, include_dirs, libraries, headers, scripts, package_dir, и т.д. Однако прямое указание этих ключевых слов не рекомендуется, так как содержимое этих аргументов ключевых слов не будет обработано или проверено на согласованность системы сборки SciPy.

Наконец, Configuration имеет .todict() метод, который возвращает все данные конфигурации в виде словаря, подходящего для передачи в setup(..) функция.

Configuration атрибуты экземпляра#

В дополнение к атрибутам, которые можно указать через ключевые аргументы в Configuration конструктор, Configuration экземпляре (обозначим как config) имеет следующие атрибуты, которые могут быть полезны при написании скриптов настройки:

  • config.name - полное имя текущего пакета. Имена родительских пакетов могут быть извлечены как config.name.split('.').

  • config.local_path - путь к текущему местоположению setup.py файл.

  • config.top_path - путь к местоположению main setup.py файл.

Configuration методы экземпляра#

  • config.todict() — возвращает словарь конфигурации, подходящий для передачи в numpy.distutils.core.setup(..) функция.

  • config.paths(*paths) --- applies ``glob.glob(..) к элементам paths при необходимости. Исправляет paths элемент, который относится к config.local_path.

  • config.get_subpackage(subpackage_name,subpackage_path=None) — возвращает список конфигураций подпакетов. Подпакет ищется в текущем каталоге под именем subpackage_name но путь также может быть указан через необязательный subpackage_path аргумент. Если subpackage_name указывается как None тогда имя подпакета будет взято из базового имени subpackage_path. Любой * используемые для имён подпакетов, расширяются как подстановочные знаки.

  • config.add_subpackage(subpackage_name,subpackage_path=None) — добавить конфигурацию подпакета SciPy к текущей. Значение и использование аргументов объяснено выше, см. config.get_subpackage() метод.

  • config.add_data_files(*files) — добавить в начало files to data_files список. Если files элемент является кортежем, то его первый элемент определяет суффикс, куда копируются файлы данных относительно каталога установки пакета, а второй элемент указывает путь к файлам данных. По умолчанию файлы данных копируются в каталог установки пакета. Например,

    config.add_data_files('foo.dat',
                          ('fun',['gun.dat','nun/pun.dat','/tmp/sun.dat']),
                          'bar/car.dat'.
                          '/full/path/to/can.dat',
                          )
    

    установит файлы данных в следующие места

    <installation path of config.name package>/
      foo.dat
      fun/
        gun.dat
        pun.dat
        sun.dat
      bar/
        car.dat
      can.dat
    

    Путь к файлам данных может быть функцией, не принимающей аргументов и возвращающей путь(и) к файлам данных — это полезно, когда файлы данных генерируются во время сборки пакета. (XXX: объяснить шаг, когда эта функция вызывается точно)

  • config.add_data_dir(data_path) — добавить каталог data_path рекурсивно к data_files. Все дерево каталогов, начиная с data_path будет скопирован в каталог установки пакета. Если data_path является кортежем, то его первый элемент определяет суффикс, куда копируются файлы данных относительно каталога установки пакета, а второй элемент указывает путь к каталогу данных. По умолчанию каталог данных копируется в каталог установки пакета под базовым именем data_path. Например,

    config.add_data_dir('fun')  # fun/ contains foo.dat bar/car.dat
    config.add_data_dir(('sun','fun'))
    config.add_data_dir(('gun','/full/path/to/fun'))
    

    установит файлы данных в следующие места

    <installation path of config.name package>/
      fun/
         foo.dat
         bar/
            car.dat
      sun/
         foo.dat
         bar/
            car.dat
      gun/
         foo.dat
         bar/
            car.dat
    
  • config.add_include_dirs(*paths) — добавить в начало paths to include_dirs list. Этот список будет виден всем расширяющим модулям текущего пакета.

  • config.add_headers(*files) — добавить в начало files to headers список. По умолчанию заголовки будут установлены в /include/pythonX.X// каталог. Если files элемент является кортежем, то его первый аргумент определяет суффикс установки относительно /include/pythonX.X/ путь. Это метод Python distutils; его использование не рекомендуется для NumPy и SciPy в пользу config.add_data_files(*files).

  • config.add_scripts(*files) — добавить в начало files to scripts список. Скрипты будут установлены под /bin/ каталог.

  • config.add_extension(name,sources,**kw) — создать и добавить Extension экземпляр для ext_modules список. Первый аргумент name определяет имя модуля расширения, который будет установлен под config.name пакет. Второй аргумент — список источников. add_extension метод также принимает ключевые аргументы, которые передаются в Extension конструктор. Список разрешенных ключевых слов следующий: include_dirs, define_macros, undef_macros, library_dirs, libraries, runtime_library_dirs, extra_objects, extra_compile_args, extra_link_args, export_symbols, swig_opts, depends, language, f2py_options, module_dirs, extra_info, extra_f77_compile_args, extra_f90_compile_args.

    Обратите внимание, что config.paths метод применяется ко всем спискам, которые могут содержать пути. extra_info является словарём или списком словарей, содержимое которого будет добавлено к именованным аргументам. Список depends содержит пути к файлам или каталогам, от которых зависят исходные коды модуля расширения. Если любой путь в depends список новее, чем модуль расширения, то модуль будет пересобран.

    Список источников может содержать функции ('генераторы источников') с шаблоном def (ext, build_dir): return or None>. Если funcname возвращает None, источники не генерируются. И если Extension экземпляр не имеет источников после обработки всех генераторов источников, расширение не будет построено. Это рекомендуемый способ условного определения модулей расширения. Функции генераторов источников вызываются build_src подкоманда numpy.distutils.

    Например, вот типичная функция-генератор источника:

    def generate_source(ext,build_dir):
        import os
        from distutils.dep_util import newer
        target = os.path.join(build_dir,'somesource.c')
        if newer(target,__file__):
            # create target file
        return target
    

    Первый аргумент содержит экземпляр Extension, который может быть полезен для доступа к его атрибутам, таким как depends, sources, и т.д. списки и изменяют их в процессе сборки. Второй аргумент указывает путь к каталогу сборки, который должен использоваться при создании файлов на диске.

  • config.add_library(name, sources, **build_info) — добавить библиотеку в libraries список. Допустимые аргументы ключевых слов: depends, macros, include_dirs, extra_compiler_args, f2py_options, extra_f77_compile_args, extra_f90_compile_args. См. .add_extension() метод для получения дополнительной информации об аргументах.

  • config.have_f77c() — возвращает True, если компилятор Fortran 77 доступен (читай: простой код Fortran 77 успешно скомпилирован).

  • config.have_f90c() — возвращает True, если компилятор Fortran 90 доступен (читай: простой код Fortran 90 успешно скомпилирован).

  • config.get_version() — возвращает строку версии текущего пакета, None если информацию о версии не удалось определить. Этот метод сканирует файлы __version__.py, _version.py, version.py, __svn_version__.py для строковых переменных version, __version__, _version.

  • config.make_svn_version_py() — добавляет функцию данных к data_files список, который сгенерирует __svn_version__.py файл в текущий каталог пакета. Файл будет удален из исходного каталога при выходе из Python.

  • config.get_build_temp_dir() — возвращает путь к временному каталогу. Это место, где следует создавать временные файлы.

  • config.get_distribution() — вернуть distutils Distribution экземпляр.

  • config.get_config_cmd() — возвращает numpy.distutils экземпляр команды config.

  • config.get_info(*names)

Преобразование .src файлы с использованием шаблонов#

NumPy distutils поддерживает автоматическое преобразование исходных файлов с именем .src. Эта возможность может использоваться для поддержания очень похожих блоков кода, требующих лишь простых изменений между блоками. На этапе сборки setup, если встречается файл-шаблон с именем .src, новый файл с именем создаётся из шаблона и помещается в директорию сборки для использования вместо него. Поддерживаются две формы преобразования шаблонов. Первая форма применяется для файлов с именем .ext.src, где ext - распознаваемое расширение Fortran (f, f90, f95, f77, for, ftn, pyf). Вторая форма используется для всех остальных случаев.

Файлы Fortran#

Этот шаблонный конвертер будет реплицировать все функция и подпрограмма блоки в файле с именами, содержащими ‘<…>’ согласно правилам в ‘<…>’. Количество слов, разделенных запятыми, в ‘<…>’ определяет, сколько раз блок повторяется. Значение этих слов указывает, на что должно быть заменено это правило повторения ‘<…>’ в каждом блоке. Все правила повторения в блоке должны содержать одинаковое количество слов, разделенных запятыми, указывающее, сколько раз блок должен повторяться. Если слово в правиле повторения требует запятой, левой или правой стрелки, то перед ним ставится обратная косая черта ‘ '. Если слово в правиле повторения совпадает с ‘ \’, то оно будет заменено на -е слово в той же спецификации повторения. Существует две формы правила повторения: именованная и краткая.

Именованное правило повторения#

Именованное правило повторения полезно, когда один и тот же набор повторений должен использоваться несколько раз в блоке. Оно задаётся с помощью , где N — количество раз, которое блок должен быть повторён. При каждом повторении блока всё выражение ‘<…>’ будет сначала заменено на item1, затем на item2 и так далее, пока не будет выполнено N повторений. После введения именованной спецификации повторения то же правило повторения может быть использовано в текущем блоке путем ссылки только на имя (т.е. ).

Короткое правило повторения#

Краткое правило повторения выглядит как <элемент1, элемент2, элемент3, …, элементN>. Правило указывает, что всё выражение '<…>' должно быть заменено сначала на элемент1, затем на элемент2 и так далее, пока не будет выполнено N повторений.

Предопределенные имена#

Доступны следующие предопределённые именованные правила повторения:

  • <_c=s,d,c,z>

  • <_t=real, double precision, complex, double complex>

Другие файлы#

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

NumPy Distutils предварительно обрабатывает исходные файлы C (расширение: .c.src) написанный на пользовательском языке шаблонов для генерации C-кода. The @ символ используется для обертывания макроподобных переменных, чтобы обеспечить механизм подстановки строк, который может описывать (например) набор типов данных.

Блоки языка шаблонов ограничены /**begin repeat и /**end repeat**/ строки, которые также могут быть вложенными с использованием последовательно нумерованных разделительных строк, таких как /**begin repeat1 и /**end repeat1**/:

  1. /**begin repeat на отдельной строке отмечает начало сегмента, который должен быть повторен.

  2. Именованные расширения переменных определяются с помощью #name=item1, item2, item3, ..., itemN# и размещаются на последовательных строках. Эти переменные заменяются в каждом блоке повтора соответствующим словом. Все именованные переменные в том же блоке повтора должны определять одинаковое количество слов.

  3. При указании правила повторения для именованной переменной, item*N является сокращением для item, item, ..., item повторяется N раз. Кроме того, скобки в сочетании с *N может использоваться для группировки нескольких элементов, которые должны повторяться. Таким образом, #name=(item1, item2)*4# эквивалентно #name=item1, item2, item1, item2, item1, item2, item1, item2#.

  4. */ на отдельной строке отмечает конец именования расширения переменной. Следующая строка — первая строка, которая будет повторена с использованием именованных правил.

  5. Внутри блока, который нужно повторить, переменные, которые должны быть расширены, указываются как @name@.

  6. /**end repeat**/ на отдельной строке отмечает предыдущую строку как последнюю строку блока для повторения.

  7. Цикл в исходном коде NumPy на C может иметь @TYPE@ переменная, предназначенная для подстановки строк, которая предварительно обрабатывается в несколько иначе идентичных циклов с несколькими строками, такими как INT, LONG, UINT, ULONG. @TYPE@ синтаксис стиля таким образом уменьшает дублирование кода и нагрузку на поддержку, имитируя языки с поддержкой обобщённых типов.

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

 1 /* TIMEDELTA to non-float types */
 2
 3 /**begin repeat
 4  *
 5  * #TOTYPE = BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG,
 6  *           LONGLONG, ULONGLONG, DATETIME,
 7  *           TIMEDELTA#
 8  * #totype = npy_byte, npy_ubyte, npy_short, npy_ushort, npy_int, npy_uint,
 9  *           npy_long, npy_ulong, npy_longlong, npy_ulonglong,
10  *           npy_datetime, npy_timedelta#
11  */
12
13 /**begin repeat1
14  *
15  * #FROMTYPE = TIMEDELTA#
16  * #fromtype = npy_timedelta#
17  */
18 static void
19 @FROMTYPE@_to_@TOTYPE@(void *input, void *output, npy_intp n,
20         void *NPY_UNUSED(aip), void *NPY_UNUSED(aop))
21 {
22     const @fromtype@ *ip = input;
23     @totype@ *op = output;
24
25     while (n--) {
26         *op++ = (@totype@)*ip++;
27     }
28 }
29 /**end repeat1**/
30
31 /**end repeat**/

Предварительная обработка файлов исходного кода на языке C с обобщенными типами (как в самом NumPy, так и в любом стороннем пакете, использующем NumPy Distutils) выполняется conv_template.py. Сгенерированные специфичные для типа C-файлы (расширение: .c) этими модулями в процессе сборки готовы к компиляции. Эта форма обобщённой типизации также поддерживается для заголовочных файлов C (предобработанных для создания .h файлы).

Полезные функции в numpy.distutils.misc_util#

  • get_numpy_include_dirs() — возвращает список базовых директорий с заголовочными файлами NumPy. Базовые директории с заголовочными файлами NumPy содержат файлы заголовков, такие как numpy/arrayobject.h, numpy/funcobject.h и т.д. Для установленного NumPy возвращаемый список имеет длину 1, но при сборке NumPy список может содержать больше директорий, например, путь к config.h файл, который numpy/base/setup.py файл генерируется и используется numpy заголовочные файлы.

  • append_path(prefix,path) — умное добавление path to prefix.

  • gpaths(paths, local_path='') — применить glob к путям и добавить префикс local_path при необходимости.

  • njoin(*path) — объединить компоненты пути + преобразовать /-разделенный путь к os.sep-разделенный путь и разрешить .., . из путей. Например, njoin('a',['b','./c'],'..','g') -> os.path.join('a','b','g').

  • minrelpath(path) — разрешает точки в path.

  • rel_path(path, parent_path) — возвращает path относительно parent_path.

  • def get_cmd(cmdname,_cache={}) — возвращает numpy.distutils экземпляр команды.

  • all_strings(lst)

  • has_f_sources(sources)

  • has_cxx_sources(sources)

  • filter_sources(sources) — возвращает c_sources, cxx_sources, f_sources, fmodule_sources

  • get_dependencies(sources)

  • is_local_src_dir(directory)

  • get_ext_source_files(ext)

  • get_script_files(scripts)

  • get_lib_source_files(lib)

  • get_data_files(data)

  • dot_join(*args) — объединить ненулевые аргументы точкой.

  • get_frame(level=0) — возвращает объект фрейма из стека вызовов с заданным уровнем.

  • cyg2win32(path)

  • mingw32() — возвращает True при использовании окружения mingw32.

  • terminal_has_colors(), red_text(s), green_text(s), yellow_text(s), blue_text(s), cyan_text(s)

  • get_path(mod_name,parent_path=None) — возвращает путь модуля относительно parent_path, если он задан. Также обрабатывает __main__ и __builtin__ модули.

  • allpath(name) — заменяет / с os.sep в name.

  • cxx_ext_match, fortran_ext_match, f90_ext_match, f90_module_name_match

numpy.distutils.system_info модуль#

  • get_info(name,notfound_action=0)

  • combine_paths(*args,**kws)

  • show_all()

numpy.distutils.cpuinfo модуль#

  • cpuinfo

numpy.distutils.log модуль#

  • set_verbosity(v)

numpy.distutils.exec_command модуль#

  • get_pythonexe()

  • find_executable(exe, path=None)

  • exec_command( command, execute_in='', use_shell=None, use_tee=None, **env )

The __init__.py файл#

Заголовок типичного SciPy __init__.py равен:

"""
Package docstring, typically with a brief description and function listing.
"""

# import functions into module namespace
from .subpackage import *
...

__all__ = [s for s in dir() if not s.startswith('_')]

from numpy.testing import Tester
test = Tester().test
bench = Tester().bench

Дополнительные возможности в NumPy Distutils#

Указание опций config_fc для библиотек в скрипте setup.py#

Можно указать опции config_fc в скриптах setup.py. Например, используя:

config.add_library('library',
                   sources=[...],
                   config_fc={'noopt':(__file__,1)})

скомпилирует library исходных кодов без флагов оптимизации.

Рекомендуется указывать только те опции config_fc таким образом, чтобы они были независимы от компилятора.

Получение дополнительных опций компилятора Fortran 77 из исходного кода#

Некоторым старым кодам на Fortran требуются специальные опции компилятора для корректной работы. Чтобы указать опции компилятора для каждого исходного файла, numpy.distutils Компилятор Fortran ищет следующий шаблон:

CF77FLAGS(<fcompiler type>) = <fcompiler f77flags>

в первых 20 строках исходного кода и использовать f77flags для указанного типа fcompiler (первый символ C является необязательным).

TODO: Эта функция может быть легко расширена для кодов Fortran 90 тоже. Сообщите нам, если вам нужна такая функция.