Spec-Zone.ru › NumPy 1.21

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/ — каталог модульных тестов

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

Файл 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.

Конструктор 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 — путь к расположению основного файла 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 в список 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 в список include_dirs . Этот список будет виден всем модулям расширения текущего пакета.
  • config.add_headers(*files) — добавляет files в список headers. По умолчанию заголовки будут установлены в каталоге <prefix>/include/pythonX.X/<config.name.replace('.','/')>/. Если элемент files является кортежем, то его первый аргумент указывает суффикс установки относительно пути <prefix>/include/pythonX.X/ . Этот метод Python distutils; его использование не рекомендуется для NumPy и SciPy в пользу config.add_data_files(*files).
  • config.add_scripts(*files) — добавляет files в список scripts . Скрипты будут установлены в каталог <prefix>/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 <funcname>(ext, build_dir): return <source(s) 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, __version__, <packagename>_version в файлах __version__.py, <packagename>_version.py, version.py, __svn_version__.py.
  • 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.get_info(*names) —

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

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

Файлы Fortran

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

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

Именованное правило повторения полезно, когда один и тот же набор повторений должен использоваться несколько раз в блоке. Оно задаётся с помощью <правило1=элемент1, элемент2, элемент3,…, элементN>, где N — количество повторений блока. При каждом повторении блока выражение «<…>» сначала заменяется на элемент1, затем на элемент2 и так далее, пока не будут выполнены все N повторений. После указания именованного правила повторения, его можно использовать в текущем блоке, ссылаясь только на имя (т.е. <правило1>).

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

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

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

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

  • <prefix=s,d,c,z>
  • <_c=s,d,c,z>
  • <_t=вещественное, двойная точность, комплексное, двойная комплексная>
  • <ftype=вещественное, двойная точность, комплексное, двойная комплексная>
  • <ctype=float, double, complex_float, complex_double>
  • <ftypereal=float, double precision, \0, \1>
  • <ctypereal=float, double, \0, \1>

Другие файлы

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

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

Блоки языка шаблонов ограничены строками /**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 к 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 )

Файл __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(‘библиотека’,

sources=[…], config_fc={‘noopt’:(__file__,1)})

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

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

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

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

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

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

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

© 2005–2022 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/1.21/reference/distutils_guide.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API