bazi 1.0.0__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 (317) hide show
  1. bazi-1.0.0/.github/workflows/release.yml +256 -0
  2. bazi-1.0.0/LICENSE +21 -0
  3. bazi-1.0.0/MANIFEST.in +16 -0
  4. bazi-1.0.0/PKG-INFO +138 -0
  5. bazi-1.0.0/README.md +125 -0
  6. bazi-1.0.0/RELEASE_NOTES.md +16 -0
  7. bazi-1.0.0/RELEASING.md +65 -0
  8. bazi-1.0.0/Requirements.txt +17 -0
  9. bazi-1.0.0/THIRD_PARTY_NOTICES.md +20 -0
  10. bazi-1.0.0/bazi/__init__.py +31 -0
  11. bazi-1.0.0/bazi/analyzer/__init__.py +5 -0
  12. bazi-1.0.0/bazi/analyzer/relationship.py +641 -0
  13. bazi-1.0.0/bazi/bazi.py +461 -0
  14. bazi-1.0.0/bazi/bazi_chart.py +667 -0
  15. bazi-1.0.0/bazi/calendar/__init__.py +23 -0
  16. bazi-1.0.0/bazi/calendar/backend.py +95 -0
  17. bazi-1.0.0/bazi/calendar/celestial_data/SCHEMA.md +136 -0
  18. bazi-1.0.0/bazi/calendar/celestial_data/__init__.py +5 -0
  19. bazi-1.0.0/bazi/calendar/celestial_data/data/jieqi_moments.txt +7223 -0
  20. bazi-1.0.0/bazi/calendar/celestial_data/data/lunar_years_algo1.txt +224 -0
  21. bazi-1.0.0/bazi/calendar/celestial_data/data/lunar_years_algo2.txt +224 -0
  22. bazi-1.0.0/bazi/calendar/celestial_data/generator.py +304 -0
  23. bazi-1.0.0/bazi/calendar/celestial_data/loader.py +200 -0
  24. bazi-1.0.0/bazi/calendar/celestial_utils.py +546 -0
  25. bazi-1.0.0/bazi/calendar/dates.py +99 -0
  26. bazi-1.0.0/bazi/calendar/hko_data/__init__.py +20 -0
  27. bazi-1.0.0/bazi/calendar/hko_data/common.py +78 -0
  28. bazi-1.0.0/bazi/calendar/hko_data/data/1901.txt +369 -0
  29. bazi-1.0.0/bazi/calendar/hko_data/data/1902.txt +369 -0
  30. bazi-1.0.0/bazi/calendar/hko_data/data/1903.txt +369 -0
  31. bazi-1.0.0/bazi/calendar/hko_data/data/1904.txt +370 -0
  32. bazi-1.0.0/bazi/calendar/hko_data/data/1905.txt +369 -0
  33. bazi-1.0.0/bazi/calendar/hko_data/data/1906.txt +369 -0
  34. bazi-1.0.0/bazi/calendar/hko_data/data/1907.txt +369 -0
  35. bazi-1.0.0/bazi/calendar/hko_data/data/1908.txt +370 -0
  36. bazi-1.0.0/bazi/calendar/hko_data/data/1909.txt +369 -0
  37. bazi-1.0.0/bazi/calendar/hko_data/data/1910.txt +369 -0
  38. bazi-1.0.0/bazi/calendar/hko_data/data/1911.txt +369 -0
  39. bazi-1.0.0/bazi/calendar/hko_data/data/1912.txt +370 -0
  40. bazi-1.0.0/bazi/calendar/hko_data/data/1913.txt +369 -0
  41. bazi-1.0.0/bazi/calendar/hko_data/data/1914.txt +369 -0
  42. bazi-1.0.0/bazi/calendar/hko_data/data/1915.txt +369 -0
  43. bazi-1.0.0/bazi/calendar/hko_data/data/1916.txt +370 -0
  44. bazi-1.0.0/bazi/calendar/hko_data/data/1917.txt +369 -0
  45. bazi-1.0.0/bazi/calendar/hko_data/data/1918.txt +369 -0
  46. bazi-1.0.0/bazi/calendar/hko_data/data/1919.txt +369 -0
  47. bazi-1.0.0/bazi/calendar/hko_data/data/1920.txt +370 -0
  48. bazi-1.0.0/bazi/calendar/hko_data/data/1921.txt +369 -0
  49. bazi-1.0.0/bazi/calendar/hko_data/data/1922.txt +369 -0
  50. bazi-1.0.0/bazi/calendar/hko_data/data/1923.txt +369 -0
  51. bazi-1.0.0/bazi/calendar/hko_data/data/1924.txt +370 -0
  52. bazi-1.0.0/bazi/calendar/hko_data/data/1925.txt +369 -0
  53. bazi-1.0.0/bazi/calendar/hko_data/data/1926.txt +369 -0
  54. bazi-1.0.0/bazi/calendar/hko_data/data/1927.txt +369 -0
  55. bazi-1.0.0/bazi/calendar/hko_data/data/1928.txt +370 -0
  56. bazi-1.0.0/bazi/calendar/hko_data/data/1929.txt +369 -0
  57. bazi-1.0.0/bazi/calendar/hko_data/data/1930.txt +369 -0
  58. bazi-1.0.0/bazi/calendar/hko_data/data/1931.txt +369 -0
  59. bazi-1.0.0/bazi/calendar/hko_data/data/1932.txt +370 -0
  60. bazi-1.0.0/bazi/calendar/hko_data/data/1933.txt +369 -0
  61. bazi-1.0.0/bazi/calendar/hko_data/data/1934.txt +369 -0
  62. bazi-1.0.0/bazi/calendar/hko_data/data/1935.txt +369 -0
  63. bazi-1.0.0/bazi/calendar/hko_data/data/1936.txt +370 -0
  64. bazi-1.0.0/bazi/calendar/hko_data/data/1937.txt +369 -0
  65. bazi-1.0.0/bazi/calendar/hko_data/data/1938.txt +369 -0
  66. bazi-1.0.0/bazi/calendar/hko_data/data/1939.txt +369 -0
  67. bazi-1.0.0/bazi/calendar/hko_data/data/1940.txt +370 -0
  68. bazi-1.0.0/bazi/calendar/hko_data/data/1941.txt +370 -0
  69. bazi-1.0.0/bazi/calendar/hko_data/data/1942.txt +370 -0
  70. bazi-1.0.0/bazi/calendar/hko_data/data/1943.txt +370 -0
  71. bazi-1.0.0/bazi/calendar/hko_data/data/1944.txt +371 -0
  72. bazi-1.0.0/bazi/calendar/hko_data/data/1945.txt +370 -0
  73. bazi-1.0.0/bazi/calendar/hko_data/data/1946.txt +370 -0
  74. bazi-1.0.0/bazi/calendar/hko_data/data/1947.txt +370 -0
  75. bazi-1.0.0/bazi/calendar/hko_data/data/1948.txt +371 -0
  76. bazi-1.0.0/bazi/calendar/hko_data/data/1949.txt +370 -0
  77. bazi-1.0.0/bazi/calendar/hko_data/data/1950.txt +370 -0
  78. bazi-1.0.0/bazi/calendar/hko_data/data/1951.txt +370 -0
  79. bazi-1.0.0/bazi/calendar/hko_data/data/1952.txt +371 -0
  80. bazi-1.0.0/bazi/calendar/hko_data/data/1953.txt +370 -0
  81. bazi-1.0.0/bazi/calendar/hko_data/data/1954.txt +370 -0
  82. bazi-1.0.0/bazi/calendar/hko_data/data/1955.txt +370 -0
  83. bazi-1.0.0/bazi/calendar/hko_data/data/1956.txt +371 -0
  84. bazi-1.0.0/bazi/calendar/hko_data/data/1957.txt +370 -0
  85. bazi-1.0.0/bazi/calendar/hko_data/data/1958.txt +370 -0
  86. bazi-1.0.0/bazi/calendar/hko_data/data/1959.txt +370 -0
  87. bazi-1.0.0/bazi/calendar/hko_data/data/1960.txt +371 -0
  88. bazi-1.0.0/bazi/calendar/hko_data/data/1961.txt +370 -0
  89. bazi-1.0.0/bazi/calendar/hko_data/data/1962.txt +370 -0
  90. bazi-1.0.0/bazi/calendar/hko_data/data/1963.txt +370 -0
  91. bazi-1.0.0/bazi/calendar/hko_data/data/1964.txt +371 -0
  92. bazi-1.0.0/bazi/calendar/hko_data/data/1965.txt +370 -0
  93. bazi-1.0.0/bazi/calendar/hko_data/data/1966.txt +370 -0
  94. bazi-1.0.0/bazi/calendar/hko_data/data/1967.txt +370 -0
  95. bazi-1.0.0/bazi/calendar/hko_data/data/1968.txt +371 -0
  96. bazi-1.0.0/bazi/calendar/hko_data/data/1969.txt +370 -0
  97. bazi-1.0.0/bazi/calendar/hko_data/data/1970.txt +370 -0
  98. bazi-1.0.0/bazi/calendar/hko_data/data/1971.txt +370 -0
  99. bazi-1.0.0/bazi/calendar/hko_data/data/1972.txt +371 -0
  100. bazi-1.0.0/bazi/calendar/hko_data/data/1973.txt +370 -0
  101. bazi-1.0.0/bazi/calendar/hko_data/data/1974.txt +370 -0
  102. bazi-1.0.0/bazi/calendar/hko_data/data/1975.txt +370 -0
  103. bazi-1.0.0/bazi/calendar/hko_data/data/1976.txt +371 -0
  104. bazi-1.0.0/bazi/calendar/hko_data/data/1977.txt +369 -0
  105. bazi-1.0.0/bazi/calendar/hko_data/data/1978.txt +369 -0
  106. bazi-1.0.0/bazi/calendar/hko_data/data/1979.txt +370 -0
  107. bazi-1.0.0/bazi/calendar/hko_data/data/1980.txt +370 -0
  108. bazi-1.0.0/bazi/calendar/hko_data/data/1981.txt +369 -0
  109. bazi-1.0.0/bazi/calendar/hko_data/data/1982.txt +369 -0
  110. bazi-1.0.0/bazi/calendar/hko_data/data/1983.txt +369 -0
  111. bazi-1.0.0/bazi/calendar/hko_data/data/1984.txt +370 -0
  112. bazi-1.0.0/bazi/calendar/hko_data/data/1985.txt +369 -0
  113. bazi-1.0.0/bazi/calendar/hko_data/data/1986.txt +369 -0
  114. bazi-1.0.0/bazi/calendar/hko_data/data/1987.txt +369 -0
  115. bazi-1.0.0/bazi/calendar/hko_data/data/1988.txt +370 -0
  116. bazi-1.0.0/bazi/calendar/hko_data/data/1989.txt +369 -0
  117. bazi-1.0.0/bazi/calendar/hko_data/data/1990.txt +369 -0
  118. bazi-1.0.0/bazi/calendar/hko_data/data/1991.txt +369 -0
  119. bazi-1.0.0/bazi/calendar/hko_data/data/1992.txt +370 -0
  120. bazi-1.0.0/bazi/calendar/hko_data/data/1993.txt +369 -0
  121. bazi-1.0.0/bazi/calendar/hko_data/data/1994.txt +369 -0
  122. bazi-1.0.0/bazi/calendar/hko_data/data/1995.txt +369 -0
  123. bazi-1.0.0/bazi/calendar/hko_data/data/1996.txt +370 -0
  124. bazi-1.0.0/bazi/calendar/hko_data/data/1997.txt +369 -0
  125. bazi-1.0.0/bazi/calendar/hko_data/data/1998.txt +369 -0
  126. bazi-1.0.0/bazi/calendar/hko_data/data/1999.txt +369 -0
  127. bazi-1.0.0/bazi/calendar/hko_data/data/2000.txt +370 -0
  128. bazi-1.0.0/bazi/calendar/hko_data/data/2001.txt +369 -0
  129. bazi-1.0.0/bazi/calendar/hko_data/data/2002.txt +369 -0
  130. bazi-1.0.0/bazi/calendar/hko_data/data/2003.txt +369 -0
  131. bazi-1.0.0/bazi/calendar/hko_data/data/2004.txt +370 -0
  132. bazi-1.0.0/bazi/calendar/hko_data/data/2005.txt +369 -0
  133. bazi-1.0.0/bazi/calendar/hko_data/data/2006.txt +369 -0
  134. bazi-1.0.0/bazi/calendar/hko_data/data/2007.txt +369 -0
  135. bazi-1.0.0/bazi/calendar/hko_data/data/2008.txt +370 -0
  136. bazi-1.0.0/bazi/calendar/hko_data/data/2009.txt +369 -0
  137. bazi-1.0.0/bazi/calendar/hko_data/data/2010.txt +369 -0
  138. bazi-1.0.0/bazi/calendar/hko_data/data/2011.txt +368 -0
  139. bazi-1.0.0/bazi/calendar/hko_data/data/2012.txt +379 -0
  140. bazi-1.0.0/bazi/calendar/hko_data/data/2013.txt +368 -0
  141. bazi-1.0.0/bazi/calendar/hko_data/data/2014.txt +400 -0
  142. bazi-1.0.0/bazi/calendar/hko_data/data/2015.txt +376 -0
  143. bazi-1.0.0/bazi/calendar/hko_data/data/2016.txt +395 -0
  144. bazi-1.0.0/bazi/calendar/hko_data/data/2017.txt +383 -0
  145. bazi-1.0.0/bazi/calendar/hko_data/data/2018.txt +368 -0
  146. bazi-1.0.0/bazi/calendar/hko_data/data/2019.txt +368 -0
  147. bazi-1.0.0/bazi/calendar/hko_data/data/2020.txt +369 -0
  148. bazi-1.0.0/bazi/calendar/hko_data/data/2021.txt +368 -0
  149. bazi-1.0.0/bazi/calendar/hko_data/data/2022.txt +368 -0
  150. bazi-1.0.0/bazi/calendar/hko_data/data/2023.txt +368 -0
  151. bazi-1.0.0/bazi/calendar/hko_data/data/2024.txt +369 -0
  152. bazi-1.0.0/bazi/calendar/hko_data/data/2025.txt +368 -0
  153. bazi-1.0.0/bazi/calendar/hko_data/data/2026.txt +368 -0
  154. bazi-1.0.0/bazi/calendar/hko_data/data/2027.txt +368 -0
  155. bazi-1.0.0/bazi/calendar/hko_data/data/2028.txt +369 -0
  156. bazi-1.0.0/bazi/calendar/hko_data/data/2029.txt +368 -0
  157. bazi-1.0.0/bazi/calendar/hko_data/data/2030.txt +368 -0
  158. bazi-1.0.0/bazi/calendar/hko_data/data/2031.txt +368 -0
  159. bazi-1.0.0/bazi/calendar/hko_data/data/2032.txt +369 -0
  160. bazi-1.0.0/bazi/calendar/hko_data/data/2033.txt +368 -0
  161. bazi-1.0.0/bazi/calendar/hko_data/data/2034.txt +368 -0
  162. bazi-1.0.0/bazi/calendar/hko_data/data/2035.txt +368 -0
  163. bazi-1.0.0/bazi/calendar/hko_data/data/2036.txt +369 -0
  164. bazi-1.0.0/bazi/calendar/hko_data/data/2037.txt +372 -0
  165. bazi-1.0.0/bazi/calendar/hko_data/data/2038.txt +368 -0
  166. bazi-1.0.0/bazi/calendar/hko_data/data/2039.txt +368 -0
  167. bazi-1.0.0/bazi/calendar/hko_data/data/2040.txt +369 -0
  168. bazi-1.0.0/bazi/calendar/hko_data/data/2041.txt +368 -0
  169. bazi-1.0.0/bazi/calendar/hko_data/data/2042.txt +368 -0
  170. bazi-1.0.0/bazi/calendar/hko_data/data/2043.txt +368 -0
  171. bazi-1.0.0/bazi/calendar/hko_data/data/2044.txt +369 -0
  172. bazi-1.0.0/bazi/calendar/hko_data/data/2045.txt +368 -0
  173. bazi-1.0.0/bazi/calendar/hko_data/data/2046.txt +368 -0
  174. bazi-1.0.0/bazi/calendar/hko_data/data/2047.txt +368 -0
  175. bazi-1.0.0/bazi/calendar/hko_data/data/2048.txt +369 -0
  176. bazi-1.0.0/bazi/calendar/hko_data/data/2049.txt +368 -0
  177. bazi-1.0.0/bazi/calendar/hko_data/data/2050.txt +380 -0
  178. bazi-1.0.0/bazi/calendar/hko_data/data/2051.txt +368 -0
  179. bazi-1.0.0/bazi/calendar/hko_data/data/2052.txt +369 -0
  180. bazi-1.0.0/bazi/calendar/hko_data/data/2053.txt +368 -0
  181. bazi-1.0.0/bazi/calendar/hko_data/data/2054.txt +368 -0
  182. bazi-1.0.0/bazi/calendar/hko_data/data/2055.txt +368 -0
  183. bazi-1.0.0/bazi/calendar/hko_data/data/2056.txt +369 -0
  184. bazi-1.0.0/bazi/calendar/hko_data/data/2057.txt +368 -0
  185. bazi-1.0.0/bazi/calendar/hko_data/data/2058.txt +368 -0
  186. bazi-1.0.0/bazi/calendar/hko_data/data/2059.txt +368 -0
  187. bazi-1.0.0/bazi/calendar/hko_data/data/2060.txt +369 -0
  188. bazi-1.0.0/bazi/calendar/hko_data/data/2061.txt +368 -0
  189. bazi-1.0.0/bazi/calendar/hko_data/data/2062.txt +368 -0
  190. bazi-1.0.0/bazi/calendar/hko_data/data/2063.txt +368 -0
  191. bazi-1.0.0/bazi/calendar/hko_data/data/2064.txt +369 -0
  192. bazi-1.0.0/bazi/calendar/hko_data/data/2065.txt +368 -0
  193. bazi-1.0.0/bazi/calendar/hko_data/data/2066.txt +368 -0
  194. bazi-1.0.0/bazi/calendar/hko_data/data/2067.txt +368 -0
  195. bazi-1.0.0/bazi/calendar/hko_data/data/2068.txt +369 -0
  196. bazi-1.0.0/bazi/calendar/hko_data/data/2069.txt +368 -0
  197. bazi-1.0.0/bazi/calendar/hko_data/data/2070.txt +368 -0
  198. bazi-1.0.0/bazi/calendar/hko_data/data/2071.txt +368 -0
  199. bazi-1.0.0/bazi/calendar/hko_data/data/2072.txt +369 -0
  200. bazi-1.0.0/bazi/calendar/hko_data/data/2073.txt +368 -0
  201. bazi-1.0.0/bazi/calendar/hko_data/data/2074.txt +368 -0
  202. bazi-1.0.0/bazi/calendar/hko_data/data/2075.txt +368 -0
  203. bazi-1.0.0/bazi/calendar/hko_data/data/2076.txt +369 -0
  204. bazi-1.0.0/bazi/calendar/hko_data/data/2077.txt +368 -0
  205. bazi-1.0.0/bazi/calendar/hko_data/data/2078.txt +368 -0
  206. bazi-1.0.0/bazi/calendar/hko_data/data/2079.txt +368 -0
  207. bazi-1.0.0/bazi/calendar/hko_data/data/2080.txt +369 -0
  208. bazi-1.0.0/bazi/calendar/hko_data/data/2081.txt +369 -0
  209. bazi-1.0.0/bazi/calendar/hko_data/data/2082.txt +368 -0
  210. bazi-1.0.0/bazi/calendar/hko_data/data/2083.txt +368 -0
  211. bazi-1.0.0/bazi/calendar/hko_data/data/2084.txt +369 -0
  212. bazi-1.0.0/bazi/calendar/hko_data/data/2085.txt +368 -0
  213. bazi-1.0.0/bazi/calendar/hko_data/data/2086.txt +368 -0
  214. bazi-1.0.0/bazi/calendar/hko_data/data/2087.txt +368 -0
  215. bazi-1.0.0/bazi/calendar/hko_data/data/2088.txt +369 -0
  216. bazi-1.0.0/bazi/calendar/hko_data/data/2089.txt +368 -0
  217. bazi-1.0.0/bazi/calendar/hko_data/data/2090.txt +368 -0
  218. bazi-1.0.0/bazi/calendar/hko_data/data/2091.txt +368 -0
  219. bazi-1.0.0/bazi/calendar/hko_data/data/2092.txt +369 -0
  220. bazi-1.0.0/bazi/calendar/hko_data/data/2093.txt +368 -0
  221. bazi-1.0.0/bazi/calendar/hko_data/data/2094.txt +368 -0
  222. bazi-1.0.0/bazi/calendar/hko_data/data/2095.txt +368 -0
  223. bazi-1.0.0/bazi/calendar/hko_data/data/2096.txt +369 -0
  224. bazi-1.0.0/bazi/calendar/hko_data/data/2097.txt +368 -0
  225. bazi-1.0.0/bazi/calendar/hko_data/data/2098.txt +368 -0
  226. bazi-1.0.0/bazi/calendar/hko_data/data/2099.txt +368 -0
  227. bazi-1.0.0/bazi/calendar/hko_data/data/2100.txt +368 -0
  228. bazi-1.0.0/bazi/calendar/hko_data/data/jieqi_encoded.bin +425 -0
  229. bazi-1.0.0/bazi/calendar/hko_data/data/lunardate_encoded.bin +0 -0
  230. bazi-1.0.0/bazi/calendar/hko_data/decoder.py +219 -0
  231. bazi-1.0.0/bazi/calendar/hko_data/encoder.py +231 -0
  232. bazi-1.0.0/bazi/calendar/hko_data_utils.py +582 -0
  233. bazi-1.0.0/bazi/calendar/utils_protocol.py +56 -0
  234. bazi-1.0.0/bazi/common.py +86 -0
  235. bazi-1.0.0/bazi/data_types.py +99 -0
  236. bazi-1.0.0/bazi/defines/__init__.py +17 -0
  237. bazi-1.0.0/bazi/defines/dizhi.py +35 -0
  238. bazi-1.0.0/bazi/defines/enum_base.py +70 -0
  239. bazi-1.0.0/bazi/defines/ganzhi.py +67 -0
  240. bazi-1.0.0/bazi/defines/jieqi.py +77 -0
  241. bazi-1.0.0/bazi/defines/relations.py +57 -0
  242. bazi-1.0.0/bazi/defines/shier_zhangsheng.py +35 -0
  243. bazi-1.0.0/bazi/defines/shishen.py +77 -0
  244. bazi-1.0.0/bazi/defines/tiangan.py +31 -0
  245. bazi-1.0.0/bazi/defines/wuxing.py +74 -0
  246. bazi-1.0.0/bazi/defines/yinyang.py +23 -0
  247. bazi-1.0.0/bazi/descriptions.py +476 -0
  248. bazi-1.0.0/bazi/interpreter.py +53 -0
  249. bazi-1.0.0/bazi/py.typed +0 -0
  250. bazi-1.0.0/bazi/rules.py +1414 -0
  251. bazi-1.0.0/bazi/school.py +496 -0
  252. bazi-1.0.0/bazi/transit_chart.py +174 -0
  253. bazi-1.0.0/bazi/transits.py +222 -0
  254. bazi-1.0.0/bazi/utils/__init__.py +11 -0
  255. bazi-1.0.0/bazi/utils/bazi_utils.py +383 -0
  256. bazi-1.0.0/bazi/utils/dizhi_utils.py +1018 -0
  257. bazi-1.0.0/bazi/utils/relation_discovery.py +60 -0
  258. bazi-1.0.0/bazi/utils/shensha_utils.py +660 -0
  259. bazi-1.0.0/bazi/utils/tiangan_utils.py +274 -0
  260. bazi-1.0.0/bazi.egg-info/PKG-INFO +138 -0
  261. bazi-1.0.0/bazi.egg-info/SOURCES.txt +315 -0
  262. bazi-1.0.0/bazi.egg-info/dependency_links.txt +1 -0
  263. bazi-1.0.0/bazi.egg-info/top_level.txt +1 -0
  264. bazi-1.0.0/pyproject.toml +30 -0
  265. bazi-1.0.0/ruff.toml +31 -0
  266. bazi-1.0.0/run_demo.py +136 -0
  267. bazi-1.0.0/run_interpreter.py +137 -0
  268. bazi-1.0.0/run_package_checks.py +200 -0
  269. bazi-1.0.0/run_relationship_analyzer.py +95 -0
  270. bazi-1.0.0/run_tests.py +506 -0
  271. bazi-1.0.0/setup.cfg +4 -0
  272. bazi-1.0.0/tests/analyzer/test_relationship_analyzer.py +2011 -0
  273. bazi-1.0.0/tests/calendar/celestial_fixtures/jieqi_moments.txt +142 -0
  274. bazi-1.0.0/tests/calendar/celestial_fixtures/lunar_years_algo1.txt +32 -0
  275. bazi-1.0.0/tests/calendar/celestial_fixtures/lunar_years_algo2.txt +32 -0
  276. bazi-1.0.0/tests/calendar/celestial_parity_data.py +121 -0
  277. bazi-1.0.0/tests/calendar/test_backend.py +181 -0
  278. bazi-1.0.0/tests/calendar/test_celestial_loader.py +294 -0
  279. bazi-1.0.0/tests/calendar/test_celestial_parity_derived.py +273 -0
  280. bazi-1.0.0/tests/calendar/test_celestial_tables.py +344 -0
  281. bazi-1.0.0/tests/calendar/test_celestial_utils.py +409 -0
  282. bazi-1.0.0/tests/calendar/test_dates.py +300 -0
  283. bazi-1.0.0/tests/calendar/test_hko_data.py +429 -0
  284. bazi-1.0.0/tests/calendar/test_hko_data_utils.py +718 -0
  285. bazi-1.0.0/tests/defines/test_dizhi.py +119 -0
  286. bazi-1.0.0/tests/defines/test_ganzhi.py +178 -0
  287. bazi-1.0.0/tests/defines/test_jieqi.py +118 -0
  288. bazi-1.0.0/tests/defines/test_relations.py +52 -0
  289. bazi-1.0.0/tests/defines/test_shier_zhangsheng.py +37 -0
  290. bazi-1.0.0/tests/defines/test_shishen.py +69 -0
  291. bazi-1.0.0/tests/defines/test_tiangan.py +116 -0
  292. bazi-1.0.0/tests/defines/test_wuxing.py +85 -0
  293. bazi-1.0.0/tests/defines/test_yinyang.py +48 -0
  294. bazi-1.0.0/tests/integration/test_ganzhi_relations.py +262 -0
  295. bazi-1.0.0/tests/integration/test_hko_data_regeneration.py +64 -0
  296. bazi-1.0.0/tests/integration/test_import_from_any_cwd.py +41 -0
  297. bazi-1.0.0/tests/integration/test_relationship_analysis.py +1088 -0
  298. bazi-1.0.0/tests/o_smoke.py +305 -0
  299. bazi-1.0.0/tests/package_smoke.py +138 -0
  300. bazi-1.0.0/tests/pytest.ini +9 -0
  301. bazi-1.0.0/tests/test_bazi.py +769 -0
  302. bazi-1.0.0/tests/test_bazi_chart.py +1124 -0
  303. bazi-1.0.0/tests/test_common.py +156 -0
  304. bazi-1.0.0/tests/test_interpreter.py +75 -0
  305. bazi-1.0.0/tests/test_package_checks.py +65 -0
  306. bazi-1.0.0/tests/test_release_policy.py +324 -0
  307. bazi-1.0.0/tests/test_rules.py +414 -0
  308. bazi-1.0.0/tests/test_run_relationship_analyzer.py +138 -0
  309. bazi-1.0.0/tests/test_run_tests.py +104 -0
  310. bazi-1.0.0/tests/test_school.py +594 -0
  311. bazi-1.0.0/tests/test_transit_chart.py +495 -0
  312. bazi-1.0.0/tests/test_transits.py +255 -0
  313. bazi-1.0.0/tests/utils/test_bazi_utils.py +389 -0
  314. bazi-1.0.0/tests/utils/test_dizhi_utils.py +1711 -0
  315. bazi-1.0.0/tests/utils/test_relation_discovery.py +33 -0
  316. bazi-1.0.0/tests/utils/test_shensha_utils.py +650 -0
  317. bazi-1.0.0/tests/utils/test_tiangan_utils.py +529 -0
