mopr 0.0.70__tar.gz → 0.0.71.dev10__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. {mopr-0.0.70 → mopr-0.0.71.dev10}/AGENTS.md +3 -1
  2. {mopr-0.0.70/mopr.egg-info → mopr-0.0.71.dev10}/PKG-INFO +159 -159
  3. {mopr-0.0.70 → mopr-0.0.71.dev10}/base/__init__.py +15 -1
  4. mopr-0.0.71.dev10/base/language.md +172 -0
  5. mopr-0.0.71.dev10/base/language.py +201 -0
  6. mopr-0.0.71.dev10/base/language_test.py +142 -0
  7. {mopr-0.0.70 → mopr-0.0.71.dev10}/base/region.md +41 -15
  8. {mopr-0.0.70 → mopr-0.0.71.dev10}/base/region.py +32 -0
  9. {mopr-0.0.70 → mopr-0.0.71.dev10}/base/region_test.py +34 -16
  10. {mopr-0.0.70 → mopr-0.0.71.dev10}/base/time.md +9 -2
  11. {mopr-0.0.70 → mopr-0.0.71.dev10}/base/time.py +17 -0
  12. mopr-0.0.71.dev10/finance/exchange/README.md +202 -0
  13. mopr-0.0.71.dev10/finance/exchange/__init__.py +46 -0
  14. mopr-0.0.71.dev10/finance/exchange/authority.py +178 -0
  15. {mopr-0.0.70 → mopr-0.0.71.dev10}/finance/exchange/exchange.py +147 -33
  16. mopr-0.0.71.dev10/finance/exchange/institution.py +216 -0
  17. {mopr-0.0.70 → mopr-0.0.71.dev10}/finance/forex/README.md +2 -1
  18. {mopr-0.0.70 → mopr-0.0.71.dev10}/finance/forex/__init__.py +2 -0
  19. {mopr-0.0.70 → mopr-0.0.71.dev10}/finance/forex/forex.py +20 -8
  20. {mopr-0.0.70 → mopr-0.0.71.dev10/mopr.egg-info}/PKG-INFO +159 -159
  21. {mopr-0.0.70 → mopr-0.0.71.dev10}/mopr.egg-info/SOURCES.txt +5 -0
  22. mopr-0.0.71.dev10/mopr.egg-info/scm_file_list.json +177 -0
  23. mopr-0.0.71.dev10/mopr.egg-info/scm_version.json +8 -0
  24. {mopr-0.0.70 → mopr-0.0.71.dev10}/python/_version.py +24 -24
  25. {mopr-0.0.70 → mopr-0.0.71.dev10}/setup.cfg +4 -4
  26. mopr-0.0.70/finance/exchange/README.md +0 -105
  27. mopr-0.0.70/finance/exchange/__init__.py +0 -27
  28. mopr-0.0.70/mopr.egg-info/scm_file_list.json +0 -172
  29. mopr-0.0.70/mopr.egg-info/scm_version.json +0 -8
  30. {mopr-0.0.70 → mopr-0.0.71.dev10}/.clang-format +0 -0
  31. {mopr-0.0.70 → mopr-0.0.71.dev10}/.clang-tidy +0 -0
  32. {mopr-0.0.70 → mopr-0.0.71.dev10}/.clangd +0 -0
  33. {mopr-0.0.70 → mopr-0.0.71.dev10}/.editorconfig +0 -0
  34. {mopr-0.0.70 → mopr-0.0.71.dev10}/.gitattributes +0 -0
  35. {mopr-0.0.70 → mopr-0.0.71.dev10}/.gitee/ISSUE_TEMPLATE/config.yml +0 -0
  36. {mopr-0.0.70 → mopr-0.0.71.dev10}/.gitee/ISSUE_TEMPLATE/proposal.yml +0 -0
  37. {mopr-0.0.70 → mopr-0.0.71.dev10}/.githooks/commit-msg +0 -0
  38. {mopr-0.0.70 → mopr-0.0.71.dev10}/.gitignore +0 -0
  39. {mopr-0.0.70 → mopr-0.0.71.dev10}/.vscode-default/settings.json +0 -0
  40. {mopr-0.0.70 → mopr-0.0.71.dev10}/.vscode-default/tasks.json +0 -0
  41. {mopr-0.0.70 → mopr-0.0.71.dev10}/.vscode-macos/settings.json +0 -0
  42. {mopr-0.0.70 → mopr-0.0.71.dev10}/.vscode-macos/tasks.json +0 -0
  43. {mopr-0.0.70 → mopr-0.0.71.dev10}/.vscode-windows/launch.json +0 -0
  44. {mopr-0.0.70 → mopr-0.0.71.dev10}/.vscode-windows/settings.json +0 -0
  45. {mopr-0.0.70 → mopr-0.0.71.dev10}/.vscode-windows/tasks.json +0 -0
  46. {mopr-0.0.70 → mopr-0.0.71.dev10}/BUILDING.md +0 -0
  47. {mopr-0.0.70 → mopr-0.0.71.dev10}/CHANGELOG.md +0 -0
  48. {mopr-0.0.70 → mopr-0.0.71.dev10}/CONTRIBUTING.md +0 -0
  49. {mopr-0.0.70 → mopr-0.0.71.dev10}/LICENSE +0 -0
  50. {mopr-0.0.70 → mopr-0.0.71.dev10}/MANIFEST.in +0 -0
  51. {mopr-0.0.70 → mopr-0.0.71.dev10}/README.md +0 -0
  52. {mopr-0.0.70 → mopr-0.0.71.dev10}/THIRD_PARTY_NOTICES.md +0 -0
  53. {mopr-0.0.70 → mopr-0.0.71.dev10}/base/crc32.md +0 -0
  54. {mopr-0.0.70 → mopr-0.0.71.dev10}/base/crc32.py +0 -0
  55. {mopr-0.0.70 → mopr-0.0.71.dev10}/base/crc32_test.py +0 -0
  56. {mopr-0.0.70 → mopr-0.0.71.dev10}/base/cron.md +0 -0
  57. {mopr-0.0.70 → mopr-0.0.71.dev10}/base/cron.py +0 -0
  58. {mopr-0.0.70 → mopr-0.0.71.dev10}/base/cron_test.py +0 -0
  59. {mopr-0.0.70 → mopr-0.0.71.dev10}/base/dataclass_utils.py +0 -0
  60. {mopr-0.0.70 → mopr-0.0.71.dev10}/base/environment.py +0 -0
  61. {mopr-0.0.70 → mopr-0.0.71.dev10}/base/filesystem.py +0 -0
  62. {mopr-0.0.70 → mopr-0.0.71.dev10}/base/mmap.md +0 -0
  63. {mopr-0.0.70 → mopr-0.0.71.dev10}/base/mmap.py +0 -0
  64. {mopr-0.0.70 → mopr-0.0.71.dev10}/base/mmap_test.py +0 -0
  65. {mopr-0.0.70 → mopr-0.0.71.dev10}/base/strings.md +0 -0
  66. {mopr-0.0.70 → mopr-0.0.71.dev10}/base/strings.py +0 -0
  67. {mopr-0.0.70 → mopr-0.0.71.dev10}/base/time_test.py +0 -0
  68. {mopr-0.0.70 → mopr-0.0.71.dev10}/base/timestamp.md +0 -0
  69. {mopr-0.0.70 → mopr-0.0.71.dev10}/base/timestamp.py +0 -0
  70. {mopr-0.0.70 → mopr-0.0.71.dev10}/base/types.py +0 -0
  71. {mopr-0.0.70 → mopr-0.0.71.dev10}/benchmark/README.md +0 -0
  72. {mopr-0.0.70 → mopr-0.0.71.dev10}/benchmark/macro/README.md +0 -0
  73. {mopr-0.0.70 → mopr-0.0.71.dev10}/benchmark/micro/README.md +0 -0
  74. {mopr-0.0.70 → mopr-0.0.71.dev10}/benchmark/micro/crc32_macos_arm64.md +0 -0
  75. {mopr-0.0.70 → mopr-0.0.71.dev10}/benchmark/micro/ringbuffer_macos_arm64.md +0 -0
  76. {mopr-0.0.70 → mopr-0.0.71.dev10}/benchmark/micro/ringbuffer_windows_amd64.md +0 -0
  77. {mopr-0.0.70 → mopr-0.0.71.dev10}/benchmark/micro/wal_macos_arm64.md +0 -0
  78. {mopr-0.0.70 → mopr-0.0.71.dev10}/benchmark/micro/wal_windows_amd64.md +0 -0
  79. {mopr-0.0.70 → mopr-0.0.71.dev10}/benchmark/regression/README.md +0 -0
  80. {mopr-0.0.70 → mopr-0.0.71.dev10}/benchmark/stability/README.md +0 -0
  81. {mopr-0.0.70 → mopr-0.0.71.dev10}/distributed/__init__.py +0 -0
  82. {mopr-0.0.70 → mopr-0.0.71.dev10}/distributed/id/README.md +0 -0
  83. {mopr-0.0.70 → mopr-0.0.71.dev10}/distributed/id/__init__.py +0 -0
  84. {mopr-0.0.70 → mopr-0.0.71.dev10}/distributed/id/generator.py +0 -0
  85. {mopr-0.0.70 → mopr-0.0.71.dev10}/distributed/id/hlc.py +0 -0
  86. {mopr-0.0.70 → mopr-0.0.71.dev10}/distributed/id/id.py +0 -0
  87. {mopr-0.0.70 → mopr-0.0.71.dev10}/distributed/id/option.py +0 -0
  88. {mopr-0.0.70 → mopr-0.0.71.dev10}/distributed/id/queue.py +0 -0
  89. {mopr-0.0.70 → mopr-0.0.71.dev10}/distributed/id/state_store.py +0 -0
  90. {mopr-0.0.70 → mopr-0.0.71.dev10}/distributed/id/tests.py +0 -0
  91. {mopr-0.0.70 → mopr-0.0.71.dev10}/encoding/README.md +0 -0
  92. {mopr-0.0.70 → mopr-0.0.71.dev10}/encoding/__init__.py +0 -0
  93. {mopr-0.0.70 → mopr-0.0.71.dev10}/encoding/charsets.py +0 -0
  94. {mopr-0.0.70 → mopr-0.0.71.dev10}/encoding/csv.md +0 -0
  95. {mopr-0.0.70 → mopr-0.0.71.dev10}/encoding/csv.py +0 -0
  96. {mopr-0.0.70 → mopr-0.0.71.dev10}/encoding/json.md +0 -0
  97. {mopr-0.0.70 → mopr-0.0.71.dev10}/encoding/yaml.md +0 -0
  98. {mopr-0.0.70 → mopr-0.0.71.dev10}/encoding/yaml.py +0 -0
  99. {mopr-0.0.70 → mopr-0.0.71.dev10}/finance/__init__.py +0 -0
  100. {mopr-0.0.70 → mopr-0.0.71.dev10}/finance/exchange/data/ISO10383_MIC.csv +0 -0
  101. {mopr-0.0.70 → mopr-0.0.71.dev10}/finance/exchange/data/mic_codes.txt +0 -0
  102. {mopr-0.0.70 → mopr-0.0.71.dev10}/finance/forex/allratestoday.py +0 -0
  103. {mopr-0.0.70 → mopr-0.0.71.dev10}/finance/forex/bcd.py +0 -0
  104. {mopr-0.0.70 → mopr-0.0.71.dev10}/finance/forex/bog.py +0 -0
  105. {mopr-0.0.70 → mopr-0.0.71.dev10}/finance/forex/cfets.py +0 -0
  106. {mopr-0.0.70 → mopr-0.0.71.dev10}/finance/forex/data/cfets_cny_1994_2005.csv +0 -0
  107. {mopr-0.0.70 → mopr-0.0.71.dev10}/finance/forex/ecb.py +0 -0
  108. {mopr-0.0.70 → mopr-0.0.71.dev10}/finance/forex/exchange_rate_api.py +0 -0
  109. {mopr-0.0.70 → mopr-0.0.71.dev10}/finance/forex/fed.py +0 -0
  110. {mopr-0.0.70 → mopr-0.0.71.dev10}/finance/forex/hkma.py +0 -0
  111. {mopr-0.0.70 → mopr-0.0.71.dev10}/finance/forex/official_source.py +0 -0
  112. {mopr-0.0.70 → mopr-0.0.71.dev10}/finance/forex/xof.py +0 -0
  113. {mopr-0.0.70 → mopr-0.0.71.dev10}/finance/forex/yahoo.py +0 -0
  114. {mopr-0.0.70 → mopr-0.0.71.dev10}/log/__init__.py +0 -0
  115. {mopr-0.0.70 → mopr-0.0.71.dev10}/log/core.py +0 -0
  116. {mopr-0.0.70 → mopr-0.0.71.dev10}/machine/__init__.py +0 -0
  117. {mopr-0.0.70 → mopr-0.0.71.dev10}/machine/id.py +0 -0
  118. {mopr-0.0.70 → mopr-0.0.71.dev10}/mopr.egg-info/dependency_links.txt +0 -0
  119. {mopr-0.0.70 → mopr-0.0.71.dev10}/mopr.egg-info/entry_points.txt +0 -0
  120. {mopr-0.0.70 → mopr-0.0.71.dev10}/mopr.egg-info/requires.txt +0 -0
  121. {mopr-0.0.70 → mopr-0.0.71.dev10}/mopr.egg-info/top_level.txt +0 -0
  122. {mopr-0.0.70 → mopr-0.0.71.dev10}/pyproject.toml +0 -0
  123. {mopr-0.0.70 → mopr-0.0.71.dev10}/python/__init__.py +0 -0
  124. {mopr-0.0.70 → mopr-0.0.71.dev10}/runtime/__init__.py +0 -0
  125. {mopr-0.0.70 → mopr-0.0.71.dev10}/runtime/once.md +0 -0
  126. {mopr-0.0.70 → mopr-0.0.71.dev10}/runtime/once.py +0 -0
  127. {mopr-0.0.70 → mopr-0.0.71.dev10}/runtime/once_test.py +0 -0
  128. {mopr-0.0.70 → mopr-0.0.71.dev10}/runtime/ringbuffer.md +0 -0
  129. {mopr-0.0.70 → mopr-0.0.71.dev10}/runtime/ringbuffer.py +0 -0
  130. {mopr-0.0.70 → mopr-0.0.71.dev10}/runtime/ringbuffer_test.py +0 -0
  131. {mopr-0.0.70 → mopr-0.0.71.dev10}/runtime/scheduler.md +0 -0
  132. {mopr-0.0.70 → mopr-0.0.71.dev10}/runtime/scheduler.py +0 -0
  133. {mopr-0.0.70 → mopr-0.0.71.dev10}/security/README.md +0 -0
  134. {mopr-0.0.70 → mopr-0.0.71.dev10}/security/__init__.py +0 -0
  135. {mopr-0.0.70 → mopr-0.0.71.dev10}/security/audit/README.md +0 -0
  136. {mopr-0.0.70 → mopr-0.0.71.dev10}/security/auth/README.md +0 -0
  137. {mopr-0.0.70 → mopr-0.0.71.dev10}/security/auth/__init__.py +0 -0
  138. {mopr-0.0.70 → mopr-0.0.71.dev10}/security/auth/authorize.py +0 -0
  139. {mopr-0.0.70 → mopr-0.0.71.dev10}/security/auth/catalog.py +0 -0
  140. {mopr-0.0.70 → mopr-0.0.71.dev10}/security/auth/credential.py +0 -0
  141. {mopr-0.0.70 → mopr-0.0.71.dev10}/security/auth/credential_test.py +0 -0
  142. {mopr-0.0.70 → mopr-0.0.71.dev10}/security/auth/errors.py +0 -0
  143. {mopr-0.0.70 → mopr-0.0.71.dev10}/security/auth/model.py +0 -0
  144. {mopr-0.0.70 → mopr-0.0.71.dev10}/security/auth/principal.py +0 -0
  145. {mopr-0.0.70 → mopr-0.0.71.dev10}/security/auth/rbac_test.py +0 -0
  146. {mopr-0.0.70 → mopr-0.0.71.dev10}/security/auth/session.py +0 -0
  147. {mopr-0.0.70 → mopr-0.0.71.dev10}/security/auth/session_test.py +0 -0
  148. {mopr-0.0.70 → mopr-0.0.71.dev10}/security/auth/spi.py +0 -0
  149. {mopr-0.0.70 → mopr-0.0.71.dev10}/security/auth/token.py +0 -0
  150. {mopr-0.0.70 → mopr-0.0.71.dev10}/security/auth/token_test.py +0 -0
  151. {mopr-0.0.70 → mopr-0.0.71.dev10}/security/compliance/README.md +0 -0
  152. {mopr-0.0.70 → mopr-0.0.71.dev10}/spec/api.md +0 -0
  153. {mopr-0.0.70 → mopr-0.0.71.dev10}/spec/behavior.md +0 -0
  154. {mopr-0.0.70 → mopr-0.0.71.dev10}/storage/__init__.py +0 -0
  155. {mopr-0.0.70 → mopr-0.0.71.dev10}/storage/storage.md +0 -0
  156. {mopr-0.0.70 → mopr-0.0.71.dev10}/storage/storage.py +0 -0
  157. {mopr-0.0.70 → mopr-0.0.71.dev10}/storage/wal.md +0 -0
  158. {mopr-0.0.70 → mopr-0.0.71.dev10}/storage/wal.py +0 -0
  159. {mopr-0.0.70 → mopr-0.0.71.dev10}/storage/wal_test.py +0 -0
  160. {mopr-0.0.70 → mopr-0.0.71.dev10}/tools/__init__.py +0 -0
  161. {mopr-0.0.70 → mopr-0.0.71.dev10}/tools/__main__.py +0 -0
  162. {mopr-0.0.70 → mopr-0.0.71.dev10}/tools/license_audit/__init__.py +0 -0
  163. {mopr-0.0.70 → mopr-0.0.71.dev10}/tools/license_audit/__main__.py +0 -0
  164. {mopr-0.0.70 → mopr-0.0.71.dev10}/tools/license_audit/cli.py +0 -0
  165. {mopr-0.0.70 → mopr-0.0.71.dev10}/tools/license_audit/handlers/__init__.py +0 -0
  166. {mopr-0.0.70 → mopr-0.0.71.dev10}/tools/license_audit/handlers/cpp.py +0 -0
  167. {mopr-0.0.70 → mopr-0.0.71.dev10}/tools/license_audit/handlers/go.py +0 -0
  168. {mopr-0.0.70 → mopr-0.0.71.dev10}/tools/license_audit/handlers/java.py +0 -0
  169. {mopr-0.0.70 → mopr-0.0.71.dev10}/tools/license_audit/handlers/python.py +0 -0
  170. {mopr-0.0.70 → mopr-0.0.71.dev10}/tools/license_audit/handlers/rust.py +0 -0
  171. {mopr-0.0.70 → mopr-0.0.71.dev10}/tools/license_audit/notices.py +0 -0
  172. {mopr-0.0.70 → mopr-0.0.71.dev10}/tools/license_audit/report.py +0 -0
  173. {mopr-0.0.70 → mopr-0.0.71.dev10}/tools/license_audit/todo.py +0 -0
  174. {mopr-0.0.70 → mopr-0.0.71.dev10}/tools/license_header/__init__.py +0 -0
  175. {mopr-0.0.70 → mopr-0.0.71.dev10}/tools/license_header/__main__.py +0 -0
  176. {mopr-0.0.70 → mopr-0.0.71.dev10}/tools/license_header/cli.py +0 -0
  177. {mopr-0.0.70 → mopr-0.0.71.dev10}/tools/license_header/core.py +0 -0
  178. {mopr-0.0.70 → mopr-0.0.71.dev10}/tools/license_header/handlers/__init__.py +0 -0
  179. {mopr-0.0.70 → mopr-0.0.71.dev10}/tools/license_header/handlers/base.py +0 -0
  180. {mopr-0.0.70 → mopr-0.0.71.dev10}/tools/license_header/handlers/cpp.py +0 -0
  181. {mopr-0.0.70 → mopr-0.0.71.dev10}/tools/license_header/handlers/css.py +0 -0
  182. {mopr-0.0.70 → mopr-0.0.71.dev10}/tools/license_header/handlers/go.py +0 -0
  183. {mopr-0.0.70 → mopr-0.0.71.dev10}/tools/license_header/handlers/html.py +0 -0
  184. {mopr-0.0.70 → mopr-0.0.71.dev10}/tools/license_header/handlers/java.py +0 -0
  185. {mopr-0.0.70 → mopr-0.0.71.dev10}/tools/license_header/handlers/javascript.py +0 -0
  186. {mopr-0.0.70 → mopr-0.0.71.dev10}/tools/license_header/handlers/python.py +0 -0
  187. {mopr-0.0.70 → mopr-0.0.71.dev10}/tools/license_header/handlers/rust.py +0 -0
  188. {mopr-0.0.70 → mopr-0.0.71.dev10}/tools/license_header/handlers/typescript.py +0 -0
  189. {mopr-0.0.70 → mopr-0.0.71.dev10}/tools/license_header/handlers/vue.py +0 -0
  190. {mopr-0.0.70 → mopr-0.0.71.dev10}/tools/license_header/project_meta.py +0 -0
  191. {mopr-0.0.70 → mopr-0.0.71.dev10}/tools/run_module.py +0 -0
