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__.py,<packagename>_version.py,version.py,__svn_version__.pyв поисках строковых переменныхversion,__version__,<packagename>_version. -
config.make_svn_version_py()— добавляет функцию данных в списокdata_files, которая сгенерирует файл__svn_version__.pyв текущий каталог пакета. Файл будет удалён из каталога исходников при завершении работы Python. -
config.get_build_temp_dir()— возвращает путь к временной директории. Это место, где следует создавать временные файлы. -
config.get_distribution()— возвращает экземплярDistributiondistutils. -
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
Этот преобразователь шаблонов будет дублировать все блоки функций и подпрограмм в файле с именами, содержащими ‘<…>’ в соответствии с правилами в ‘<…>’. Количество слов, разделенных запятыми в ‘<…>’, определяет количество повторений блока. Эти слова указывают, на что следует заменить правило повторения ‘<…>’ в каждом блоке. Все правила повторения в блоке должны содержать одинаковое количество слов, разделённых запятыми, указывающее на количество повторений блока. Если слово в правиле повторения требует запятую, левую или правую стрелку, то добавьте перед ним обратный слэш ‘\’. Если слово в правиле повторения соответствует ‘\<индекс>’, то оно будет заменено на <индекс>-е слово в том же спецификации правила повторения. Существует два вида правил повторения: именованное и краткое.
Именованное правило повторения
Именованное правило повторения полезно, когда один и тот же набор повторений должен быть использован несколько раз в блоке. Оно задается с помощью <rule1=item1, item2, item3,…, itemN>, где N — количество повторений блока. При каждом повторении блока все выражение ‘<…>’ будет сначала заменено на item1, затем на item2 и так далее, пока не будут выполнены все N повторений. После того, как именованное правило повторения было введено, то же правило повторения может быть использовано в текущем блоке, просто ссылаясь на его имя (т.е. <rule1>).
Краткое правило повторения
Краткое правило повторения выглядит так: <item1, item2, item3, …, itemN>. Правило определяет, что все выражение ‘<…>’ должно быть заменено сначала на item1, затем на item2 и так далее, пока не будут выполнены все N повторений.
Предопределённые имена
Доступны следующие предопределённые именованные правила повторения:
- <prefix=s,d,c,z>
- <_c=s,d,c,z>
- <_t=real, double precision, complex, double complex>
- <ftype=real, double precision, complex, double complex>
- <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**/.
-
/**begin repeatв отдельной строке отмечает начало сегмента, который должен быть повторен. - Расширения именованных переменных определяются с помощью
#name=item1, item2, item3, ..., itemN#и размещаются в последующих строках. Эти переменные заменяются в каждом блоке повторения соответствующим словом. Все именованные переменные в одном блоке повторения должны определять одинаковое количество слов. - При указании правила повторения для именованной переменной
item*N— это сокращение дляitem, item, ..., itemповторений N раз. Кроме того, скобки в сочетании с*Nмогут использоваться для группирования нескольких элементов, которые должны быть повторены. Таким образом,#name=(item1, item2)*4#эквивалентно#name=item1, item2, item1, item2, item1, item2, item1, item2#. -
*/в отдельной строке отмечает конец именования расширения переменных. Следующая строка — первая строка, которая будет повторена с использованием именованных правил. - Внутри блока, который должен быть повторен, переменные, которые должны быть расширены, указываются как
@name@. -
/**end repeat**/в отдельной строке отмечает предыдущую строку как последнюю строку блока, который должен быть повторен. - Цикл в исходном коде NumPy C может иметь переменную
@TYPE@, предназначенную для подстановки строк, которая предобрабатывается в несколько идентичных циклов с несколькими строками, такими какINT,LONG,UINT,ULONG. Синтаксис@TYPE@таким образом уменьшает дублирование кода и нагрузку по обслуживанию, имитируя языки с универсальной поддержкой типов.
Вышеуказанные правила могут быть более понятными на следующем примере исходного кода шаблона:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
Предобработка исходных файлов 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(‘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–2021 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/1.20/reference/distutils_guide.html