@@ -0,0 +1,256 @@
1
+ name: Prepare or Publish Release
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ mode:
7
+ description: Rehearse only, or explicitly publish the tested release
8
+ type: choice
9
+ options: [rehearsal, release]
10
+ default: rehearsal
11
+ required: true
12
+
13
+ permissions: {}
14
+ concurrency:
15
+ group: bazi-release
16
+ cancel-in-progress: false
17
+
18
+ jobs:
19
+ preflight:
20
+ if: inputs.mode == 'release'
21
+ runs-on: ubuntu-latest
22
+ permissions: {}
23
+ steps:
24
+ - name: Require an existing protected release environment
25
+ env:
26
+ MODE: ${{ inputs.mode }}
27
+ REPOSITORY: ${{ github.repository }}
28
+ REF: ${{ github.ref }}
29
+ SHA: ${{ github.sha }}
30
+ shell: python
31
+ run: |
32
+ import json, os, re, urllib.request
33
+ if (os.environ['MODE'] != 'release' or os.environ['REPOSITORY'] != '0xf3cd/bazi'
34
+ or os.environ['REF'] != 'refs/heads/main' or not re.fullmatch(r'[0-9a-f]{40}', os.environ['SHA'])):
35
+ raise SystemExit('Real release requires the main dispatch SHA of 0xf3cd/bazi')
36
+ def get(path):
37
+ request = urllib.request.Request('https://api.github.com/repos/0xf3cd/bazi/' + path,
38
+ headers={'Accept': 'application/vnd.github+json', 'X-GitHub-Api-Version': '2022-11-28'})
39
+ with urllib.request.urlopen(request, timeout=30) as response:
40
+ return json.load(response)
41
+ environment = get('environments/release')
42
+ policies = get('environments/release/deployment-branch-policies?per_page=100')
43
+ reviewers = [rule for rule in environment.get('protection_rules', [])
44
+ if rule.get('type') == 'required_reviewers' and any(
45
+ reviewer.get('type') in ('User', 'Team') and isinstance(reviewer.get('reviewer', {}).get('id'), int)
46
+ and reviewer['reviewer']['id'] > 0 for reviewer in rule.get('reviewers', []))]
47
+ if (environment.get('name') != 'release' or not reviewers
48
+ or environment.get('deployment_branch_policy') != {'protected_branches': False, 'custom_branch_policies': True}
49
+ or policies.get('total_count') != 1
50
+ or [(p.get('name'), p.get('type')) for p in policies.get('branch_policies', [])] != [('main', 'branch')]):
51
+ raise SystemExit('release must have required reviewers and exactly the main branch policy')
52
+
53
+ build:
54
+ needs: [preflight]
55
+ if: ${{ !cancelled() && (inputs.mode == 'rehearsal' || (inputs.mode == 'release' && needs.preflight.result == 'success')) }}
56
+ runs-on: ubuntu-latest
57
+ permissions:
58
+ contents: read
59
+ outputs:
60
+ artifact-id: ${{ steps.upload.outputs.artifact-id }}
61
+ manifest-sha: ${{ steps.bundle.outputs.manifest-sha }}
62
+ version: ${{ steps.bundle.outputs.version }}
63
+ steps:
64
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
65
+ with:
66
+ ref: ${{ github.sha }}
67
+ persist-credentials: false
68
+ - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
69
+ with:
70
+ python-version: '3.12'
71
+ - name: Verify source identity
72
+ env:
73
+ SHA: ${{ github.sha }}
74
+ MODE: ${{ inputs.mode }}
75
+ REPOSITORY: ${{ github.repository }}
76
+ REF: ${{ github.ref }}
77
+ shell: python
78
+ run: |
79
+ import os, re, subprocess
80
+ mode, sha = os.environ['MODE'], os.environ['SHA']
81
+ if mode not in ('rehearsal', 'release') or not re.fullmatch(r'[0-9a-f]{40}', sha):
82
+ raise SystemExit('Invalid release mode or SHA')
83
+ if mode == 'release' and (os.environ['REPOSITORY'], os.environ['REF']) != ('0xf3cd/bazi', 'refs/heads/main'):
84
+ raise SystemExit('Real release is main-only')
85
+ if subprocess.check_output(['git', 'rev-parse', 'HEAD'], text=True).strip() != sha:
86
+ raise SystemExit('Checkout SHA differs from dispatch SHA')
87
+ - name: Install development requirements
88
+ run: |
89
+ python -m pip install -r Requirements.txt
90
+ python -m pip install celestial-calendar==0.6.1
91
+ - name: Run full gate and retain its tested pair
92
+ run: python run_tests.py -a -v --package-output-dir "$RUNNER_TEMP/bundle/dist"
93
+ - name: Seal release bundle
94
+ id: bundle
95
+ env:
96
+ SHA: ${{ github.sha }}
97
+ REPOSITORY: ${{ github.repository }}
98
+ RUN_ID: ${{ github.run_id }}
99
+ MODE: ${{ inputs.mode }}
100
+ shell: python
101
+ run: |
102
+ import hashlib, json, os, re, shutil, tomllib
103
+ from pathlib import Path
104
+ version = tomllib.loads(Path('pyproject.toml').read_text())['project']['version']
105
+ if not re.fullmatch(r'[0-9]+\.[0-9]+\.[0-9]+', version):
106
+ raise SystemExit('Use a stable three-part release version')
107
+ bundle = Path(os.environ['RUNNER_TEMP']) / 'bundle'
108
+ names = {f'bazi-{version}.tar.gz', f'bazi-{version}-py3-none-any.whl'}
109
+ if {p.name for p in (bundle / 'dist').iterdir()} != names:
110
+ raise SystemExit('Expected exactly the tested sdist and rebuilt wheel')
111
+ shutil.copyfile('RELEASE_NOTES.md', bundle / 'RELEASE_NOTES.md')
112
+ metadata = {'name': 'bazi', 'version': version, 'sha': os.environ['SHA'],
113
+ 'repository': os.environ['REPOSITORY'], 'run_id': os.environ['RUN_ID'], 'mode': os.environ['MODE']}
114
+ (bundle / 'release.json').write_text(json.dumps(metadata, sort_keys=True) + '\n')
115
+ files = sorted([*(bundle / 'dist').iterdir(), bundle / 'RELEASE_NOTES.md', bundle / 'release.json'], key=lambda p: p.relative_to(bundle).as_posix())
116
+ checksums = ''.join(f'{hashlib.sha256(p.read_bytes()).hexdigest()} {p.relative_to(bundle).as_posix()}\n' for p in files)
117
+ (bundle / 'SHA256SUMS').write_bytes(checksums.encode('utf-8'))
118
+ with open(os.environ['GITHUB_OUTPUT'], 'a') as output:
119
+ output.write(f'version={version}\nmanifest-sha={hashlib.sha256(checksums.encode()).hexdigest()}\n')
120
+ - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
121
+ id: upload
122
+ with:
123
+ name: tested-release
124
+ path: ${{ runner.temp }}/bundle/
125
+ if-no-files-found: error
126
+ overwrite: false
127
+ retention-days: 30
128
+
129
+ pypi:
130
+ needs: [preflight, build]
131
+ if: inputs.mode == 'release' && needs.preflight.result == 'success' && needs.build.result == 'success' && github.repository == '0xf3cd/bazi' && github.ref == 'refs/heads/main'
132
+ runs-on: ubuntu-latest
133
+ environment: release
134
+ permissions:
135
+ id-token: write
136
+ steps:
137
+ - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
138
+ with: &download
139
+ artifact-ids: ${{ needs.build.outputs.artifact-id }}
140
+ path: artifact
141
+ merge-multiple: true
142
+ digest-mismatch: error
143
+ - &verify
144
+ name: Verify exact same-run bundle
145
+ env:
146
+ ARTIFACT_ID: ${{ needs.build.outputs.artifact-id }}
147
+ MANIFEST_SHA: ${{ needs.build.outputs.manifest-sha }}
148
+ VERSION: ${{ needs.build.outputs.version }}
149
+ SHA: ${{ github.sha }}
150
+ RUN_ID: ${{ github.run_id }}
151
+ MODE: ${{ inputs.mode }}
152
+ REPOSITORY: ${{ github.repository }}
153
+ REF: ${{ github.ref }}
154
+ shell: python
155
+ run: |
156
+ import hashlib, json, os, re
157
+ from pathlib import Path
158
+ version = os.environ['VERSION']
159
+ if (os.environ['MODE'] != 'release' or os.environ['REPOSITORY'] != '0xf3cd/bazi'
160
+ or os.environ['REF'] != 'refs/heads/main' or not re.fullmatch(r'[0-9a-f]{40}', os.environ['SHA'])
161
+ or not re.fullmatch(r'[1-9][0-9]*', os.environ['ARTIFACT_ID'])
162
+ or not re.fullmatch(r'[0-9]+\.[0-9]+\.[0-9]+', version)):
163
+ raise SystemExit('Invalid publication identity')
164
+ root = Path('artifact')
165
+ expected = {f'dist/bazi-{version}.tar.gz', f'dist/bazi-{version}-py3-none-any.whl', 'release.json', 'RELEASE_NOTES.md'}
166
+ if ({p.relative_to(root).as_posix() for p in root.rglob('*') if p.is_file()} != expected | {'SHA256SUMS'}
167
+ or any(p.is_symlink() for p in root.rglob('*'))):
168
+ raise SystemExit('Unexpected release bundle files')
169
+ checksums = (root / 'SHA256SUMS').read_bytes()
170
+ if hashlib.sha256(checksums).hexdigest() != os.environ['MANIFEST_SHA']:
171
+ raise SystemExit('Manifest differs from tested build')
172
+ wanted = ''.join(f'{hashlib.sha256((root / name).read_bytes()).hexdigest()} {name}\n' for name in sorted(expected))
173
+ if checksums.decode() != wanted:
174
+ raise SystemExit('Release bundle checksum mismatch')
175
+ metadata = json.loads((root / 'release.json').read_text())
176
+ if metadata != {'name': 'bazi', 'version': version, 'sha': os.environ['SHA'], 'repository': '0xf3cd/bazi', 'run_id': os.environ['RUN_ID'], 'mode': 'release'}:
177
+ raise SystemExit('Release metadata identity mismatch')
178
+ - name: Reject existing version, tag or release
179
+ env:
180
+ VERSION: ${{ needs.build.outputs.version }}
181
+ shell: python
182
+ run: |
183
+ import os, urllib.error, urllib.request
184
+ version = os.environ['VERSION']
185
+ urls = [f'https://pypi.org/pypi/bazi/{version}/json',
186
+ f'https://api.github.com/repos/0xf3cd/bazi/git/ref/tags/v{version}',
187
+ f'https://api.github.com/repos/0xf3cd/bazi/releases/tags/v{version}']
188
+ for url in urls:
189
+ try:
190
+ with urllib.request.urlopen(url, timeout=30):
191
+ pass
192
+ except urllib.error.HTTPError as error:
193
+ if error.code == 404:
194
+ continue
195
+ raise
196
+ raise SystemExit('Version, tag or release already exists; deliberate recovery required')
197
+ - uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
198
+ with:
199
+ packages-dir: artifact/dist/
200
+ skip-existing: false
201
+
202
+ github-release:
203
+ needs: [preflight, build, pypi]
204
+ if: inputs.mode == 'release' && needs.preflight.result == 'success' && needs.pypi.result == 'success' && github.repository == '0xf3cd/bazi' && github.ref == 'refs/heads/main'
205
+ runs-on: ubuntu-latest
206
+ environment: release
207
+ permissions:
208
+ contents: write
209
+ steps:
210
+ - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
211
+ with: *download
212
+ - *verify
213
+ - name: Verify PyPI bytes and create the tag and release without overwriting
214
+ env:
215
+ VERSION: ${{ needs.build.outputs.version }}
216
+ SHA: ${{ github.sha }}
217
+ GH_TOKEN: ${{ github.token }}
218
+ shell: python
219
+ run: |
220
+ import hashlib, json, os, urllib.error, urllib.request
221
+ from pathlib import Path
222
+ version, sha = os.environ['VERSION'], os.environ['SHA']
223
+ with urllib.request.urlopen(f'https://pypi.org/pypi/bazi/{version}/json', timeout=30) as response:
224
+ published = json.load(response)
225
+ actual = {entry['filename']: entry['digests']['sha256'] for entry in published['urls']}
226
+ expected = {p.name: hashlib.sha256(p.read_bytes()).hexdigest() for p in Path('artifact/dist').iterdir()}
227
+ if actual != expected:
228
+ raise SystemExit('PyPI bytes differ from tested pair')
229
+ base = 'https://api.github.com/repos/0xf3cd/bazi'
230
+ headers = {'Authorization': 'Bearer ' + os.environ['GH_TOKEN'], 'Accept': 'application/vnd.github+json', 'X-GitHub-Api-Version': '2022-11-28'}
231
+ for path in (f'/git/ref/tags/v{version}', f'/releases/tags/v{version}'):
232
+ try:
233
+ with urllib.request.urlopen(urllib.request.Request(base + path, headers=headers), timeout=30):
234
+ pass
235
+ except urllib.error.HTTPError as error:
236
+ if error.code == 404:
237
+ continue
238
+ raise
239
+ raise SystemExit('Existing tag/release; deliberate recovery required')
240
+ def post(path, data):
241
+ request = urllib.request.Request(base + path, data=json.dumps(data).encode(), headers={**headers, 'Content-Type': 'application/json'}, method='POST')
242
+ with urllib.request.urlopen(request, timeout=30) as response:
243
+ return json.load(response)
244
+ post('/git/refs', {'ref': f'refs/tags/v{version}', 'sha': sha})
245
+ release = post('/releases', {'tag_name': f'v{version}', 'target_commitish': sha,
246
+ 'name': f'bazi {version}', 'body': Path('artifact/RELEASE_NOTES.md').read_text(), 'draft': True})
247
+ for path in sorted(p for p in Path('artifact').rglob('*') if p.is_file()):
248
+ url = f'https://uploads.github.com/repos/0xf3cd/bazi/releases/{release["id"]}/assets?name={path.name}'
249
+ request = urllib.request.Request(url, data=path.read_bytes(), headers={**headers, 'Content-Type': 'application/octet-stream'}, method='POST')
250
+ with urllib.request.urlopen(request, timeout=60) as response:
251
+ asset = json.load(response)
252
+ if asset.get('digest') != 'sha256:' + hashlib.sha256(path.read_bytes()).hexdigest():
253
+ raise SystemExit('Uploaded GitHub asset digest mismatch; release remains draft')
254
+ request = urllib.request.Request(base + f'/releases/{release["id"]}', data=b'{"draft":false}', headers={**headers, 'Content-Type': 'application/json'}, method='PATCH')
255
+ with urllib.request.urlopen(request, timeout=30):
256
+ pass
bazi-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-2026 Ningqi Wang (0xf3cd)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
bazi-1.0.0/MANIFEST.in ADDED
@@ -0,0 +1,16 @@
1
+ include pyproject.toml MANIFEST.in LICENSE THIRD_PARTY_NOTICES.md
2
+ include README.md RELEASE_NOTES.md RELEASING.md Requirements.txt ruff.toml
3
+ include run_tests.py run_package_checks.py run_demo.py run_interpreter.py run_relationship_analyzer.py
4
+ recursive-include bazi *.py py.typed
5
+ include bazi/calendar/hko_data/data/jieqi_encoded.bin
6
+ include bazi/calendar/hko_data/data/lunardate_encoded.bin
7
+ include bazi/calendar/hko_data/data/19[0-9][0-9].txt
8
+ include bazi/calendar/hko_data/data/20[0-9][0-9].txt
9
+ include bazi/calendar/hko_data/data/2100.txt
10
+ include bazi/calendar/celestial_data/data/jieqi_moments.txt
11
+ include bazi/calendar/celestial_data/data/lunar_years_algo1.txt
12
+ include bazi/calendar/celestial_data/data/lunar_years_algo2.txt
13
+ include bazi/calendar/celestial_data/SCHEMA.md
14
+ recursive-include tests *.py *.ini *.txt *.json
15
+ include .github/workflows/release.yml
16
+ global-exclude *.py[cod]
bazi-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,138 @@
1
+ Metadata-Version: 2.4
2
+ Name: bazi
3
+ Version: 1.0.0
4
+ Summary: Four Pillars charts, calendar backends and Bazi relations
5
+ Author: Ningqi Wang (0xf3cd)
6
+ Project-URL: Repository, https://github.com/0xf3cd/bazi
7
+ Project-URL: Issues, https://github.com/0xf3cd/bazi/issues
8
+ Requires-Python: >=3.11
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ License-File: THIRD_PARTY_NOTICES.md
12
+ Dynamic: license-file
13
+
14
+ # Bazi
15
+ > 排盘、五行、十神、纳音、刑冲破害、合会
16
+
17
+ A Python 3.11+ library for Four Pillars charts and their relations. Runtime uses
18
+ only the standard library and five bundled calendar tables; no network access or
19
+ data generation is needed.
20
+
21
+ ## Installation
22
+
23
+ To build and validate the current checkout and retain an installable pair:
24
+
25
+ ```sh
26
+ python -m pip install -r Requirements.txt
27
+ python run_package_checks.py --output-dir ../bazi-tested
28
+ python -m pip install ../bazi-tested/bazi-1.0.0-py3-none-any.whl
29
+ ```
30
+
31
+ The output directory must be empty and outside the checkout. For a published
32
+ version, install from the registry with `python -m pip install bazi==1.0.0`.
33
+ There is no installed CLI, `src` compatibility package or root-class facade.
34
+
35
+ ```python
36
+ from datetime import datetime
37
+
38
+ from bazi.bazi import Bazi
39
+ from bazi.bazi_chart import BaziChart
40
+ from bazi.school import BaziConfig
41
+ from bazi.transit_chart import TransitChart
42
+ from bazi.analyzer.relationship import RelationshipAnalyzer
43
+
44
+ config = BaziConfig.from_values(backend='celestial', precision='day')
45
+ chart = BaziChart(Bazi.create(datetime(2000, 1, 1, 12), 'male', config))
46
+ restored = BaziChart.from_json(chart.json)
47
+ assert restored.json == chart.json
48
+ print(chart.json['pillars'])
49
+ print(TransitChart(chart).at_year(2024))
50
+ print(RelationshipAnalyzer(chart).at_birth.shensha)
51
+ ```
52
+
53
+ Birth times are naive local civil times; timezone-aware inputs are rejected.
54
+ Defaults remain `CELESTIAL` with day precision. `hko` provides date-level calendar
55
+ data; `celestial` and `celestial-algo2` use the bundled astronomical tables.
56
+ Backend differences and supported date ranges are documented in the calendar
57
+ modules; selecting a backend does not install its offline generator.
58
+
59
+ ## Interfaces
60
+
61
+ Use the defining modules rather than expecting classes at the package root:
62
+
63
+ | Module | Principal interfaces |
64
+ | --- | --- |
65
+ | `bazi.bazi` | `Bazi`, `BaziGender` |
66
+ | `bazi.bazi_chart` | `BaziChart`, `BaziJson`, JSON restoration with `from_json` |
67
+ | `bazi.school` | `BaziConfig`, `BaziSchool`, precision and school options |
68
+ | `bazi.calendar` | `CalendarBackend`, `CalendarDate`, `CalendarType`, `calendar_utils_of` |
69
+ | `bazi.transit_chart`, `bazi.transits` | `TransitChart`, `TransitSet`, `TransitKind` |
70
+ | `bazi.analyzer.relationship` | `RelationshipAnalyzer` |
71
+ | `bazi.interpreter` | `Interpreter.interpret_tiangan`, `Interpreter.interpret_shishen` |
72
+ | `bazi.defines`, `bazi.utils` | Domain enums and relation utilities, documented in their modules |
73
+
74
+ Domain names use Pinyin; enums also provide Chinese aliases. JSON retains Chinese
75
+ domain values and records configuration. `py.typed` exposes the inline type hints.
76
+ Private names, offline generation tools and undocumented internals are not a
77
+ promise of a stable public interface.
78
+
79
+ ## Instructions
80
+
81
+ `Requirements.txt` is the development setup, not the installed library's dependency
82
+ list. It includes test, lint, typing and distribution-verification tools.
83
+ Source-tree mypy also needs `celestial-calendar==0.6.1`, as installed by CPython CI.
84
+
85
+ ```sh
86
+ python -m pip install -r Requirements.txt
87
+ python -m pip install celestial-calendar==0.6.1
88
+ python run_tests.py -a -v
89
+ ```
90
+
91
+ The full gate runs every test (including slow and HKO-data tests), requires 100%
92
+ coverage, runs ruff and strict source mypy, demos, Interpreter, optimized input
93
+ checks, and isolated wheel/sdist-derived-wheel checks. The artifact checks use two
94
+ fresh dependency-free consumers and a separate installed mypy environment.
95
+ The artifact checks (`run_package_checks.py`, `-pkg` and `-a`) may download tools
96
+ and their dependencies from the package index to seed isolated build and typing
97
+ environments, even after the development requirements are installed.
98
+ Builds and consumers live in external temporary directories. Root demo scripts may
99
+ write to `output_data/`; the installed library's read-only behavior is checked separately.
100
+
101
+ | Flag | Effect |
102
+ | --- | --- |
103
+ | `-a`, `--all` | Full gate; overrides `-nt` and `-k` |
104
+ | `-nt`, `--no-test` | Skip tests and coverage |
105
+ | `-s`, `-hko` | Include slow tests or HKO-data tests |
106
+ | `-k <expression>` | Select pytest tests; ignores `-s` and `-hko` |
107
+ | `-v` | Verbose output |
108
+ | `-c` | Coverage report, also written to `covhtml/` |
109
+ | `-cr <rate>`, `--coverage-rate <rate>` | Minimum coverage (default 100) |
110
+ | `-r`, `--ruff` | Run `ruff check` |
111
+ | `-m`, `-mypy`, `--mypy` | Run source mypy with the runner's strict flags |
112
+ | `-d` | Run both demo scripts |
113
+ | `-i` | Run Interpreter examples |
114
+ | `-osmoke`, `--o-smoke` | Run source public-contract smoke with `python -O` |
115
+ | `-pkg`, `--package` | Run isolated distribution checks |
116
+ | `--package-output-dir <path>` | With `-pkg` or `-a`, retain only the verified sdist and its rebuilt wheel |
117
+
118
+ A bare `python run_tests.py` omits slow/HKO tests and all optional tasks. It is not
119
+ the full verification gate. Do not run autoformatters; use `ruff check .`.
120
+
121
+ ## Offline Generation
122
+
123
+ The committed data is read, not regenerated, during packaging or installed use.
124
+ The source tree and sdist contain the raw HKO inputs; the wheel does not.
125
+ Maintainers can regenerate from those sources with `python -m bazi.calendar.hko_data.encoder`
126
+ (`requests` is needed only if inputs must be downloaded), or `python -m bazi.calendar.celestial_data.generator`
127
+ with `celestial-calendar==0.6.1`. These optional tools are not runtime dependencies.
128
+ If an installed table is missing, reinstall the distribution instead.
129
+
130
+ ## License
131
+
132
+ Author-owned material is available under the standard [MIT License](LICENSE).
133
+ [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) describes the separate scope of
134
+ calendar data, quotations and other third-party material. Those materials are not
135
+ relicensed by the author's MIT grant. Both notices are included in built artifacts.
136
+
137
+ See [RELEASE_NOTES.md](RELEASE_NOTES.md) for version changes and
138
+ [RELEASING.md](RELEASING.md) for the manual release procedure.
bazi-1.0.0/README.md ADDED
@@ -0,0 +1,125 @@
1
+ # Bazi
2
+ > 排盘、五行、十神、纳音、刑冲破害、合会
3
+
4
+ A Python 3.11+ library for Four Pillars charts and their relations. Runtime uses
5
+ only the standard library and five bundled calendar tables; no network access or
6
+ data generation is needed.
7
+
8
+ ## Installation
9
+
10
+ To build and validate the current checkout and retain an installable pair:
11
+
12
+ ```sh
13
+ python -m pip install -r Requirements.txt
14
+ python run_package_checks.py --output-dir ../bazi-tested
15
+ python -m pip install ../bazi-tested/bazi-1.0.0-py3-none-any.whl
16
+ ```
17
+
18
+ The output directory must be empty and outside the checkout. For a published
19
+ version, install from the registry with `python -m pip install bazi==1.0.0`.
20
+ There is no installed CLI, `src` compatibility package or root-class facade.
21
+
22
+ ```python
23
+ from datetime import datetime
24
+
25
+ from bazi.bazi import Bazi
26
+ from bazi.bazi_chart import BaziChart
27
+ from bazi.school import BaziConfig
28
+ from bazi.transit_chart import TransitChart
29
+ from bazi.analyzer.relationship import RelationshipAnalyzer
30
+
31
+ config = BaziConfig.from_values(backend='celestial', precision='day')
32
+ chart = BaziChart(Bazi.create(datetime(2000, 1, 1, 12), 'male', config))
33
+ restored = BaziChart.from_json(chart.json)
34
+ assert restored.json == chart.json
35
+ print(chart.json['pillars'])
36
+ print(TransitChart(chart).at_year(2024))
37
+ print(RelationshipAnalyzer(chart).at_birth.shensha)
38
+ ```
39
+
40
+ Birth times are naive local civil times; timezone-aware inputs are rejected.
41
+ Defaults remain `CELESTIAL` with day precision. `hko` provides date-level calendar
42
+ data; `celestial` and `celestial-algo2` use the bundled astronomical tables.
43
+ Backend differences and supported date ranges are documented in the calendar
44
+ modules; selecting a backend does not install its offline generator.
45
+
46
+ ## Interfaces
47
+
48
+ Use the defining modules rather than expecting classes at the package root:
49
+
50
+ | Module | Principal interfaces |
51
+ | --- | --- |
52
+ | `bazi.bazi` | `Bazi`, `BaziGender` |
53
+ | `bazi.bazi_chart` | `BaziChart`, `BaziJson`, JSON restoration with `from_json` |
54
+ | `bazi.school` | `BaziConfig`, `BaziSchool`, precision and school options |
55
+ | `bazi.calendar` | `CalendarBackend`, `CalendarDate`, `CalendarType`, `calendar_utils_of` |
56
+ | `bazi.transit_chart`, `bazi.transits` | `TransitChart`, `TransitSet`, `TransitKind` |
57
+ | `bazi.analyzer.relationship` | `RelationshipAnalyzer` |
58
+ | `bazi.interpreter` | `Interpreter.interpret_tiangan`, `Interpreter.interpret_shishen` |
59
+ | `bazi.defines`, `bazi.utils` | Domain enums and relation utilities, documented in their modules |
60
+
61
+ Domain names use Pinyin; enums also provide Chinese aliases. JSON retains Chinese
62
+ domain values and records configuration. `py.typed` exposes the inline type hints.
63
+ Private names, offline generation tools and undocumented internals are not a
64
+ promise of a stable public interface.
65
+
66
+ ## Instructions
67
+
68
+ `Requirements.txt` is the development setup, not the installed library's dependency
69
+ list. It includes test, lint, typing and distribution-verification tools.
70
+ Source-tree mypy also needs `celestial-calendar==0.6.1`, as installed by CPython CI.
71
+
72
+ ```sh
73
+ python -m pip install -r Requirements.txt
74
+ python -m pip install celestial-calendar==0.6.1
75
+ python run_tests.py -a -v
76
+ ```
77
+
78
+ The full gate runs every test (including slow and HKO-data tests), requires 100%
79
+ coverage, runs ruff and strict source mypy, demos, Interpreter, optimized input
80
+ checks, and isolated wheel/sdist-derived-wheel checks. The artifact checks use two
81
+ fresh dependency-free consumers and a separate installed mypy environment.
82
+ The artifact checks (`run_package_checks.py`, `-pkg` and `-a`) may download tools
83
+ and their dependencies from the package index to seed isolated build and typing
84
+ environments, even after the development requirements are installed.
85
+ Builds and consumers live in external temporary directories. Root demo scripts may
86
+ write to `output_data/`; the installed library's read-only behavior is checked separately.
87
+
88
+ | Flag | Effect |
89
+ | --- | --- |
90
+ | `-a`, `--all` | Full gate; overrides `-nt` and `-k` |
91
+ | `-nt`, `--no-test` | Skip tests and coverage |
92
+ | `-s`, `-hko` | Include slow tests or HKO-data tests |
93
+ | `-k <expression>` | Select pytest tests; ignores `-s` and `-hko` |
94
+ | `-v` | Verbose output |
95
+ | `-c` | Coverage report, also written to `covhtml/` |
96
+ | `-cr <rate>`, `--coverage-rate <rate>` | Minimum coverage (default 100) |
97
+ | `-r`, `--ruff` | Run `ruff check` |
98
+ | `-m`, `-mypy`, `--mypy` | Run source mypy with the runner's strict flags |
99
+ | `-d` | Run both demo scripts |
100
+ | `-i` | Run Interpreter examples |
101
+ | `-osmoke`, `--o-smoke` | Run source public-contract smoke with `python -O` |
102
+ | `-pkg`, `--package` | Run isolated distribution checks |
103
+ | `--package-output-dir <path>` | With `-pkg` or `-a`, retain only the verified sdist and its rebuilt wheel |
104
+
105
+ A bare `python run_tests.py` omits slow/HKO tests and all optional tasks. It is not
106
+ the full verification gate. Do not run autoformatters; use `ruff check .`.
107
+
108
+ ## Offline Generation
109
+
110
+ The committed data is read, not regenerated, during packaging or installed use.
111
+ The source tree and sdist contain the raw HKO inputs; the wheel does not.
112
+ Maintainers can regenerate from those sources with `python -m bazi.calendar.hko_data.encoder`
113
+ (`requests` is needed only if inputs must be downloaded), or `python -m bazi.calendar.celestial_data.generator`
114
+ with `celestial-calendar==0.6.1`. These optional tools are not runtime dependencies.
115
+ If an installed table is missing, reinstall the distribution instead.
116
+
117
+ ## License
118
+
119
+ Author-owned material is available under the standard [MIT License](LICENSE).
120
+ [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) describes the separate scope of
121
+ calendar data, quotations and other third-party material. Those materials are not
122
+ relicensed by the author's MIT grant. Both notices are included in built artifacts.
123
+
124
+ See [RELEASE_NOTES.md](RELEASE_NOTES.md) for version changes and
125
+ [RELEASING.md](RELEASING.md) for the manual release procedure.
@@ -0,0 +1,16 @@
1
+ # bazi 1.0.0
2
+
3
+ - Installable `bazi` package with modular imports, Python 3.11+, inline typing and
4
+ no runtime dependencies. Source imports now use `bazi.*`, not `src.*`.
5
+ - The wheel bundles the two encoded HKO tables and three celestial tables. The
6
+ source archive also includes raw HKO inputs and source-verification tests.
7
+ - Existing calendar backends, rules, school defaults, JSON restoration, transits,
8
+ relationship analysis and Interpreter remain available without data regeneration.
9
+ - Standard MIT permission covers author-owned material; third-party notices state
10
+ the separate scope of embedded data and quotations.
11
+ - The manual release workflow defaults to rehearsal. Publication requires protected
12
+ environment approval and publishes only the tested sdist and its rebuilt wheel.
13
+
14
+ No `src` shim, root-class facade or installed command-line entrypoint is provided.
15
+ See `README.md` for principal module interfaces and `RELEASING.md` for release
16
+ prerequisites.
@@ -0,0 +1,65 @@
1
+ # Releasing bazi
2
+
3
+ The manual `release.yml` workflow prepares one verified sdist and its rebuilt
4
+ wheel. The direct wheel is only a validation control. There are no push/tag
5
+ publication triggers and no arbitrary ref, registry or artifact inputs.
6
+
7
+ ## Prerequisites
8
+
9
+ The owner must separately confirm PyPI account/project eligibility for `bazi` and
10
+ configure a Trusted Publisher (or pending publisher) for owner `0xf3cd`, repository
11
+ `bazi`, workflow `release.yml`, environment `release`. A public name lookup does not
12
+ reserve the name or establish account readiness. No API token belongs in this repository.
13
+
14
+ Create the GitHub environment `release` explicitly, with at least one required
15
+ reviewer and a custom deployment policy containing exactly branch `main` (not a
16
+ tag or wildcard). Personal owner approval is valid; allow self-review if that is
17
+ the intended approval path. This workflow does not require two people. Only the
18
+ publishing jobs bind the environment; an earlier read-only REST preflight fails
19
+ closed if the environment, reviewer rule or exact branch policy is missing.
20
+
21
+ The workflow must exist on the default branch before it can be dispatched.
22
+
23
+ ## Rehearsal
24
+
25
+ 1. Review `pyproject.toml`'s static version and `RELEASE_NOTES.md`. Merge the intended
26
+ release changes to main before dispatch; do not create a tag yet.
27
+ 2. Dispatch `Prepare or Publish Release` on main with `mode=rehearsal` (the default).
28
+ This runs the full source and artifact gates at the dispatch SHA, with no
29
+ environment binding, write permission or OIDC permission.
30
+ 3. Inspect the run's `tested-release` artifact: exactly one wheel and sdist under
31
+ `dist/`, release notes, `release.json`, and `SHA256SUMS`. Confirm the source SHA,
32
+ run ID, version, licenses, file contents and checksums. Artifacts expire after
33
+ 30 days; retain the approved bytes before expiry when recovery may be needed.
34
+
35
+ Run a rehearsal before authorizing real publication. Local artifact and offline
36
+ policy tests do not substitute for it or prove GitHub/PyPI configuration is ready.
37
+
38
+ ## Real Release
39
+
40
+ 1. Obtain separate authorization and dispatch main with `mode=release`. Confirm
41
+ that the dispatch SHA is the intended release commit. The real run performs its
42
+ own full verification; it never accepts an artifact ID from a previous run.
43
+ 2. Approve the protected environment for PyPI after checking the run's verified
44
+ bundle. The OIDC-only job downloads the exact immutable artifact ID produced by
45
+ this run, verifies checksums and identity, and rejects existing versions/tags/releases.
46
+ 3. After PyPI succeeds, approve the GitHub Release job. This contents-write-only job
47
+ checks PyPI hashes, creates `v<version>` at the tested SHA, creates a draft release,
48
+ uploads the same bytes and notices, checks asset digests, then publishes the draft.
49
+ Neither privileged job checks out, builds, installs or imports repository code.
50
+ 4. Independently download both published artifacts from PyPI and GitHub and compare
51
+ them to the retained checksums. Confirm the tag SHA and release metadata. Install
52
+ the published wheel in a fresh environment and repeat the consumer smoke.
53
+ Verify the actual PyPI Trusted Publisher identity/attestations too.
54
+
55
+ ## Partial Failure
56
+
57
+ Publication is not transactional. An existing version, tag or release is an error;
58
+ there is no overwrite, tag move or `skip-existing` recovery. Do not rerun the whole
59
+ workflow to bypass a partial failure and do not rebuild the accepted bytes.
60
+
61
+ If PyPI has the pair but GitHub failed, stop and inspect the retained same-run
62
+ bundle, PyPI digests, tag SHA and any draft release/assets. The owner must approve
63
+ a targeted recovery of only the missing operations using those exact bytes. If a
64
+ tag or draft already exists, verify it rather than moving or replacing it. A digest
65
+ mismatch requires investigation, not automatic deletion, a version bump or a new upload.
@@ -0,0 +1,17 @@
1
+ colorama==0.4.6
2
+ psutil==7.2.2
3
+ tzdata==2026.3
4
+ pytest==9.1.1
5
+ ruff==0.16.1
6
+ coverage==7.16.0
7
+ mypy==2.3.1
8
+ emoji==2.15.0
9
+ types-colorama==0.4.15.20260508
10
+ types-psutil==7.2.2.20260906
11
+
12
+ # Distribution builds and artifact verification (not runtime dependencies).
13
+ build==1.6.1
14
+ setuptools==84.0.0
15
+ twine==7.0.0
16
+ PyYAML==6.0.3
17
+ types-PyYAML==6.0.12.20250915