@@ -92,6 +92,8 @@ mopr 同一语义的多语言实现遵循固定的**角色与演进顺序**,
92
92
  - `encoding/csv.h` → `MOPR_ENCODING_CSV_H`;`encoding/charsets.h` → `MOPR_ENCODING_CHARSETS_H`;
93
93
  - `distributed/id/hlc.h` → `MOPR_DISTRIBUTED_ID_HLC_H`;`base/strings.h` → `MOPR_BASE_STRINGS_H`。
94
94
  - 守卫宏与 `#pragma once` 并存;`#endif` 行以注释回写宏名(如 `#endif // MOPR_ENCODING_CSV_H`)。
95
+ - **平台宏一律走 `base/feature_detection.h`**:C++ 侧**禁止**裸写 `_WIN32` / `__linux__` / `__APPLE__` / `_MSC_VER` / `__clang__` / `__aarch64__` / `_M_X64` 等编译器与平台原生宏,改用该头带 `MOPR_` 前缀的检测宏(`MOPR_OS_IS_WINDOWS` / `MOPR_OS_IS_LINUX` / `MOPR_OS_IS_APPLE` / `MOPR_COMPILER_IS_MSVC` / `MOPR_COMPILER_IS_CLANG` / `MOPR_COMPILER_IS_GNU_LIKE` / `MOPR_CPU_IS_X86_64` / `MOPR_CPU_IS_ARM_64` / `MOPR_ENDIAN_LITTLE` 等)。`base/feature_detection.h` 是全仓**唯一**允许裸写原生宏的文件(它必须,否则无从检测);编译期能定的放这里,运行期收敛(函数包装)放 `base/platform.h`。理由:无前缀的 `OS_IS_*` / `TARGET_*` / `FORCE_INLINE` 与 Boost、Qt 等第三方存在撞名风险,且未定义宏在 `#if` 中静默求值为 0——拼错不报错、只会静默走错分支。
96
+ - 替换既有裸宏须逐处确认语义等价,**不可机械替换**:`defined(__GNUC__) && !defined(_MSC_VER)`(GNU 扩展属性是否可用)**不能**写成 `MOPR_COMPILER_IS_GCC`(会漏掉 clang,且在 clang-cl 下语义相反),应写 `MOPR_COMPILER_IS_GNU_LIKE`;只判 `MOPR_CPU_IS_ARM_64` 而漏掉编译器判断,会让 MSVC ARM64 误入 GNU 内联汇编分支。
95
97
  - 新增公共头文件一律遵循上述规则。既有不合规处按模块逐个收敛(`runtime::*` → `mopr::runtime::*`、`base/filesystem.h` 的 `filesystem` → `mopr::base::filesystem`、`base/except.h` 的 `quant1x` → `mopr::base`、`base/mmap.h` 的全局 `file_mmap` / `mmap_open` / `mmap_flush` / `mmap_close` → `mopr::base` 内的 `MmapHandle` / `mmap_open` / `mmap_flush` / `mmap_close`),不做一次性大范围改名。
