-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsetup.py
More file actions
137 lines (108 loc) · 4.56 KB
/
setup.py
File metadata and controls
137 lines (108 loc) · 4.56 KB
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
import os
from pathlib import Path
import sys
from setuptools import Extension # pyright: ignore[reportMissingModuleSource]
from setuptools import setup # pyright: ignore[reportMissingModuleSource]
from setuptools.command.build_ext import build_ext # pyright: ignore[reportMissingModuleSource]
from setuptools.dist import Distribution # pyright: ignore[reportMissingModuleSource]
# Try to import Cython, but make it optional
try:
from Cython.Build import cythonize # pyright: ignore[reportMissingImports]
from Cython.Distutils.build_ext import (
build_ext as cython_build_ext, # pyright: ignore[reportMissingImports]
)
CYTHON_AVAILABLE = True
except ImportError:
CYTHON_AVAILABLE = False
cythonize = None
cython_build_ext = None
def _supports_frame_eval_extension() -> bool:
"""Return whether this interpreter can currently build the frame-eval extension."""
version_tuple = sys.version_info[:2]
return version_tuple in {(3, 11), (3, 12)}
class BuildExt(build_ext):
"""Custom build_ext to handle optional Cython compilation."""
def build_extensions(self):
# If Cython is not available, skip Cython extensions
if not CYTHON_AVAILABLE:
# Filter out Cython extensions
self.extensions = [
ext for ext in self.extensions if not ext.name.startswith("dapper._frame_eval")
]
if not self.extensions:
print("Cython not available - skipping frame evaluation extensions")
return
super().build_extensions()
def get_frame_eval_extensions():
"""Get Cython extensions for frame evaluation if supported."""
if not CYTHON_AVAILABLE or not _supports_frame_eval_extension():
return []
# Get the base directory
base_dir = Path(__file__).parent.resolve()
# Define the frame evaluation extensions
extensions = []
# Core frame evaluator
frame_evaluator_ext = Extension(
"dapper._frame_eval._frame_evaluator",
sources=[str(Path("dapper") / "_frame_eval" / "_frame_evaluator.pyx")],
include_dirs=[str(base_dir / "dapper" / "_frame_eval")],
define_macros=[("NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION")],
extra_compile_args=(
["-O3", "-Wno-unused-function"] if sys.platform != "win32" else ["/O2"]
),
)
extensions.append(frame_evaluator_ext)
return extensions
def get_extensions():
"""Get all extensions for the build."""
extensions = []
# Add frame evaluation extensions if available
frame_eval_exts = get_frame_eval_extensions()
extensions.extend(frame_eval_exts)
# Cythonize extensions if Cython is available
if CYTHON_AVAILABLE and frame_eval_exts and cythonize is not None:
cython_build_dir = os.environ.get("CYTHON_BUILD_DIR", "build/frame-eval/cython")
cython_extensions = cythonize(
extensions,
compiler_directives={
"language_level": "3",
"boundscheck": False,
"wraparound": False,
"initializedcheck": False,
"cdivision": True,
"embedsignature": True,
},
annotate=str(os.environ.get("CYTHON_ANNOTATE", "0")).lower() == "1",
build_dir=str(Path(cython_build_dir)),
)
if cython_extensions is not None:
extensions = cython_extensions
return extensions
# Determine if we should include frame evaluation extensions.
# The current implementation depends on CPython internals and currently builds
# the compiled backend for the supported 3.11-3.12 package targets.
include_frame_eval = CYTHON_AVAILABLE and _supports_frame_eval_extension()
# Custom Distribution class to force platform-specific wheels
class BinaryDistribution(Distribution):
"""Distribution which always forces a binary package with platform name."""
def has_ext_modules(self):
return True
# Setup configuration
# Project metadata lives in pyproject.toml (PEP 621). Keep setup.py focused
# on build-time extension wiring only.
setup_kwargs = {
"distclass": BinaryDistribution,
"cmdclass": {"build_ext": BuildExt},
"options": {
"bdist_wheel": {
"universal": False, # This ensures platform-specific wheels are built
"dist_dir": "dist", # Output directory for wheels
"py_limited_api": False, # Don't use Python's limited API
}
},
}
# Add extensions if frame evaluation is included
if include_frame_eval:
setup_kwargs["ext_modules"] = get_extensions()
# Run setup
setup(**setup_kwargs)