-
Notifications
You must be signed in to change notification settings - Fork 0
/
setup.py
328 lines (269 loc) · 10.3 KB
/
setup.py
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
#! /usr/bin/env python
# SPDX-FileCopyrightText: Copyright 2021, Siavash Ameli <sameli@berkeley.edu>
# SPDX-License-Identifier: BSD-3-Clause
# SPDX-FileType: SOURCE
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the license found in the LICENSE.txt file in the root
# directory of this source tree.
# =======
# Imports
# =======
from __future__ import print_function
import os
from os.path import join
import sys
import subprocess
import multiprocessing
# ===============
# install package
# ===============
def install_package(package):
"""
Installs packages using pip.
Example:
.. code-block:: python
>>> install_package('numpy>1.11')
:param package: Name of package with or without its version pin.
:type package: string
"""
subprocess.check_call([sys.executable, "-m", "pip", "install",
"--prefer-binary", package])
# =====================
# Import Setup Packages
# =====================
# Import numpy
try:
from numpy.distutils.misc_util import Configuration
except ImportError:
# Install numpy
install_package('numpy>1.11')
from numpy.distutils.misc_util import Configuration
# Import Cython (to convert pyx to C code)
try:
from Cython.Build import cythonize
except ImportError:
# Install Cython
install_package('cython')
from Cython.Build import cythonize
# =============
# configuration
# =============
def configuration(parent_package='', top_path=None):
"""
A utility function from numpy.distutils.misc_util to compile Fortran and C
codes. This function will be passed to numpy.distutil.core.setup().
"""
config = Configuration(None, parent_package, top_path)
# Define extern directory where external libraries source codes are.
package_name = 'special_functions'
extern_dir_name = '_extern'
extern_dir = os.path.join('.', package_name, extern_dir_name)
macros = []
if sys.platform == 'win32':
macros.append(('_USE_MATH_DEFINES', None))
# amos (fortran library)
config.add_library(
'amos',
sources=[
os.path.join(extern_dir, 'amos', 'mach', '*.f'),
os.path.join(extern_dir, 'amos', 'double_precision', '*.f'),
os.path.join(extern_dir, 'amos', 'single_precision', '*.f')
],
macros=macros)
# cephes (c library)
config.add_library(
'cephes',
sources=[
os.path.join(extern_dir, 'cephes', 'bessel', '*.c'),
os.path.join(extern_dir, 'cephes', 'cprob', '*.c'),
os.path.join(extern_dir, 'cephes', 'eval', '*.c'),
os.path.join(extern_dir, 'cephes', 'cmath', '*.c')
],
include_dirs=[
os.path.join(extern_dir, 'cephes', 'eval')
],
macros=macros)
# If environment var "CYTHON_BUILD_IN_SOURCE" exists, cython creates *.c
# files in the source code, otherwise in /build.
cython_build_in_source = os.environ.get('CYTHON_BUILD_IN_SOURCE', None)
if bool(cython_build_in_source):
cython_build_dir = None # builds *.c in source alongside *.pyx files
else:
cython_build_dir = 'build'
# Cythontize *.pyx files to generate *.c files.
extensions = cythonize(
os.path.join('.', package_name, '*.pyx'),
build_dir=cython_build_dir,
include_path=[os.path.join('.', package_name)],
language_level="3",
nthreads=multiprocessing.cpu_count(),
compiler_directives={
'boundscheck': False,
'cdivision': True,
'wraparound': False,
'nonecheck': False,
'embedsignature': True,
'cpow': True,
'linetrace': True
})
# Add extensions to config per each *.c file
for extension in extensions:
config.add_extension(
extension.name,
sources=extension.sources,
include_dirs=extension.include_dirs,
libraries=['amos', 'cephes'],
language=extension.language,
define_macros=macros)
# Additional files, particularly, the API files to (c)import (*.pxd, *.py)
config.add_data_files(os.path.join(package_name, '*.pxd')) # cython API
config.add_data_files(os.path.join(package_name, '*.py')) # python API
config.add_data_files((package_name, 'LICENSE.txt'))
config.add_data_files((package_name, 'AUTHORS.txt'))
config.add_data_files((package_name, 'README.rst'))
config.add_data_files((package_name, 'CHANGELOG.rst'))
return config
# =====================
# parse setup arguments
# =====================
def parse_setup_arguments():
"""
Checks the arguments of sys.argv. If any build (or related to build)
command was found in the arguments, this function returns True, otherwise
it returns False.
This function is used to determine whether to use the setup() function from
1. numpy.distutil.core.setup() # used if this function returns True
2. setuptools.setup() # used if this function returns False
"""
# List of commands that require build using numpy.distutils
build_commands = ['build', 'build_ext', 'install', 'bdist_wheel', 'sdist',
'develop', 'build_py', 'bdist_rpm', 'bdist_msi',
'bdist_mpkg', 'bdist_wininst', 'build_scripts',
'build_clib']
run_build = False
for build_command in build_commands:
if build_command in sys.argv:
run_build = True
break
return run_build
# ================
# get requirements
# ================
def get_requirements(directory, subdirectory=""):
"""
Returns a list containing the package requirements given in a file named
"requirements.txt" in a subdirectory.
"""
requirements_filename = join(directory, subdirectory, "requirements.txt")
requirements_file = open(requirements_filename, 'r')
requirements = [i.strip() for i in requirements_file.readlines()]
return requirements
# ====
# main
# ====
def main(argv):
directory = os.path.dirname(os.path.realpath(__file__))
package_name = "special_functions"
# Version
# version_dummy = {}
# version_file = os.path.join(directory, package_name, '__version__.py')
# exec(open(version_file, 'r').read(), version_dummy)
# version = version_dummy['__version__']
# del version_dummy
version = '0.0.1'
# Author
author_file = os.path.join(directory, 'AUTHORS.txt')
author = open(author_file, 'r').read().rstrip()
# Requirements
test_requirements = get_requirements(directory, subdirectory="tests")
docs_requirements = get_requirements(directory, subdirectory="docs")
# ReadMe
readme_file = os.path.join(directory, 'README.rst')
long_description = open(readme_file, 'r').read()
# URLs
url = 'https://github.com/ameli/special_functions'
download_url = url + '/archive/main.zip'
documentation_url = url + '/blob/main/README.rst'
tracker_url = url + '/issues'
# inputs to numpy.distutils.core.setup
metadata = dict(
name=package_name,
packages=['special_functions'], # needed after setuptools for py>=3.7
version=version,
author=author,
author_email='sameli@berkeley.edu',
description="Cython and Python API for special functions.",
long_description=long_description,
long_description_content_type='text/x-rst',
keywords="""special-functions bessel-function gamma-function""",
url=url,
download_url=download_url,
platforms=['Linux', 'OSX', 'Windows'],
classifiers=[
'Programming Language :: Cython',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
'License :: OSI Approved :: MIT License',
'Operating System :: Unix',
'Operating System :: POSIX',
'Operating System :: POSIX :: Linux',
'Operating System :: Microsoft :: Windows',
'Operating System :: MacOS',
'Natural Language :: English',
'Intended Audience :: Science/Research',
'Intended Audience :: Developers',
'Topic :: Software Development',
'Topic :: Software Development :: Libraries :: Python Modules',
],
)
# additional inputs that can only be used for setuptools.setup.
# These inputs cannot be used for numpy.distutil.core.setup
additional_metadata = dict(
long_description_content_type='text/x-rst',
project_urls={
"Documentation": documentation_url,
"Source": url,
"Tracker": tracker_url
},
# ext_modules=ExternalModules,
# include_dirs=[numpy.get_include()],
# install_requires=requirements,
packages=['special_functions'],
python_requires='>=3.9',
setup_requires=[
'numpy>1.11',
'cython'],
tests_require=[
'pytest',
'pytest-cov'],
include_package_data=True,
# cmdclass={'build_ext': custom_build_extension},
zip_safe=False, # the package can run out of an .egg file
extras_require={
'test': test_requirements,
'docs': docs_requirements,
},
)
# if run_build is true, we will use numpy.distutils.core.setup to build,
# otherwise, we will use setuptools.setup
run_build = parse_setup_arguments()
# Note: setuptools.setup must be imported before numpy.distutils.core.setup
# so that some input commands (like bdist_wheel) work properly with the
# numpy.distutil.core.setup.
from setuptools import setup
if run_build:
from numpy.distutils.core import setup # noqa: F811
metadata['configuration'] = configuration
else:
metadata.update(additional_metadata)
setup(**metadata)
# ============
# Scipt's Main
# ============
if __name__ == "__main__":
main(sys.argv)