96
98
  - **公开符号命名跨语言一致**(同一语义在各语言同名,便于跨语言对照与回溯):
97
99
  - **snake_case 优先(铁律)**:只要目标语言允许小写蛇形,公开函数名就**必须**用 snake_case,**不得**用驼峰(lowerCamel / PascalCase)。此处"跨语言一致"指**语义名一致**,不是字形一致——**禁止**拿"要与某语言同名"当作 Python / C++ 保留驼峰的理由:
@@ -107,7 +109,7 @@ mopr 同一语义的多语言实现遵循固定的**角色与演进顺序**,
107
109
  - **句柄操作遵循语言惯例**:Python / Rust / Go 用句柄方法(`flush` / `close`、`Flush` / `Close`),C++ 句柄是 POD,用同名自由函数(`mmap_flush` / `mmap_close`)与之一一对应。
108
110
  - **同一语义可同时提供「公开 API + RAII 类」两层**:第一层为跨平台公开 API(如 `mmap_open` / `mmap_flush` / `mmap_close` + 句柄),资源由调用方显式管理;第二层为跨语言同名的 RAII 类(如 `MappedMemory`:`MappedMemory.open` 打开,析构 / `Drop` / `with` / `defer` 自动释放)。两层共用同一实现、语义必须一致,区别仅在释放时机;模块文档须给出两层对照表(示例见 `base/mmap.md`「1. 分层与命名」)。
