-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathsetup.py
More file actions
153 lines (122 loc) · 6.2 KB
/
Copy pathsetup.py
File metadata and controls
153 lines (122 loc) · 6.2 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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# -*- coding: utf-8 -*-
"""
MiniBT 构建脚本(方案 A:构建时现场编译 Cython 扩展)
========================================================
本文件只负责 ``ext_modules``;包发现、``package-data``、元数据等仍由
``pyproject.toml`` 统一管理。
**打包前复位 ``LocalDatas`` 索引**(``minibt/data/utils.py``)**已自动化**:
build_py / sdist 命令会先调用 :func:`reset_local_datas_index`(并有“import 到的
必须是本目录源码”的守卫,绝不会去改 site-packages 里那份)。手动兜底::
# 在 minibt/ 的**上一层**目录执行(保证 import 到的是源码)
python -c "import minibt.data.tools as t; print(t.__file__); t.reset_index()"
它调用 ``tools.reset_index()``(= ``rewrite(base_only=True)``),把索引里
的用户名(``~/.minibt/datas``,即 C 盘那份)去掉,只留包内基础数据
(``minibt/data/test/*.csv``)—— 否则这些私人名字会被写进 wheel,而对应
CSV 不在 ``package-data`` 里,别人装完会看到一堆取不到数据的名字。
它**只改索引文件**,不会删任何数据;要让某个名字真正消失,得用
``LocalDatas.deleter('名字')`` / ``LocalDatas.keep('名字')`` 删真实文件。
- 构建 wheel / sdist 时会自动把这些 ``.pyx`` 编译成对应平台的扩展模块,
因此最终 wheel 的平台标签会正确变为 ``cp3xx-cp3xx-<平台>``,不会再被
误标成 ``py3-none-any``。
- 本地开发(不打包)时可单独执行::
python setup.py build_ext --inplace
在源码目录生成 ``.pyd`` / ``.so``,方便直接运行代码。
"""
import os
import numpy as np
from Cython.Build import cythonize
from setuptools import Extension, setup
CYTHON_FUNCTIONS = "minibt/cython_functions"
ZIGZAG = "minibt/zigzag"
# MSVC 用 /O2,其它编译器用 -O3
_extra_compile_args = ["/O2"] if os.name == "nt" else ["-O3"]
def _ext(name: str, source: str) -> Extension:
"""构造一个带 numpy 头文件路径的扩展模块定义。"""
return Extension(
name,
[source],
include_dirs=[np.get_include()],
extra_compile_args=_extra_compile_args,
)
# 需要编译的全部扩展模块(注意:不再包含已废弃的
# "backtrader_from_signals copy 2.pyx")
_ext_modules = [
_ext("minibt.cython_functions.backtest_engine",
f"{CYTHON_FUNCTIONS}/backtest_engine.pyx"),
_ext("minibt.cython_functions.backtrader_from_signals",
f"{CYTHON_FUNCTIONS}/backtrader_from_signals.pyx"),
_ext("minibt.cython_functions.backtrader_pair_from_signals",
f"{CYTHON_FUNCTIONS}/backtrader_pair_from_signals.pyx"),
_ext("minibt.cython_functions.signal_profit",
f"{CYTHON_FUNCTIONS}/signal_profit.pyx"),
_ext("minibt.zigzag.core",
f"{ZIGZAG}/core.pyx"),
]
def _find_datas_utils() -> str:
"""定位包内 ``data/utils.py``(兼容 setup.py 在包内 / 在仓库根两种布局)。"""
here = os.path.dirname(os.path.abspath(__file__))
for relative in ("minibt/data/utils.py", "data/utils.py"):
path = os.path.join(here, relative)
if os.path.exists(path):
return os.path.abspath(path)
return ""
def reset_local_datas_index() -> None:
"""构建前复位 ``LocalDatas`` 索引(``minibt/data/utils.py``,**只列包内基础数据**)。
为什么要做:本机 ``~/.minibt/datas`` 里的私人数据名会被 `rewrite()` 写进
``utils.py``;不打这个补丁,它们就会跟着 wheel 分发给别人(而对应 CSV 不在
``package-data`` 里,装完会看到一堆取不到数据的名字)。
只改索引文件,**不删任何数据**;失败只提示、绝不中断构建。
「解析到的必须是本目录源码」这道守卫很重要:如果 ``import minibt`` 命中的是
**已安装的** site-packages,就会去改别人那份,而不是正在打包的源码。
"""
import subprocess
import sys
target = _find_datas_utils()
if not target:
print("[minibt] 没找到 data/utils.py,跳过 LocalDatas 索引复位")
return
here = os.path.dirname(os.path.abspath(__file__))
code = ("import minibt.data.tools as t; "
"print(t.__file__); t.reset_index()")
env = dict(os.environ)
roots = [here, os.path.dirname(here)]
env["PYTHONPATH"] = os.pathsep.join(
roots + [env.get("PYTHONPATH", "")]).rstrip(os.pathsep)
try:
done = subprocess.run([sys.executable, "-c", code], cwd=here, env=env,
capture_output=True, text=True, timeout=180)
except Exception as error: # 超时 / 没解释器
print(f"[minibt] 跳过 LocalDatas 索引复位:{error}")
return
if done.returncode != 0 or not (done.stdout or "").strip():
tail = (done.stderr or "").strip().splitlines()[-1:] or ["?"]
print(f"[minibt] 跳过 LocalDatas 索引复位:{tail[0]}")
return
resolved = os.path.abspath((done.stdout or "").splitlines()[0].strip())
# 比的是**目录**:`target` 是 utils.py,而 import 出来的是同目录下的 tools.py
if os.path.dirname(resolved) != os.path.dirname(target):
print(f"[minibt] 跳过 LocalDatas 索引复位(import 到的是 {resolved})")
return
print(f"[minibt] LocalDatas 索引已复位(只列包内基础数据):{target}")
class _ResetIndexBeforeBuild:
"""构建命令的 mixin:`run()` 之前先复位 `LocalDatas` 索引。"""
def run(self, *args, **kwargs):
reset_local_datas_index()
return super().run(*args, **kwargs)
def _cmdclass() -> dict:
"""把复位钩子挂到 build_py / sdist 上(`python -m build` 会经过它们)。"""
from setuptools.command.build_py import build_py
from setuptools.command.sdist import sdist
def _with(command):
return type(f"_ResetIndex_{command.__name__}",
(_ResetIndexBeforeBuild, command), {})
return {"build_py": _with(build_py), "sdist": _with(sdist)}
if __name__ == "__main__":
setup(
cmdclass=_cmdclass(),
ext_modules=cythonize(
_ext_modules,
language_level=3,
compiler_directives={"language_level": 3},
),
)