109
111
  - **RAII 类名不得与模块名重复**:类名不叫 `mmap` / `Mmap`(与模块名、包名重复,且易被误读为句柄),统一用其语义名(如 `MappedMemory`),并跨语言同名;句柄另用 `XxxHandle`(如 `MmapHandle`,四语言同名,C++ 亦为 `struct MmapHandle`)。
110
- - 已知遗留(需按模块逐一评估,勿批量替换):`base/common.h` 的全局 typedef 与常量(`i8` / `u8` / … 为标量别名、非结构体 typedef,不适用 `mopr_` 前缀约定),`base/format.h` 的全局探测模板与 `std::vector` 的 `operator<<`(依赖 ADL / 非限定查找,移入命名空间会导致使用方找不到),`tests/` 下 `*_test_types` 等测试辅助命名空间。
112
+ - 已知遗留(需按模块逐一评估,勿批量替换):`base/types.h` 的跨语言标量别名(`i8` / `u8` / … 为逐字符对齐 Rust 原生整型名而保留,是标量别名、非结构体 typedef,不适用 `mopr_` 前缀约定;由原 `base/common.h` 的全局 typedef 收敛而来,仅用于有 `.rs` 对照实现的模块,勿把现有 `int64_t` 批量改写为 `i64`),`base/format.h` 的全局探测模板与 `std::vector` 的 `operator<<`(依赖 ADL / 非限定查找,移入命名空间会导致使用方找不到),`tests/` 下 `*_test_types` 等测试辅助命名空间。
111
113
 
112
114
  ## 测试规范
113
115
 
@@ -1,159 +1,159 @@
1
- Metadata-Version: 2.4
2
- Name: mopr
3
- Version: 0.0.70
4
- Summary: Monorepo Optimal Portable Runtime
5
- Author: Quant1X
6
- Author-email: Quant1X <wangfengxy@sina.cn>
7
- License-Expression: AGPL-3.0-only
8
- Project-URL: Homepage, https://github.com/quant1x/mopr
9
- Keywords: monorepo,portable-runtime,benchmark,performance
10
- Requires-Python: >=3.12
11
- Description-Content-Type: text/markdown
12
- License-File: LICENSE
13
- Requires-Dist: loguru>=0.7.3
14
- Requires-Dist: python-dotenv>=1.0.0
15
- Requires-Dist: pandas>=2.1.0
16
- Requires-Dist: apscheduler~=3.11.3
17
- Requires-Dist: PyYAML>=6.0
18
- Requires-Dist: requests>=2.32.0
19
- Dynamic: license-file
20
-
21
- # MOPR
22
-
23
- > **单仓最优可移植运行时**
24
- > *Monorepo Optimal Portable Runtime*
25
- >
26
- > 统一规范,基准驱动,**性能第一,正确与稳定是隐含前提**。
27
- >
28
- > 读作 *M-O-P-R*(或 *mopr*,/mɑpr/)。
29
-
30
- ---
31
-
32
- ## 项目简介
33
-
34
- MOPR 是在统一规范约束下,以多轮基准测试为验证手段,从所有异构实现中筛选并维护性能最优运行时解的项目。它不是“基础库”,也不是“标准实现”——它的定位是**标杆实现**:为同一份规范下的其他实现提供可量化的性能参照与验证基准。
35
-
36
- ### 核心概念
37
-
38
- | 概念 | 定义 | 职责 |
39
- | --- | --- | --- |
40
- | Spec | 规范 | 定义行为契约与接口边界,划定实现必须遵守的范围 |
41
- | Benchmark | 基准测试 | 以多轮、多维度的量化测试验证实现,产出排名依据 |
42
- | MOPR | 标杆实现 | 满足 Spec 与 Benchmark 验证的前提下,性能最优的实现 |
43
-
44
- ## 设计原则
45
-
46
- ### 1. 性能第一,正确与稳定是隐含前提
47
-
48
- 性能是优化方向与排名依据的第一优先级。正确性与稳定性并非与性能并列的取舍维度,而是参与性能排名的前置条件:不满足正确性与稳定性验证的实现,不具备参与性能排名的资格。
49
-
50
- 每一轮基准测试同时覆盖三个维度:
51
-
52
- - **正确性**:输出是否符合 Spec 定义的行为契约
53
- - **稳定性**:长时间运行、压力与边界条件下是否可靠
54
- - **性能**:在前两者满足的前提下,是否达到最优
55
-
56
- 正确性与稳定性作为过滤条件,性能作为排序依据。
57
-
58
- ### 2. Spec 统一,实现自由
59
-
60
- Spec 定义行为契约与接口边界。同一份规范下允许存在多种异构实现,MOPR 是其中经过多轮基准测试反复验证、在目标平台上始终正确且性能最优的实现,作为其他实现的对标基准。
61
-
62
- ### 3. 基准测试是设计驱动力
63
-
64
- 每个设计决策都必须回答一个问题:**该改动是否提升性能?** 其成立前提是不得破坏正确性与稳定性——若基准数据改善但正确性或稳定性测试失败,则该改动不成立。
65
-
66
- ### 4. 可移植性不等于性能下限
67
-
68
- MOPR 在不同平台上分别追求该平台能达到的最高性能,而非让所有平台退化为同一套低性能通用实现。平台相关的极致优化是核心策略,但每份平台实现都必须先通过正确性与稳定性验证。
69
-
70
- ## 基准测试体系
71
-
72
- 基准测试套件按验证目标分为四类:
73
-
74
- | 目录 | 类型 | 验证目标 |
75
- | --- | --- | --- |
76
- | `benchmark/micro/` | 微基准 | 吞吐、延迟、内存占用 |
77
- | `benchmark/macro/` | 场景基准 | 端到端性能 |
78
- | `benchmark/stability/` | 稳定性测试 | 长时间运行、压力、边界条件 |
79
- | `benchmark/regression/` | 回归检测 | 性能劣化监测 |
80
-
81
- 只有通过正确性与稳定性验证的实现,其性能数据才纳入排名与对照。
82
-
83
- ## 项目结构
84
-
85
- ```
86
- mopr/
87
- ├── spec/ # 规范定义
88
- │ ├── api.md # 接口契约
89
- │ └── behavior.md # 行为语义
90
- ├── base/ # 基础组件:cache / mem / mmap / cron / timestamp ...
91
- ├── runtime/ # 运行时:ringbuffer / scheduler / once ...
92
- ├── distributed/ # 分布式:id(HLC / 生成器 / 状态存储)
93
- ├── encoding/ # 编码:csv / charsets / json / base64 / barcode ...
94
- ├── storage/ # 存储抽象
95
- ├── security/ # 安全:auth / audit / compliance
96
- ├── machine/ # 机器标识
97
- ├── log/ # 日志
98
- ├── finance/ # 金融:forex / exchange
99
- ├── benchmark/ # 基准测试套件
100
- │ ├── micro/ # 微基准:吞吐、延迟、内存
101
- │ ├── macro/ # 场景基准:端到端性能
102
- │ ├── stability/ # 稳定性:长时间运行、压力、边界
103
- │ └── regression/ # 性能回归检测
104
- ├── tests/ # 跨语言测试用例
105
- ├── docs/ # 设计决策记录
106
- │ └── proposals/ # 提案(RFC):跨语言语义 / 契约变更的决策过程
107
- └── tools/ # 辅助工具(许可审计、许可证头)
108
- ```
109
-
110
- **仓库根即实现根**:不设 `src/` 层,功能模块目录直接平铺在仓库根,同一功能的多语言实现共处同一目录、同名不同后缀(如 `base/mmap.py` / `base/mmap.h` / `base/mmap.rs` / `base/mmap/mmap.go`;各语言入口 `main.cpp` / `main.rs` 同样平铺在根)。
111
-
112
- - **与 Go 的 package 对齐**:Go 的包边界以目录划分,功能模块目录因此与 Go 的 `package` 一一对应(`distributed/id` 即 `package id`);C++ 命名空间第二级(`distributed/id/hlc.h` → `mopr::distributed::id`)、Python 子模块路径、Rust `mod` 路径均取自同一目录路径,保证同一功能在各语言的模块语义相同。
113
- - **平台无关核心与平台极致优化就地组织**:核心语义落在各模块的公共实现中,平台相关优化留在同一模块内,按文件名后缀(`_posix` / `_windows` / `_unsupported`)或条件编译区分,不另设 `core/` / `platform/` 目录。
114
-
115
- 这些约定只服务一件事:**让同一语义在各语言占据同一位置**——看到 `mmap.py`,顺手就能看到 `mmap.h` / `mmap.rs` / `mmap/mmap.go`,跨语言对照不必先找路径。完整论证(层级为何由语言工具链决定、按语言分目录为何是反例、对 AI 的友好之处)见 [docs/directory-layout-rationale.md](docs/directory-layout-rationale.md)。
116
-
117
- ## 语言与版本要求
118
-
119
- MOPR 是单仓多语言库(monolibrary),同一份规范下将来会有多种语言实现。各语言的版本基线由本项目独立维护,不要求与同系列 `quant1x` 仓库同步:
120
-
121
- | 语言 | 最低版本 | 推荐版本 |
122
- | --- | --- | --- |
123
- | Python | 3.12+ | 3.12.x |
124
- | C++ | C++20 | GCC 13+ / Clang 17+ / MSVC 14.3+ |
125
- | Rust | 1.98.1+ | 1.98.1+(2024 edition) |
126
- | Go | 1.27+ | 1.27.x |
127
- | Java | 8+ | JDK 8 及以上(见 `pom.xml` 的 `java.version`) |
128
-
129
- - 当前仓库实现以 **C++20 + MSVC v143(Visual Studio 2022)+ CMake(≥3.25)/ Conan 2** 为主,构建细节见 [BUILDING.md](BUILDING.md)。
130
- - 后续新增 Python / Rust / Go / Java 实现时,须遵循上表最低版本基线,不得使用低于基线的语言特性与 API;版本表变更由本项目独立评估。
131
-
132
- ## 快速开始
133
-
134
- 当前 C++ 工程(CMake + Conan 2)的构建与编译步骤见 [BUILDING.md](BUILDING.md)。
135
-
136
- ```text
137
- // TODO: 基准运行示例,待接口定义完成后补充
138
- ```
139
-
140
- ## 项目状态与路线图
141
-
142
- 🚧 早期阶段 — Spec 正在起草中,实现尚未稳定。
143
-
144
- 当前推进顺序:
145
-
146
- 1. 起草 Spec:确定 `spec/api.md`(接口契约)与 `spec/behavior.md`(行为语义)
147
- 2. 搭建基准测试套件与验证流程
148
- 3. 基于平台无关核心实现首个基准实现
149
- 4. 开展平台相关优化,并逐平台完成正确性、稳定性与性能验证
150
-
151
- ## License / 协议
152
-
153
- 本项目采用 **GNU Affero General Public License v3**([AGPL-3.0](LICENSE) / SPDX: `AGPL-3.0-only`)授权。
154
-
155
- - 允许自由使用、复制、修改与再分发,但派生作品必须同样以 AGPLv3 发布(强 Copyleft)。
156
- - 通过网络提供服务的修改版本(含远程交互)须按第 13 条向用户开放对应源码。
157
- - 完整条款见根目录 [LICENSE](LICENSE) 文件。
158
-
159
- Copyright (C) 2026 Quant1X <wangfengxy@sina.cn>
1
+ Metadata-Version: 2.4
2
+ Name: mopr
3
+ Version: 0.0.71.dev10
4
+ Summary: Monorepo Optimal Portable Runtime
5
+ Author: Quant1X
6
+ Author-email: Quant1X <wangfengxy@sina.cn>
7
+ License-Expression: AGPL-3.0-only
8
+ Project-URL: Homepage, https://github.com/quant1x/mopr
9
+ Keywords: monorepo,portable-runtime,benchmark,performance
10
+ Requires-Python: >=3.12
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: loguru>=0.7.3
14
+ Requires-Dist: python-dotenv>=1.0.0
15
+ Requires-Dist: pandas>=2.1.0
16
+ Requires-Dist: apscheduler~=3.11.3
17
+ Requires-Dist: PyYAML>=6.0
18
+ Requires-Dist: requests>=2.32.0
19
+ Dynamic: license-file
20
+
21
+ # MOPR
22
+
23
+ > **单仓最优可移植运行时**
24
+ > *Monorepo Optimal Portable Runtime*
25
+ >
26
+ > 统一规范,基准驱动,**性能第一,正确与稳定是隐含前提**。
27
+ >
28
+ > 读作 *M-O-P-R*(或 *mopr*,/mɑpr/)。
29
+
30
+ ---
31
+
32
+ ## 项目简介
33
+
34
+ MOPR 是在统一规范约束下,以多轮基准测试为验证手段,从所有异构实现中筛选并维护性能最优运行时解的项目。它不是“基础库”,也不是“标准实现”——它的定位是**标杆实现**:为同一份规范下的其他实现提供可量化的性能参照与验证基准。
35
+
36
+ ### 核心概念
37
+
38
+ | 概念 | 定义 | 职责 |
39
+ | --- | --- | --- |
40
+ | Spec | 规范 | 定义行为契约与接口边界,划定实现必须遵守的范围 |
41
+ | Benchmark | 基准测试 | 以多轮、多维度的量化测试验证实现,产出排名依据 |
42
+ | MOPR | 标杆实现 | 满足 Spec 与 Benchmark 验证的前提下,性能最优的实现 |
43
+
44
+ ## 设计原则
45
+
46
+ ### 1. 性能第一,正确与稳定是隐含前提
47
+
48
+ 性能是优化方向与排名依据的第一优先级。正确性与稳定性并非与性能并列的取舍维度,而是参与性能排名的前置条件:不满足正确性与稳定性验证的实现,不具备参与性能排名的资格。
49
+
50
+ 每一轮基准测试同时覆盖三个维度:
51
+
52
+ - **正确性**:输出是否符合 Spec 定义的行为契约
53
+ - **稳定性**:长时间运行、压力与边界条件下是否可靠
54
+ - **性能**:在前两者满足的前提下,是否达到最优
55
+
56
+ 正确性与稳定性作为过滤条件,性能作为排序依据。
57
+
58
+ ### 2. Spec 统一,实现自由
59
+
60
+ Spec 定义行为契约与接口边界。同一份规范下允许存在多种异构实现,MOPR 是其中经过多轮基准测试反复验证、在目标平台上始终正确且性能最优的实现,作为其他实现的对标基准。
61
+
62
+ ### 3. 基准测试是设计驱动力
63
+
64
+ 每个设计决策都必须回答一个问题:**该改动是否提升性能?** 其成立前提是不得破坏正确性与稳定性——若基准数据改善但正确性或稳定性测试失败,则该改动不成立。
65
+
66
+ ### 4. 可移植性不等于性能下限
67
+
68
+ MOPR 在不同平台上分别追求该平台能达到的最高性能,而非让所有平台退化为同一套低性能通用实现。平台相关的极致优化是核心策略,但每份平台实现都必须先通过正确性与稳定性验证。
69
+
70
+ ## 基准测试体系
71
+
72
+ 基准测试套件按验证目标分为四类:
73
+
74
+ | 目录 | 类型 | 验证目标 |
75
+ | --- | --- | --- |
76
+ | `benchmark/micro/` | 微基准 | 吞吐、延迟、内存占用 |
77
+ | `benchmark/macro/` | 场景基准 | 端到端性能 |
78
+ | `benchmark/stability/` | 稳定性测试 | 长时间运行、压力、边界条件 |
79
+ | `benchmark/regression/` | 回归检测 | 性能劣化监测 |
80
+
81
+ 只有通过正确性与稳定性验证的实现,其性能数据才纳入排名与对照。
82
+
83
+ ## 项目结构
84
+
85
+ ```
86
+ mopr/
87
+ ├── spec/ # 规范定义
88
+ │ ├── api.md # 接口契约
89
+ │ └── behavior.md # 行为语义
90
+ ├── base/ # 基础组件:cache / mem / mmap / cron / timestamp ...
91
+ ├── runtime/ # 运行时:ringbuffer / scheduler / once ...
92
+ ├── distributed/ # 分布式:id(HLC / 生成器 / 状态存储)
93
+ ├── encoding/ # 编码:csv / charsets / json / base64 / barcode ...
94
+ ├── storage/ # 存储抽象
95
+ ├── security/ # 安全:auth / audit / compliance
96
+ ├── machine/ # 机器标识
97
+ ├── log/ # 日志
98
+ ├── finance/ # 金融:forex / exchange
99
+ ├── benchmark/ # 基准测试套件
100
+ │ ├── micro/ # 微基准:吞吐、延迟、内存
101
+ │ ├── macro/ # 场景基准:端到端性能
102
+ │ ├── stability/ # 稳定性:长时间运行、压力、边界
103
+ │ └── regression/ # 性能回归检测
104
+ ├── tests/ # 跨语言测试用例
105
+ ├── docs/ # 设计决策记录
106
+ │ └── proposals/ # 提案(RFC):跨语言语义 / 契约变更的决策过程
107
+ └── tools/ # 辅助工具(许可审计、许可证头)
108
+ ```
109
+
110
+ **仓库根即实现根**:不设 `src/` 层,功能模块目录直接平铺在仓库根,同一功能的多语言实现共处同一目录、同名不同后缀(如 `base/mmap.py` / `base/mmap.h` / `base/mmap.rs` / `base/mmap/mmap.go`;各语言入口 `main.cpp` / `main.rs` 同样平铺在根)。
111
+
112
+ - **与 Go 的 package 对齐**:Go 的包边界以目录划分,功能模块目录因此与 Go 的 `package` 一一对应(`distributed/id` 即 `package id`);C++ 命名空间第二级(`distributed/id/hlc.h` → `mopr::distributed::id`)、Python 子模块路径、Rust `mod` 路径均取自同一目录路径,保证同一功能在各语言的模块语义相同。
113
+ - **平台无关核心与平台极致优化就地组织**:核心语义落在各模块的公共实现中,平台相关优化留在同一模块内,按文件名后缀(`_posix` / `_windows` / `_unsupported`)或条件编译区分,不另设 `core/` / `platform/` 目录。
114
+
115
+ 这些约定只服务一件事:**让同一语义在各语言占据同一位置**——看到 `mmap.py`,顺手就能看到 `mmap.h` / `mmap.rs` / `mmap/mmap.go`,跨语言对照不必先找路径。完整论证(层级为何由语言工具链决定、按语言分目录为何是反例、对 AI 的友好之处)见 [docs/directory-layout-rationale.md](docs/directory-layout-rationale.md)。
116
+
117
+ ## 语言与版本要求
118
+
119
+ MOPR 是单仓多语言库(monolibrary),同一份规范下将来会有多种语言实现。各语言的版本基线由本项目独立维护,不要求与同系列 `quant1x` 仓库同步:
120
+
121
+ | 语言 | 最低版本 | 推荐版本 |
122
+ | --- | --- | --- |
123
+ | Python | 3.12+ | 3.12.x |
124
+ | C++ | C++20 | GCC 13+ / Clang 17+ / MSVC 14.3+ |
125
+ | Rust | 1.98.1+ | 1.98.1+(2024 edition) |
126
+ | Go | 1.27+ | 1.27.x |
127
+ | Java | 8+ | JDK 8 及以上(见 `pom.xml` 的 `java.version`) |
128
+
129
+ - 当前仓库实现以 **C++20 + MSVC v143(Visual Studio 2022)+ CMake(≥3.25)/ Conan 2** 为主,构建细节见 [BUILDING.md](BUILDING.md)。
130
+ - 后续新增 Python / Rust / Go / Java 实现时,须遵循上表最低版本基线,不得使用低于基线的语言特性与 API;版本表变更由本项目独立评估。
131
+
132
+ ## 快速开始
133
+
134
+ 当前 C++ 工程(CMake + Conan 2)的构建与编译步骤见 [BUILDING.md](BUILDING.md)。
135
+
136
+ ```text
137
+ // TODO: 基准运行示例,待接口定义完成后补充
138
+ ```
139
+
140
+ ## 项目状态与路线图
141
+
142
+ 🚧 早期阶段 — Spec 正在起草中,实现尚未稳定。
143
+
144
+ 当前推进顺序:
145
+
146
+ 1. 起草 Spec:确定 `spec/api.md`(接口契约)与 `spec/behavior.md`(行为语义)
147
+ 2. 搭建基准测试套件与验证流程
148
+ 3. 基于平台无关核心实现首个基准实现
149
+ 4. 开展平台相关优化,并逐平台完成正确性、稳定性与性能验证
150
+
151
+ ## License / 协议
152
+
153
+ 本项目采用 **GNU Affero General Public License v3**([AGPL-3.0](LICENSE) / SPDX: `AGPL-3.0-only`)授权。
154
+
155
+ - 允许自由使用、复制、修改与再分发,但派生作品必须同样以 AGPLv3 发布(强 Copyleft)。
156
+ - 通过网络提供服务的修改版本(含远程交互)须按第 13 条向用户开放对应源码。
157
+ - 完整条款见根目录 [LICENSE](LICENSE) 文件。
158
+
159
+ Copyright (C) 2026 Quant1X <wangfengxy@sina.cn>
@@ -11,7 +11,7 @@ base 各单文件模块的公共函数与类型经 __init__ 直接导出, 业务
11
11
  """
12
12
 
13
13
  # 模块对象保留为包属性; environment 依赖 filesystem, 故 filesystem 在前
14
- from . import cron, crc32, dataclass_utils, environment, filesystem, mmap, region, strings, time, timestamp
14
+ from . import cron, crc32, dataclass_utils, environment, filesystem, language, mmap, region, strings, time, timestamp
15
15
 
16
16
  # cron
17
17
  from .cron import CronExpr, make_cron, make_cron_for_region, resolve_timezone
@@ -23,6 +23,15 @@ from .dataclass_utils import get_field_names
23
23
  from .filesystem import expand_user, homedir, mkdirs, touch, update_file_mtime
24
24
  # environment
25
25
  from .environment import app_entry, env, getuser, read_dotenv
26
+ # language (region 依赖它, 故在其前)
27
+ from .language import (
28
+ DEFAULT_LANGUAGE,
29
+ Language,
30
+ language_chain,
31
+ language_code,
32
+ language_parent,
33
+ parse_language,
34
+ )
26
35
  # mmap (MappedMemory / MmapHandle 名称过于通用, 连同两层 API 一并保留在 mopr.base.mmap 内)
27
36
  from .mmap import MmapError, mmap_open
28
37
  # time
@@ -43,6 +52,7 @@ from .time import (
43
52
  market_fiscal_year_start_month,
44
53
  market_reporting_frequency,
45
54
  to_rfc1123,
55
+ utc_offset_text,
46
56
  )
47
57
  # timestamp
48
58
  from .timestamp import Timestamp
@@ -64,10 +74,14 @@ __all__ = [
64
74
  "expand_user", "homedir", "mkdirs", "touch", "update_file_mtime",
65
75
  # environment
66
76
  "app_entry", "env", "getuser", "read_dotenv",
77
+ # language
78
+ "DEFAULT_LANGUAGE", "Language", "language_chain", "language_code",
79
+ "language_parent", "parse_language",
67
80
  # mmap
68
81
  "MmapError", "mmap_open",
69
82
  # time
70
83
  "from_rfc1123", "get_quarter_by_date", "get_timezone_offset_standard", "to_rfc1123",
84
+ "utc_offset_text",
71
85
  # time (财报周期)
72
86
  "DEFAULT_FISCAL_YEAR_START_MONTH",
73
87
  "DISCLOSURE_DEADLINE_DAYS_BY_MARKET",
@@ -0,0 +1,172 @@
1
+ # language:跨语言语种组件(i18n 查询键)
2
+
3
+ `base/language.md` 是本仓库 `base/` 下 language 多语言实现的对齐说明与 API 对照。
4
+
5
+ language 收敛**语种(BCP 47 语言标签,含地区变体子标签)**,是 base 多语言模型的第一层(L1)。它解决一个具体问题:**需要多语言文字的入口,该以什么为查询键**。
6
+
7
+ > **一等公民**:本组件与 `region` / `time` / `strings` 同等待遇 —— Python(spec)、C++(生产)、Rust / Go(扩展)**各有实现与单测**,Java 随 `base.Region` 一并跟进;不因"只是给 display_name 打个底"而只留 Python 一份。
8
+
9
+ > **为何叫 `language` 而不叫 `i18n`**:`i18n`(internationalization)是**工程领域**名,指"让产品支持多语言"的整套能力 —— 消息目录、复数规则、日期/数字/货币格式化、排序、双向文字等;本组件只收敛其中的**语种标签**这一个值类型(外加它的解析与父子回退链),即 i18n 的**查询键 / 入参**,而非 i18n 本身。把值类型改叫 `i18n` 会占住地名:将来真要落消息目录与格式化时,`base/i18n` 往哪放?同类的自证反例是 `Region.language` —— 它返回"该地官方语言",改叫 `Region.i18n` 显然不通。故本组件保持 `language`(与 BCP 47 的 *language tag* 同义、与 `region` / `time` / `cron` 同族),在文档中标明它是 **i18n 体系的第一层(L1)**;完整 i18n 设施将来另立模块,本组件作为其依赖与查询键被调用(见 §5.2)。
10
+
11
+ 各语言遵循仓库固定的**语言角色与演进顺序**(见根 `AGENTS.md`「语言角色与演进顺序」):
12
+
13
+ 1. **Python 为 spec**:先行实现,确定语义、枚举成员与默认行为,是本组件的语义参照;
14
+ 2. **C++ 为生产实现**:Python spec 确定后落地(header-only);
15
+ 3. **Rust / Go 为扩展**:随后跟进,与 spec 保持 API/语义对齐。
16
+
17
+ **对齐原则**:以 Python(spec)为准绳——成员集、码值与解析/回退语义完全一致。本文档按 `python → c++ → rust → go` 顺序描述语言,对照表列序与此一致。
18
+
19
+ ## 1. 定位与文件清单
20
+
21
+ | 角色 | 语言 | 文件 | 类型 | 说明 |
22
+ | --- | --- | --- | --- | --- |
23
+ | spec | Python | `base/language.py` | `class Language(Enum)`(`@unique`) | 先行定语义;含 `parse_language` / `language_code` |
24
+ | 生产 | C++ | `base/language.h` | `enum class Language : uint8_t` | header-only(`inline` 函数),`mopr::base` |
25
+ | 扩展 | Rust | `base/language.rs` | `enum Language` | 纯 `std`(无外部依赖),含单测 |
26
+ | 扩展 | Go | `base/language.go` | `type Language string` + 常量 | 与 `region.go` 同包 |
27
+
28
+ > **当前状态:Python / C++ / Rust / Go 已落地(四语言逐值对齐),Java 待跟进**(见 §5.1)。
29
+
30
+ ## 2. 为何语种独立于地区(Region)
31
+
32
+ **地区与语种是正交的两件事**,拿地区推定语种必然出错:
33
+
34
+ | 反例 | 说明 |
35
+ | --- | --- |
36
+ | 一地区多官方语言 | `HK` 中英、`DJ` 阿法——单个地区码无法表达"读者要哪一种" |
37
+ | 一语言跨多地区 | `fr` 同属 `UEMOA` / `DJ` / `FR` / `CA`——**"法语区"在 `Region` 里没有单一成员可传** |
38
+ | 属地 ≠ 界面语言 | 读者在法语区、软件界面是英文(真实常见)——按属地推定会给出读者看不懂的文字 |
39
+ | 语种变体无对应地区 | 本库登记 `zh-TW` 而**没有** `Region.TW` —— 语种按**读者**分片,不按地区分片;若只有地区能表达语种,就会迫使后人为了语种去补地区,把两个维度重新焊死 |
40
+
41
+ 结论:**i18n 的查询键是 `Language`,不是 `Region`**。二者的分工是:
42
+
43
+ | 概念 | 定位 | 谁持有 | 用途 |
44
+ | --- | --- | --- | --- |
45
+ | `Language` | i18n **查询键**(读者要哪种文字) | 调用方显式传入 | 严格口径:`display_name(Language.FR)` |
46
+ | `Region.language` | 地区的**属性**(该地官方文件第一语言) | `base.Region` | 推定口径:仅当"只知属地、不知语种"时用 |
47
+
48
+ `Region.language` **不是**被废除,而是**降级为推定入口**:它的返回值是 `Language`(类型化、非空),但用它取文字等于替读者做了选择,文档必须标注为推定。`base/region.md` §2 的官方语言口径(双语地区取官方文件第一语言)保持不变,只是值的类型从字符串码升级为 `Language` 成员。
49
+
50
+ ## 3. 取词回退链(i18n fallback)
51
+
52
+ 需要文字的入口(现为 `finance.exchange` 的 `Institution.display_name`)一律按**固定回退链**取词,四语言同一实现、同一顺序。
53
+
54
+ 回退的**方向是 BCP 47 截断(具体到泛)**,由 `language_chain(l)` 给出候选序列(含自身、不含 `UNKNOWN`):
55
+
56
+ | 目标语种 | 候选序列 | 说明 |
57
+ | --- | --- | --- |
58
+ | `ZH_TW` | `zh-TW` → `zh` | 地区变体无独立登记 → 回落到通用中文 |
59
+ | `ZH_HK` | `zh-HK` → `zh` | 同上 |
60
+ | `EN` | `en` | 命中即止 |
61
+ | `FR` | `fr` | 无登记 → 交由取词入口的终档 `EN` |
62
+ | `UNKNOWN` | (空) | 不进回退链,直接取 `DEFAULT_LANGUAGE` |
63
+
64
+ 取词入口遍历候选序列,逐档判定:
65
+
66
+ 1. **候选有登记名称** → 用它(如 `ZH` → `official_name`、`EN` → `english_name`)。繁体地区变体 `zh-HK` / `zh-TW` 现阶段无独立登记,沿链回落到 `ZH`,给**简体**中文名 —— 降级但不错给:繁体读者读简体中文名远好过读英文名;
67
+ 2. **候选未登记** → 回落到**该机构法域的官方语言**原名 `native_name`(⏳ 待登记,见 §5.3);
68
+ 3. **序列走完仍未命中** → 回落到 `EN`(英文是唯一要求全机构必填的语种)。
69
+
70
+ > 回退链的原则:**可以降级,不可错给**。宁可给英文(读者普遍能读),不可返回中文形态——中文名对法语读者与英文读者同样无意义。回退是**显式且可预测**的,禁止"查不到就给个大概"。
71
+
72
+ `EN` 之所以是终点而非 `ZH`:本库主语境是中文,但英文名是唯一**全机构必填**的名称(见 `finance/exchange/README.md` §5.1),覆盖面确定;中文名是本库的主语境但并非跨语种通用。
73
+
74
+ ## 4. 成员集与取值
75
+
76
+ 成员**按需登记**,不做 ISO 639-1 / BCP 47 全量表;新增语种必须有消费方(某地区的 `Region.language`、某机构的登记名称,或明确的读者侧需求)。
77
+
78
+ | 成员(码) | 语种 | 父标签 | 登记依据 |
79
+ | --- | --- | --- | --- |
80
+ | `ZH`(`zh`) | 中文(本库主语境与默认语种;简体形态) | — | `Region.CN`;回退链中 `zh-HK` / `zh-TW` 的落点 |
81
+ | `ZH_HK`(`zh-HK`) | 香港中文(繁体 + 港式用词) | `ZH` | `Region.HK` |
82
+ | `ZH_TW`(`zh-TW`) | 台湾中文(繁体 + 台澎金马用词) | `ZH` | **读者侧** —— 本库无 `Region.TW`,语种登记不依赖地区 |
83
+ | `EN`(`en`) | 英文 | — | `Region.US` / `UK` / `EU` / `SG` / `GH`;**回退链终点** |
84
+ | `FR`(`fr`) | 法文 | — | `Region.UEMOA` / `Region.DJ` |
85
+ | `JA`(`ja`) | 日文 | — | `Region.JP` |
86
+ | `UNKNOWN`(`""`) | 未知 / 无语种可依 | — | `Region.GLB` / `Region.UNKNOWN` 的 `language`;码为**空串**(非 ISO 639 的 `und`,本库只需"有没有语种"这一个判断) |
87
+
88
+ > **为何登记 `zh-HK` / `zh-TW` 而不只分繁简**:"繁体中文"不是一种文字 —— 港台同用繁体字却**用词不同**(港:巴士 / 的士 / 按揭;台:公车 / 计程车 / 房贷)。只按正字法分(`zh-Hant`)会把港台合并,与 §2 第 1 类错(一地区多语言被压缩)是同一个毛病。故本层**登记到地区子标签**,正字法由标签隐含(`zh-HK` / `zh-TW` 均隐含 Hant),不单独登记 `zh-Hant` 成员 —— 那是 `Locale` 第二层的事(见 §5.2)。
89
+ >
90
+ > `zh-TW` 现在**没有**对应的 `Region` 成员,这是刻意的:语种按读者分片,不按地区分片。不要为了"对称"去补 `Region.TW`(补地区要连带货币 `TWD`、时区 `Asia/Taipei` 与外汇源,是独立议题)。
91
+
92
+ ## 4.1 API 对照
93
+
94
+ | 语义 | Python | C++ | Rust | Go |
95
+ | --- | --- | --- | --- | --- |
96
+ | 语种码(BCP 47) | `language_code(l)` | `language_code(Language)` | `l.code()` / `Display` | `l.Code()` |
97
+ | 解析(宽容,未知→`UNKNOWN`) | `parse_language(s)` | `parse_language(std::string_view)` | `parse_language(&str)` | `ParseLanguage(s)` |
98
+ | 是否未知 | `l.is_unknown`(property) | `language_is_unknown(l)` | `l.is_unknown()` | `l.IsUnknown()` |
99
+ | 父标签(去掉最右子标签) | `language_parent(l)` | `language_parent(Language)` | `l.parent()` | `l.Parent()` |
100
+ | 回退链候选序列 | `language_chain(l)` → `tuple` | `language_chain(Language)` → `vector` | `l.chain()` → `Vec` | `l.Chain()` → `[]Language` |
101
+ | 默认语种常量 | `DEFAULT_LANGUAGE`(`Language.ZH`) | `kDefaultLanguage` | `DEFAULT_LANGUAGE` | `DefaultLanguage` |
102
+
103
+ **解析宽容度**:四语言一律**宽容**,未知码返回 `Language.UNKNOWN`、不抛异常——语种来自用户设置 / 系统 locale / 配置,属不可信输入,解析失败应降级到默认语种而非中断调用方。这与 `base/region.md` §5.1 记录的 `Region` 解析宽容度差异(C++ 宽容、Rust/Go 报错)**不同**,是本组件显式选定的新口径,勿照搬 `Region`。
104
+
105
+ **解析按 BCP 47 lookup(RFC 4647 §3.4)**:先匹配**最具体**的整串,失配则逐级去掉最右子标签重试,命中第一个已登记者即止。
106
+
107
+ **接受的输入形态**(四语言一致,单元测试逐条锁定):
108
+
109
+ | 输入 | 结果 | 说明 |
110
+ | --- | --- | --- |
111
+ | `"fr"` / `"FR"` / `" fr "` | `Language.FR` | 忽略大小写与首尾空白 |
112
+ | `"zh-HK"` / `"ZH-hk"` / `"zh_TW"` | `Language.ZH_HK` / `ZH_HK` / `ZH_TW` | **整串命中**:地区变体不退化成语言子标签;下划线视同连字符 |
113
+ | `"zh-Hant-HK"` / `"zh-Hant"` | `Language.ZH` | 逐级截断:`zh-Hant` 未登记 → 落到 `zh` |
114
+ | `"fr-FR"` / `"en_US"` / `"ja-JP"` | `Language.FR` / `EN` / `JA` | 同上,逐级截断 |
115
+ | `""` / `None` / `"xx"` / `"-"` | `Language.UNKNOWN` | 宽容降级 |
116
+
117
+ > 解析**不**推断地区语义:`fr-FR` 里的 `FR` 不会被读成 `Region`。已登记的地区变体(`zh-HK` / `zh-TW`)是**成员**而非推断结果 —— 它们是按需登记的查询键,不是"由地区推出来的语言"。
118
+
119
+ ## 5. 待办(TODO)
120
+
121
+ ### 5.1 跨语言跟进(Python / C++ / Rust / Go 已落地)
122
+
123
+ | 语言 | 实现 | 单测 | 状态 |
124
+ | --- | --- | --- | --- |
125
+ | Python(spec) | `base/language.py` | `base/language_test.py`(unittest,7 用例) | ✅ |
126
+ | C++(生产) | `base/language.h`(header-only) | `tests/base/language_test.cpp`(gtest) | ✅ |
127
+ | Rust(扩展) | `base/language.rs` | 同文件内联 `#[cfg(test)]`(5 用例) | ✅ |
128
+ | Go(扩展) | `base/language.go` | `base/language_test.go`(6 用例) | ✅ |
129
+ | Java | — | — | ⏳ 随 `base.Region` 一并落地 |
130
+
131
+ - [x] 四语言各自的成员集(7 个)、码值、解析形态表、父标签与回退链**逐值比对**一致;
132
+ - [x] `base.Region.language` 四语言同步落地(C++ `region_language(r)` / Rust `r.language()` / Go `r.Language()`),返回 `Language` 而非字符串码(见 `base/region.md` §8.2);
133
+ - [ ] Java:`base/Language.java` + `Region.language()` 返回 `Language`(待 `base.Region` 落地时一并实现)。
134
+
135
+ ### 5.2 第二层 `Locale`(BCP 47,⏳ 未立项)
136
+
137
+ 第一层已覆盖**语种 + 地区变体**(`zh-HK` / `zh-TW`),第二层要补的是**正字法与格式**,与前者不是一回事:
138
+
139
+ | 维度 | 归哪层 | 现状 |
140
+ | --- | --- | --- |
141
+ | 语言(`zh` / `en` / `fr` / `ja`) | L1 `Language` | ✅ 已落地 |
142
+ | 地区变体(`zh-HK` / `zh-TW`) | L1 `Language` | ✅ 已落地(用词差异) |
143
+ | 正字法(`zh-Hans` / `zh-Hant`) | L2 `Locale` | ⏳ 待触发 |
144
+ | 数字 / 日期 / 货币格式 | L2 `Locale` | ⏳ 待触发 |
145
+
146
+ **触发条件**(满足任一才立项,不因"完整"而提前铺):
147
+
148
+ - 需要按**正字法**取词(同一机构名的简繁两种写法要分别登记);
149
+ - 需要区分**同一语种的地区格式**(`en-US` / `en-GB` 的日期与数字格式);
150
+ - 需要 `script` 子标签单独参与取词(即"繁体但不分港台"这一档)。
151
+
152
+ 立项前**不引入**:`display_name` 的入参只有 `Language` 一种(外加 `Region` 推定重载),不引入三态参数。`parse_language` 已能吃下完整 locale 串(`zh-Hant-HK` → `ZH`),且 `zh-HK` / `zh-TW` **整串命中**,故现在传 locale 字符串也不会报错,未登记的部分按 lookup 逐级忽略 —— 这是有意的平滑过渡。
153
+
154
+ ### 5.3 语言自身的展示名(⏳ 未登记)
155
+
156
+ `english_name`(Chinese / French)与 `native_name`(中文 / Français)暂不登记:当前无消费方(无语言选择器)。待出现"需要把语种本身显示给用户"的场景时补,届时按 §4 的按需登记原则一并加自检。
157
+
158
+ ## 6. 验证
159
+
160
+ | 语言 | 命令 | 覆盖 |
161
+ | --- | --- | --- |
162
+ | Python | `python -m mopr.base.language_test`(需在仓库根的父目录运行,或把仓库根加入 `PYTHONPATH`,见根 `AGENTS.md`) | 成员集恰为 7、码值逐值、解析形态表、`parent` / `chain`、`UNKNOWN` 非空串兜底、默认语种 |
163
+ | C++ | `cmake --build --preset <配置> --target mopr_tests` 后运行(gtest) | 与 Python **同一张成员表与解析形态表** |
164
+ | Rust | `cargo test --lib base::language` | 同上 |
165
+ | Go | `go test ./base/ -run Language -v` | 同上 |
166
+
167
+ 交互冒烟(四语言同解):
168
+
169
+ - `parse_language("zh-TW")` → `ZH_TW`、`parse_language("zh-Hant-HK")` → `ZH`、`parse_language("xx")` → `UNKNOWN`;
170
+ - `language_chain(ZH_TW)` → `ZH_TW, ZH`;`language_chain(UNKNOWN)` → 空(取默认语种)。
171
+
172
+ 跨语言一致性:以 7 个码(`zh` / `zh-HK` / `zh-TW` / `en` / `fr` / `ja` / `""`)在各语言做"码 → Language → 码"回环断言比对,并比对 §4.1 的解析形态表与父子链。