crabxl 0.1.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 (354) hide show
  1. crabxl-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +46 -0
  2. crabxl-0.1.0/.github/ISSUE_TEMPLATE/compatibility.yml +32 -0
  3. crabxl-0.1.0/.github/ISSUE_TEMPLATE/config.yml +8 -0
  4. crabxl-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +16 -0
  5. crabxl-0.1.0/.github/actions/setup-supported-python/action.yml +42 -0
  6. crabxl-0.1.0/.github/pull_request_template.md +15 -0
  7. crabxl-0.1.0/.github/workflows/docs.yml +30 -0
  8. crabxl-0.1.0/.github/workflows/python.yml +62 -0
  9. crabxl-0.1.0/.github/workflows/release.yml +181 -0
  10. crabxl-0.1.0/.gitignore +10 -0
  11. crabxl-0.1.0/CHANGELOG.md +47 -0
  12. crabxl-0.1.0/CONTRIBUTING.md +146 -0
  13. crabxl-0.1.0/Cargo.lock +590 -0
  14. crabxl-0.1.0/Cargo.toml +33 -0
  15. crabxl-0.1.0/LICENSE +21 -0
  16. crabxl-0.1.0/PKG-INFO +153 -0
  17. crabxl-0.1.0/README.md +126 -0
  18. crabxl-0.1.0/SECURITY.md +30 -0
  19. crabxl-0.1.0/docs/changelog.md +7 -0
  20. crabxl-0.1.0/docs/compatibility.md +54 -0
  21. crabxl-0.1.0/docs/guide/charts/anchors.md +99 -0
  22. crabxl-0.1.0/docs/guide/charts/area.md +102 -0
  23. crabxl-0.1.0/docs/guide/charts/area.png +0 -0
  24. crabxl-0.1.0/docs/guide/charts/area3d.png +0 -0
  25. crabxl-0.1.0/docs/guide/charts/bar.md +123 -0
  26. crabxl-0.1.0/docs/guide/charts/bar.png +0 -0
  27. crabxl-0.1.0/docs/guide/charts/bar3d.png +0 -0
  28. crabxl-0.1.0/docs/guide/charts/bubble.md +62 -0
  29. crabxl-0.1.0/docs/guide/charts/bubble.png +0 -0
  30. crabxl-0.1.0/docs/guide/charts/chart-layout.md +139 -0
  31. crabxl-0.1.0/docs/guide/charts/chart-layout.png +0 -0
  32. crabxl-0.1.0/docs/guide/charts/chartsheet.md +46 -0
  33. crabxl-0.1.0/docs/guide/charts/chartsheet.png +0 -0
  34. crabxl-0.1.0/docs/guide/charts/doughnut.md +69 -0
  35. crabxl-0.1.0/docs/guide/charts/doughnut.png +0 -0
  36. crabxl-0.1.0/docs/guide/charts/gauge.md +73 -0
  37. crabxl-0.1.0/docs/guide/charts/gauge.png +0 -0
  38. crabxl-0.1.0/docs/guide/charts/graphical.md +87 -0
  39. crabxl-0.1.0/docs/guide/charts/highlighted.png +0 -0
  40. crabxl-0.1.0/docs/guide/charts/index.md +58 -0
  41. crabxl-0.1.0/docs/guide/charts/limits-and-scaling-log.png +0 -0
  42. crabxl-0.1.0/docs/guide/charts/limits-and-scaling-minmax.png +0 -0
  43. crabxl-0.1.0/docs/guide/charts/limits-and-scaling-orientation.png +0 -0
  44. crabxl-0.1.0/docs/guide/charts/limits-and-scaling.md +229 -0
  45. crabxl-0.1.0/docs/guide/charts/line.md +150 -0
  46. crabxl-0.1.0/docs/guide/charts/line.png +0 -0
  47. crabxl-0.1.0/docs/guide/charts/line3d.png +0 -0
  48. crabxl-0.1.0/docs/guide/charts/meta.json +24 -0
  49. crabxl-0.1.0/docs/guide/charts/pattern.md +57 -0
  50. crabxl-0.1.0/docs/guide/charts/pattern.png +0 -0
  51. crabxl-0.1.0/docs/guide/charts/pie-gradient.png +0 -0
  52. crabxl-0.1.0/docs/guide/charts/pie.md +192 -0
  53. crabxl-0.1.0/docs/guide/charts/pie.png +0 -0
  54. crabxl-0.1.0/docs/guide/charts/pie3d.png +0 -0
  55. crabxl-0.1.0/docs/guide/charts/projected-pie.png +0 -0
  56. crabxl-0.1.0/docs/guide/charts/radar.md +67 -0
  57. crabxl-0.1.0/docs/guide/charts/radar.png +0 -0
  58. crabxl-0.1.0/docs/guide/charts/scatter.md +60 -0
  59. crabxl-0.1.0/docs/guide/charts/scatter.png +0 -0
  60. crabxl-0.1.0/docs/guide/charts/secondary.md +58 -0
  61. crabxl-0.1.0/docs/guide/charts/secondary.png +0 -0
  62. crabxl-0.1.0/docs/guide/charts/stock.md +181 -0
  63. crabxl-0.1.0/docs/guide/charts/stock.png +0 -0
  64. crabxl-0.1.0/docs/guide/charts/surface.md +143 -0
  65. crabxl-0.1.0/docs/guide/charts/surface.png +0 -0
  66. crabxl-0.1.0/docs/guide/comments.md +93 -0
  67. crabxl-0.1.0/docs/guide/conditional-formatting.md +207 -0
  68. crabxl-0.1.0/docs/guide/dates.md +101 -0
  69. crabxl-0.1.0/docs/guide/defined-names.md +104 -0
  70. crabxl-0.1.0/docs/guide/document-properties.md +63 -0
  71. crabxl-0.1.0/docs/guide/editing-worksheets.md +81 -0
  72. crabxl-0.1.0/docs/guide/exercise-1-result.png +0 -0
  73. crabxl-0.1.0/docs/guide/filters.md +153 -0
  74. crabxl-0.1.0/docs/guide/filters.png +0 -0
  75. crabxl-0.1.0/docs/guide/formula-parsing.md +115 -0
  76. crabxl-0.1.0/docs/guide/formulas.md +65 -0
  77. crabxl-0.1.0/docs/guide/images.md +24 -0
  78. crabxl-0.1.0/docs/guide/memory.md +48 -0
  79. crabxl-0.1.0/docs/guide/meta.json +29 -0
  80. crabxl-0.1.0/docs/guide/optimized.md +122 -0
  81. crabxl-0.1.0/docs/guide/pandas.md +102 -0
  82. crabxl-0.1.0/docs/guide/pivot-tables.md +30 -0
  83. crabxl-0.1.0/docs/guide/print-settings.md +89 -0
  84. crabxl-0.1.0/docs/guide/protection.md +70 -0
  85. crabxl-0.1.0/docs/guide/rich-text.md +144 -0
  86. crabxl-0.1.0/docs/guide/simple-usage.md +84 -0
  87. crabxl-0.1.0/docs/guide/styles.md +355 -0
  88. crabxl-0.1.0/docs/guide/tables.md +142 -0
  89. crabxl-0.1.0/docs/guide/tutorial.md +388 -0
  90. crabxl-0.1.0/docs/guide/validation.md +131 -0
  91. crabxl-0.1.0/docs/guide/worksheet-properties.md +90 -0
  92. crabxl-0.1.0/docs/index.md +36 -0
  93. crabxl-0.1.0/docs/meta.json +4 -0
  94. crabxl-0.1.0/docs/migration.md +58 -0
  95. crabxl-0.1.0/docs/quick-start.md +51 -0
  96. crabxl-0.1.0/docs/support.md +33 -0
  97. crabxl-0.1.0/internal/STATUS.md +89 -0
  98. crabxl-0.1.0/internal/architecture.md +54 -0
  99. crabxl-0.1.0/internal/binding-contract.md +52 -0
  100. crabxl-0.1.0/internal/releases.md +94 -0
  101. crabxl-0.1.0/internal/roadmap.md +27 -0
  102. crabxl-0.1.0/pyproject.toml +84 -0
  103. crabxl-0.1.0/python/crabxl/__init__.py +66 -0
  104. crabxl-0.1.0/python/crabxl/_features.py +469 -0
  105. crabxl-0.1.0/python/crabxl/_values.py +142 -0
  106. crabxl-0.1.0/python/crabxl/cell/__init__.py +18 -0
  107. crabxl-0.1.0/python/crabxl/cell/cell.py +449 -0
  108. crabxl-0.1.0/python/crabxl/cell/read_only.py +5 -0
  109. crabxl-0.1.0/python/crabxl/cell/rich_text.py +298 -0
  110. crabxl-0.1.0/python/crabxl/cell/text.py +213 -0
  111. crabxl-0.1.0/python/crabxl/chart/_3d.py +106 -0
  112. crabxl-0.1.0/python/crabxl/chart/__init__.py +20 -0
  113. crabxl-0.1.0/python/crabxl/chart/_chart.py +212 -0
  114. crabxl-0.1.0/python/crabxl/chart/area_chart.py +107 -0
  115. crabxl-0.1.0/python/crabxl/chart/axis.py +402 -0
  116. crabxl-0.1.0/python/crabxl/chart/bar_chart.py +145 -0
  117. crabxl-0.1.0/python/crabxl/chart/bubble_chart.py +68 -0
  118. crabxl-0.1.0/python/crabxl/chart/chartspace.py +196 -0
  119. crabxl-0.1.0/python/crabxl/chart/data_source.py +247 -0
  120. crabxl-0.1.0/python/crabxl/chart/descriptors.py +44 -0
  121. crabxl-0.1.0/python/crabxl/chart/error_bar.py +63 -0
  122. crabxl-0.1.0/python/crabxl/chart/label.py +128 -0
  123. crabxl-0.1.0/python/crabxl/chart/layout.py +75 -0
  124. crabxl-0.1.0/python/crabxl/chart/legend.py +76 -0
  125. crabxl-0.1.0/python/crabxl/chart/line_chart.py +130 -0
  126. crabxl-0.1.0/python/crabxl/chart/marker.py +91 -0
  127. crabxl-0.1.0/python/crabxl/chart/picture.py +36 -0
  128. crabxl-0.1.0/python/crabxl/chart/pie_chart.py +186 -0
  129. crabxl-0.1.0/python/crabxl/chart/pivot.py +66 -0
  130. crabxl-0.1.0/python/crabxl/chart/plotarea.py +163 -0
  131. crabxl-0.1.0/python/crabxl/chart/print_settings.py +58 -0
  132. crabxl-0.1.0/python/crabxl/chart/radar_chart.py +56 -0
  133. crabxl-0.1.0/python/crabxl/chart/reader.py +33 -0
  134. crabxl-0.1.0/python/crabxl/chart/reference.py +125 -0
  135. crabxl-0.1.0/python/crabxl/chart/scatter_chart.py +54 -0
  136. crabxl-0.1.0/python/crabxl/chart/series.py +198 -0
  137. crabxl-0.1.0/python/crabxl/chart/series_factory.py +42 -0
  138. crabxl-0.1.0/python/crabxl/chart/shapes.py +90 -0
  139. crabxl-0.1.0/python/crabxl/chart/stock_chart.py +55 -0
  140. crabxl-0.1.0/python/crabxl/chart/surface_chart.py +120 -0
  141. crabxl-0.1.0/python/crabxl/chart/text.py +79 -0
  142. crabxl-0.1.0/python/crabxl/chart/title.py +77 -0
  143. crabxl-0.1.0/python/crabxl/chart/trendline.py +99 -0
  144. crabxl-0.1.0/python/crabxl/chart/updown_bars.py +32 -0
  145. crabxl-0.1.0/python/crabxl/chartsheet/__init__.py +3 -0
  146. crabxl-0.1.0/python/crabxl/chartsheet/chartsheet.py +251 -0
  147. crabxl-0.1.0/python/crabxl/chartsheet/custom.py +53 -0
  148. crabxl-0.1.0/python/crabxl/chartsheet/properties.py +25 -0
  149. crabxl-0.1.0/python/crabxl/chartsheet/protection.py +46 -0
  150. crabxl-0.1.0/python/crabxl/chartsheet/publish.py +69 -0
  151. crabxl-0.1.0/python/crabxl/chartsheet/relation.py +95 -0
  152. crabxl-0.1.0/python/crabxl/chartsheet/views.py +50 -0
  153. crabxl-0.1.0/python/crabxl/comments/__init__.py +4 -0
  154. crabxl-0.1.0/python/crabxl/comments/author.py +18 -0
  155. crabxl-0.1.0/python/crabxl/comments/comment_sheet.py +197 -0
  156. crabxl-0.1.0/python/crabxl/comments/comments.py +52 -0
  157. crabxl-0.1.0/python/crabxl/compat/__init__.py +55 -0
  158. crabxl-0.1.0/python/crabxl/compat/abc.py +9 -0
  159. crabxl-0.1.0/python/crabxl/compat/numbers.py +46 -0
  160. crabxl-0.1.0/python/crabxl/compat/product.py +18 -0
  161. crabxl-0.1.0/python/crabxl/compat/singleton.py +40 -0
  162. crabxl-0.1.0/python/crabxl/compat/strings.py +25 -0
  163. crabxl-0.1.0/python/crabxl/descriptors/__init__.py +55 -0
  164. crabxl-0.1.0/python/crabxl/descriptors/base.py +259 -0
  165. crabxl-0.1.0/python/crabxl/descriptors/container.py +37 -0
  166. crabxl-0.1.0/python/crabxl/descriptors/excel.py +108 -0
  167. crabxl-0.1.0/python/crabxl/descriptors/namespace.py +12 -0
  168. crabxl-0.1.0/python/crabxl/descriptors/nested.py +116 -0
  169. crabxl-0.1.0/python/crabxl/descriptors/sequence.py +129 -0
  170. crabxl-0.1.0/python/crabxl/descriptors/serialisable.py +233 -0
  171. crabxl-0.1.0/python/crabxl/descriptors/slots.py +18 -0
  172. crabxl-0.1.0/python/crabxl/drawing/__init__.py +3 -0
  173. crabxl-0.1.0/python/crabxl/drawing/colors.py +436 -0
  174. crabxl-0.1.0/python/crabxl/drawing/connector.py +151 -0
  175. crabxl-0.1.0/python/crabxl/drawing/drawing.py +84 -0
  176. crabxl-0.1.0/python/crabxl/drawing/effect.py +408 -0
  177. crabxl-0.1.0/python/crabxl/drawing/fill.py +426 -0
  178. crabxl-0.1.0/python/crabxl/drawing/geometry.py +585 -0
  179. crabxl-0.1.0/python/crabxl/drawing/graphic.py +177 -0
  180. crabxl-0.1.0/python/crabxl/drawing/image.py +78 -0
  181. crabxl-0.1.0/python/crabxl/drawing/line.py +145 -0
  182. crabxl-0.1.0/python/crabxl/drawing/picture.py +151 -0
  183. crabxl-0.1.0/python/crabxl/drawing/properties.py +190 -0
  184. crabxl-0.1.0/python/crabxl/drawing/relation.py +16 -0
  185. crabxl-0.1.0/python/crabxl/drawing/spreadsheet_drawing.py +130 -0
  186. crabxl-0.1.0/python/crabxl/drawing/text.py +718 -0
  187. crabxl-0.1.0/python/crabxl/drawing/xdr.py +30 -0
  188. crabxl-0.1.0/python/crabxl/formatting/__init__.py +3 -0
  189. crabxl-0.1.0/python/crabxl/formatting/formatting.py +103 -0
  190. crabxl-0.1.0/python/crabxl/formatting/rule.py +398 -0
  191. crabxl-0.1.0/python/crabxl/formula/__init__.py +5 -0
  192. crabxl-0.1.0/python/crabxl/formula/tokenizer.py +246 -0
  193. crabxl-0.1.0/python/crabxl/formula/translate.py +73 -0
  194. crabxl-0.1.0/python/crabxl/optimized.py +904 -0
  195. crabxl-0.1.0/python/crabxl/packaging/__init__.py +4 -0
  196. crabxl-0.1.0/python/crabxl/packaging/core.py +130 -0
  197. crabxl-0.1.0/python/crabxl/packaging/custom.py +254 -0
  198. crabxl-0.1.0/python/crabxl/packaging/extended.py +139 -0
  199. crabxl-0.1.0/python/crabxl/packaging/interface.py +48 -0
  200. crabxl-0.1.0/python/crabxl/packaging/manifest.py +170 -0
  201. crabxl-0.1.0/python/crabxl/packaging/relationship.py +137 -0
  202. crabxl-0.1.0/python/crabxl/packaging/workbook.py +196 -0
  203. crabxl-0.1.0/python/crabxl/pivot/__init__.py +32 -0
  204. crabxl-0.1.0/python/crabxl/pivot/_loaded.py +102 -0
  205. crabxl-0.1.0/python/crabxl/pivot/cache.py +977 -0
  206. crabxl-0.1.0/python/crabxl/pivot/fields.py +330 -0
  207. crabxl-0.1.0/python/crabxl/pivot/record.py +89 -0
  208. crabxl-0.1.0/python/crabxl/pivot/table.py +1375 -0
  209. crabxl-0.1.0/python/crabxl/reader/__init__.py +1 -0
  210. crabxl-0.1.0/python/crabxl/reader/excel.py +3 -0
  211. crabxl-0.1.0/python/crabxl/reader/strings.py +39 -0
  212. crabxl-0.1.0/python/crabxl/resources.py +149 -0
  213. crabxl-0.1.0/python/crabxl/styles/__init__.py +27 -0
  214. crabxl-0.1.0/python/crabxl/styles/_base.py +313 -0
  215. crabxl-0.1.0/python/crabxl/styles/_collections.py +92 -0
  216. crabxl-0.1.0/python/crabxl/styles/_x/__init__.py +10 -0
  217. crabxl-0.1.0/python/crabxl/styles/_x/alignment.py +85 -0
  218. crabxl-0.1.0/python/crabxl/styles/_x/borders.py +138 -0
  219. crabxl-0.1.0/python/crabxl/styles/_x/colors.py +227 -0
  220. crabxl-0.1.0/python/crabxl/styles/_x/fills.py +237 -0
  221. crabxl-0.1.0/python/crabxl/styles/_x/fonts.py +147 -0
  222. crabxl-0.1.0/python/crabxl/styles/_x/numbers.py +197 -0
  223. crabxl-0.1.0/python/crabxl/styles/_x/protection.py +17 -0
  224. crabxl-0.1.0/python/crabxl/styles/alignment.py +65 -0
  225. crabxl-0.1.0/python/crabxl/styles/borders.py +85 -0
  226. crabxl-0.1.0/python/crabxl/styles/builtins.py +1397 -0
  227. crabxl-0.1.0/python/crabxl/styles/cell_style.py +215 -0
  228. crabxl-0.1.0/python/crabxl/styles/colors.py +126 -0
  229. crabxl-0.1.0/python/crabxl/styles/differential.py +124 -0
  230. crabxl-0.1.0/python/crabxl/styles/fills.py +146 -0
  231. crabxl-0.1.0/python/crabxl/styles/fonts.py +87 -0
  232. crabxl-0.1.0/python/crabxl/styles/named_styles.py +394 -0
  233. crabxl-0.1.0/python/crabxl/styles/numbers.py +3 -0
  234. crabxl-0.1.0/python/crabxl/styles/protection.py +12 -0
  235. crabxl-0.1.0/python/crabxl/styles/proxy.py +5 -0
  236. crabxl-0.1.0/python/crabxl/styles/styleable.py +139 -0
  237. crabxl-0.1.0/python/crabxl/styles/stylesheet.py +201 -0
  238. crabxl-0.1.0/python/crabxl/styles/table.py +119 -0
  239. crabxl-0.1.0/python/crabxl/utils/__init__.py +15 -0
  240. crabxl-0.1.0/python/crabxl/utils/bound_dictionary.py +25 -0
  241. crabxl-0.1.0/python/crabxl/utils/cell.py +246 -0
  242. crabxl-0.1.0/python/crabxl/utils/dataframe.py +88 -0
  243. crabxl-0.1.0/python/crabxl/utils/datetime.py +147 -0
  244. crabxl-0.1.0/python/crabxl/utils/escape.py +42 -0
  245. crabxl-0.1.0/python/crabxl/utils/exceptions.py +29 -0
  246. crabxl-0.1.0/python/crabxl/utils/formulas.py +382 -0
  247. crabxl-0.1.0/python/crabxl/utils/indexed_list.py +49 -0
  248. crabxl-0.1.0/python/crabxl/utils/inference.py +67 -0
  249. crabxl-0.1.0/python/crabxl/utils/protection.py +22 -0
  250. crabxl-0.1.0/python/crabxl/utils/units.py +112 -0
  251. crabxl-0.1.0/python/crabxl/workbook/__init__.py +3 -0
  252. crabxl-0.1.0/python/crabxl/workbook/child.py +153 -0
  253. crabxl-0.1.0/python/crabxl/workbook/defined_name.py +188 -0
  254. crabxl-0.1.0/python/crabxl/workbook/external_link/__init__.py +3 -0
  255. crabxl-0.1.0/python/crabxl/workbook/external_link/external.py +186 -0
  256. crabxl-0.1.0/python/crabxl/workbook/external_reference.py +15 -0
  257. crabxl-0.1.0/python/crabxl/workbook/function_group.py +37 -0
  258. crabxl-0.1.0/python/crabxl/workbook/loader.py +109 -0
  259. crabxl-0.1.0/python/crabxl/workbook/properties.py +151 -0
  260. crabxl-0.1.0/python/crabxl/workbook/protection.py +180 -0
  261. crabxl-0.1.0/python/crabxl/workbook/smart_tags.py +57 -0
  262. crabxl-0.1.0/python/crabxl/workbook/views.py +155 -0
  263. crabxl-0.1.0/python/crabxl/workbook/web.py +112 -0
  264. crabxl-0.1.0/python/crabxl/workbook/workbook.py +721 -0
  265. crabxl-0.1.0/python/crabxl/worksheet/__init__.py +3 -0
  266. crabxl-0.1.0/python/crabxl/worksheet/_cell_cache.py +43 -0
  267. crabxl-0.1.0/python/crabxl/worksheet/cell_range.py +201 -0
  268. crabxl-0.1.0/python/crabxl/worksheet/cell_watch.py +35 -0
  269. crabxl-0.1.0/python/crabxl/worksheet/controls.py +108 -0
  270. crabxl-0.1.0/python/crabxl/worksheet/custom.py +35 -0
  271. crabxl-0.1.0/python/crabxl/worksheet/datavalidation.py +219 -0
  272. crabxl-0.1.0/python/crabxl/worksheet/dimensions.py +578 -0
  273. crabxl-0.1.0/python/crabxl/worksheet/drawing.py +15 -0
  274. crabxl-0.1.0/python/crabxl/worksheet/errors.py +93 -0
  275. crabxl-0.1.0/python/crabxl/worksheet/filters.py +576 -0
  276. crabxl-0.1.0/python/crabxl/worksheet/formula.py +70 -0
  277. crabxl-0.1.0/python/crabxl/worksheet/header_footer.py +277 -0
  278. crabxl-0.1.0/python/crabxl/worksheet/hyperlink.py +199 -0
  279. crabxl-0.1.0/python/crabxl/worksheet/merge.py +237 -0
  280. crabxl-0.1.0/python/crabxl/worksheet/multi_cell_range.py +90 -0
  281. crabxl-0.1.0/python/crabxl/worksheet/ole.py +134 -0
  282. crabxl-0.1.0/python/crabxl/worksheet/page.py +175 -0
  283. crabxl-0.1.0/python/crabxl/worksheet/pagebreak.py +89 -0
  284. crabxl-0.1.0/python/crabxl/worksheet/picture.py +9 -0
  285. crabxl-0.1.0/python/crabxl/worksheet/print_settings.py +175 -0
  286. crabxl-0.1.0/python/crabxl/worksheet/properties.py +94 -0
  287. crabxl-0.1.0/python/crabxl/worksheet/protection.py +149 -0
  288. crabxl-0.1.0/python/crabxl/worksheet/related.py +14 -0
  289. crabxl-0.1.0/python/crabxl/worksheet/scenario.py +103 -0
  290. crabxl-0.1.0/python/crabxl/worksheet/smart_tag.py +78 -0
  291. crabxl-0.1.0/python/crabxl/worksheet/table.py +385 -0
  292. crabxl-0.1.0/python/crabxl/worksheet/views.py +155 -0
  293. crabxl-0.1.0/python/crabxl/worksheet/worksheet.py +1319 -0
  294. crabxl-0.1.0/python/crabxl/xml/__init__.py +47 -0
  295. crabxl-0.1.0/python/crabxl/xml/_preserve.py +170 -0
  296. crabxl-0.1.0/python/crabxl/xml/constants.py +131 -0
  297. crabxl-0.1.0/python/crabxl/xml/functions.py +61 -0
  298. crabxl-0.1.0/rust-toolchain.toml +4 -0
  299. crabxl-0.1.0/src/dimensions.rs +183 -0
  300. crabxl-0.1.0/src/editor.rs +471 -0
  301. crabxl-0.1.0/src/features.rs +932 -0
  302. crabxl-0.1.0/src/functions.rs +268 -0
  303. crabxl-0.1.0/src/hyperlink_views.rs +199 -0
  304. crabxl-0.1.0/src/images.rs +117 -0
  305. crabxl-0.1.0/src/imports.rs +23 -0
  306. crabxl-0.1.0/src/lib.rs +98 -0
  307. crabxl-0.1.0/src/reader.rs +557 -0
  308. crabxl-0.1.0/src/resources.rs +177 -0
  309. crabxl-0.1.0/src/rich_text.rs +120 -0
  310. crabxl-0.1.0/src/streaming/writer.rs +887 -0
  311. crabxl-0.1.0/src/streaming.rs +280 -0
  312. crabxl-0.1.0/src/style_owners.rs +471 -0
  313. crabxl-0.1.0/src/styles.rs +440 -0
  314. crabxl-0.1.0/src/values.rs +452 -0
  315. crabxl-0.1.0/src/workbook.rs +247 -0
  316. crabxl-0.1.0/src/worksheet/hyperlinks.rs +116 -0
  317. crabxl-0.1.0/src/worksheet/rows.rs +84 -0
  318. crabxl-0.1.0/src/worksheet.rs +1197 -0
  319. crabxl-0.1.0/tests/conftest.py +53 -0
  320. crabxl-0.1.0/tests/test_a12_compat.py +309 -0
  321. crabxl-0.1.0/tests/test_alpha11_integration.py +284 -0
  322. crabxl-0.1.0/tests/test_charts.py +422 -0
  323. crabxl-0.1.0/tests/test_compression.py +70 -0
  324. crabxl-0.1.0/tests/test_dimensions.py +107 -0
  325. crabxl-0.1.0/tests/test_features.py +1387 -0
  326. crabxl-0.1.0/tests/test_hyperlinks.py +117 -0
  327. crabxl-0.1.0/tests/test_images.py +270 -0
  328. crabxl-0.1.0/tests/test_merges.py +201 -0
  329. crabxl-0.1.0/tests/test_nonfinite.py +60 -0
  330. crabxl-0.1.0/tests/test_optimized.py +644 -0
  331. crabxl-0.1.0/tests/test_parity.py +2335 -0
  332. crabxl-0.1.0/tests/test_plain_append.py +84 -0
  333. crabxl-0.1.0/tests/test_resources.py +375 -0
  334. crabxl-0.1.0/tests/test_rich_text.py +247 -0
  335. crabxl-0.1.0/tests/test_structured_formulas.py +426 -0
  336. crabxl-0.1.0/tests/test_tokenizer.py +110 -0
  337. crabxl-0.1.0/tests/test_upstream_translate.py +197 -0
  338. crabxl-0.1.0/tests/test_upstream_workbook.py +146 -0
  339. crabxl-0.1.0/tests/test_upstream_worksheet.py +299 -0
  340. crabxl-0.1.0/tests/test_xml_preserve.py +66 -0
  341. crabxl-0.1.0/third_party/README.md +31 -0
  342. crabxl-0.1.0/third_party/licenses/openpyxl-MIT.txt +23 -0
  343. crabxl-0.1.0/third_party/python-formula-tests.json +28 -0
  344. crabxl-0.1.0/third_party/python-tests.json +58 -0
  345. crabxl-0.1.0/third_party/python-workbook-tests.json +37 -0
  346. crabxl-0.1.0/tools/check_release_files.py +106 -0
  347. crabxl-0.1.0/tools/openpyxl_suite_expected.json +339 -0
  348. crabxl-0.1.0/tools/platform_wheels.py +80 -0
  349. crabxl-0.1.0/tools/release.py +89 -0
  350. crabxl-0.1.0/tools/release_tag.py +42 -0
  351. crabxl-0.1.0/tools/run_openpyxl_suite.py +216 -0
  352. crabxl-0.1.0/tools/test_distribution.py +32 -0
  353. crabxl-0.1.0/tools/verify_openpyxl_corpus.py +282 -0
  354. crabxl-0.1.0/tools/verify_python_test_provenance.py +61 -0
@@ -0,0 +1,46 @@
1
+ name: Bug report
2
+ description: CrabXL gives an incorrect result, an error or a crash.
3
+ labels: [bug]
4
+ body:
5
+ - type: input
6
+ id: version
7
+ attributes:
8
+ label: CrabXL version
9
+ description: Run `python -c "import crabxl; print(crabxl.__version__)"`.
10
+ placeholder: 0.1.0
11
+ validations:
12
+ required: true
13
+ - type: input
14
+ id: environment
15
+ attributes:
16
+ label: Python version and platform
17
+ placeholder: CPython 3.13, Linux x86_64
18
+ validations:
19
+ required: true
20
+ - type: textarea
21
+ id: problem
22
+ attributes:
23
+ label: Problem
24
+ description: What did CrabXL do? Include the full traceback.
25
+ validations:
26
+ required: true
27
+ - type: textarea
28
+ id: expected
29
+ attributes:
30
+ label: Expected result
31
+ description: What did you expect? If openpyxl gives a different result, tell us.
32
+ validations:
33
+ required: true
34
+ - type: textarea
35
+ id: reproduce
36
+ attributes:
37
+ label: Steps to reproduce
38
+ description: Give the smallest code that shows the problem.
39
+ render: python
40
+ validations:
41
+ required: true
42
+ - type: textarea
43
+ id: file
44
+ attributes:
45
+ label: Input file
46
+ description: Attach the smallest .xlsx file that shows the problem. Remove confidential data first.
@@ -0,0 +1,32 @@
1
+ name: Difference from openpyxl
2
+ description: Code that works with openpyxl 3.1.5 fails or gives a different result with CrabXL.
3
+ labels: [compatibility]
4
+ body:
5
+ - type: textarea
6
+ id: code
7
+ attributes:
8
+ label: Code
9
+ description: Give the smallest code that shows the difference.
10
+ render: python
11
+ validations:
12
+ required: true
13
+ - type: textarea
14
+ id: openpyxl
15
+ attributes:
16
+ label: openpyxl result
17
+ validations:
18
+ required: true
19
+ - type: textarea
20
+ id: crabxl
21
+ attributes:
22
+ label: CrabXL result
23
+ description: For example, a NotImplementedError or a different value.
24
+ validations:
25
+ required: true
26
+ - type: input
27
+ id: version
28
+ attributes:
29
+ label: CrabXL version
30
+ placeholder: 0.1.0
31
+ validations:
32
+ required: true
@@ -0,0 +1,8 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: Rust core problem
4
+ url: https://github.com/crabxl/crabxl/issues/new/choose
5
+ about: Report problems with the Rust API in the crabxl repository.
6
+ - name: Security vulnerability
7
+ url: https://github.com/crabxl/crabxl-python/security/advisories/new
8
+ about: Report a vulnerability privately. Do not open a public issue.
@@ -0,0 +1,16 @@
1
+ name: Feature request
2
+ description: Suggest a new capability or an API improvement.
3
+ labels: [enhancement]
4
+ body:
5
+ - type: textarea
6
+ id: need
7
+ attributes:
8
+ label: Need
9
+ description: What do you want to do? Why can you not do it now?
10
+ validations:
11
+ required: true
12
+ - type: textarea
13
+ id: proposal
14
+ attributes:
15
+ label: Proposal
16
+ description: How could the API look? Give example code if possible.
@@ -0,0 +1,42 @@
1
+ name: Set up supported CPython versions
2
+ description: Install all supported interpreters on one runner; leave Python 3.14 as the builder.
3
+ outputs:
4
+ python-311:
5
+ description: CPython 3.11 executable
6
+ value: ${{ steps.py311.outputs.python-path }}
7
+ python-312:
8
+ description: CPython 3.12 executable
9
+ value: ${{ steps.py312.outputs.python-path }}
10
+ python-313:
11
+ description: CPython 3.13 executable
12
+ value: ${{ steps.py313.outputs.python-path }}
13
+ python-314:
14
+ description: CPython 3.14 executable
15
+ value: ${{ steps.py314.outputs.python-path }}
16
+ python-315:
17
+ description: CPython 3.15 executable
18
+ value: ${{ steps.py315.outputs.python-path }}
19
+ runs:
20
+ using: composite
21
+ steps:
22
+ - uses: actions/setup-python@v7
23
+ id: py311
24
+ with:
25
+ python-version: '3.11'
26
+ - uses: actions/setup-python@v7
27
+ id: py312
28
+ with:
29
+ python-version: '3.12'
30
+ - uses: actions/setup-python@v7
31
+ id: py313
32
+ with:
33
+ python-version: '3.13'
34
+ - uses: actions/setup-python@v7
35
+ id: py315
36
+ with:
37
+ python-version: '3.15'
38
+ allow-prereleases: true
39
+ - uses: actions/setup-python@v7
40
+ id: py314
41
+ with:
42
+ python-version: '3.14'
@@ -0,0 +1,15 @@
1
+ ## Change
2
+
3
+ <!-- What does this pull request change, and why? -->
4
+
5
+ Closes #
6
+
7
+ ## Checks
8
+
9
+ - [ ] `ruff format --check .` and `ruff check .`
10
+ - [ ] `cargo fmt -- --check` and `cargo clippy --all-targets --locked -- -D warnings`
11
+ - [ ] `python -m pytest tests -q`
12
+ - [ ] Behavior matches openpyxl 3.1.5, or the difference is an explicit error.
13
+ - [ ] Selected openpyxl test assertions are unchanged.
14
+ - [ ] User documentation in `docs/` is updated, if behavior changed.
15
+ - [ ] Performance claims include measurements.
@@ -0,0 +1,30 @@
1
+ name: Documentation
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ paths: [docs/**]
7
+ pull_request:
8
+ paths: [docs/**]
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ jobs:
14
+ check:
15
+ uses: crabxl/crabxl-docs/.github/workflows/check-docs.yml@main
16
+
17
+ rebuild-site:
18
+ needs: check
19
+ if: github.event_name == 'push'
20
+ runs-on: ubuntu-latest
21
+ steps:
22
+ - name: Start a rebuild of the documentation site
23
+ env:
24
+ HOOK: ${{ secrets.DOCS_DEPLOY_HOOK }}
25
+ run: |
26
+ if [ -z "$HOOK" ]; then
27
+ echo "::notice::DOCS_DEPLOY_HOOK is not set. The daily rebuild includes this change."
28
+ exit 0
29
+ fi
30
+ curl -fsS -X POST "$HOOK"
@@ -0,0 +1,62 @@
1
+ name: Python compatibility
2
+ on:
3
+ push:
4
+ pull_request:
5
+ permissions:
6
+ contents: read
7
+ jobs:
8
+ quality:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v7
12
+ - uses: actions/setup-python@v7
13
+ with:
14
+ python-version: '3.14'
15
+ - run: python -m pip install 'ruff>=0.16.10,<0.17'
16
+ - run: ruff format --check .
17
+ - run: ruff check .
18
+ adapter:
19
+ needs: quality
20
+ runs-on: ubuntu-latest
21
+ steps:
22
+ - uses: actions/checkout@v7
23
+ - uses: dtolnay/rust-toolchain@stable
24
+ with:
25
+ components: rustfmt, clippy
26
+ - uses: ./.github/actions/setup-supported-python
27
+ id: pythons
28
+ - run: cargo fmt -- --check
29
+ - run: cargo clippy --all-targets --locked -- -D warnings
30
+ - run: python -m pip install 'maturin>=1.15,<2'
31
+ - name: Build all five wheels with shared Rust dependencies
32
+ env:
33
+ PYTHON_PATHS: ${{ toJSON(steps.pythons.outputs) }}
34
+ run: python tools/platform_wheels.py build
35
+ - name: Test each wheel in its matching clean Python environment
36
+ env:
37
+ PYTHON_PATHS: ${{ toJSON(steps.pythons.outputs) }}
38
+ run: python tools/platform_wheels.py test
39
+ - run: maturin sdist --out dist
40
+ - uses: actions/upload-artifact@v7
41
+ with:
42
+ name: python-packages-linux-x86_64
43
+ path: dist/*
44
+ if-no-files-found: error
45
+
46
+ native-platforms:
47
+ needs: quality
48
+ strategy:
49
+ matrix:
50
+ os: [windows-latest, macos-latest]
51
+ runs-on: ${{ matrix.os }}
52
+ steps:
53
+ - uses: actions/checkout@v7
54
+ - uses: dtolnay/rust-toolchain@stable
55
+ - uses: actions/setup-python@v7
56
+ with:
57
+ python-version: '3.14'
58
+ - run: python -m pip install maturin "pytest>=9.1.1,<10" "openpyxl==3.1.5" "Pillow>=12.3" "lxml>=6.1.3"
59
+ - run: maturin build --release --locked --out dist
60
+ - shell: bash
61
+ run: python -m pip install dist/*.whl
62
+ - run: python -m pytest tests -q
@@ -0,0 +1,181 @@
1
+ name: Release Python
2
+
3
+ # Push a version tag, for example 0.1.0, to release that commit.
4
+ on:
5
+ push:
6
+ tags:
7
+ - '[0-9]+.[0-9]+.[0-9]+'
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ concurrency:
13
+ group: python-release
14
+ cancel-in-progress: false
15
+
16
+ jobs:
17
+ validate:
18
+ runs-on: ubuntu-latest
19
+ outputs:
20
+ version: ${{ steps.request.outputs.version }}
21
+ notes: ${{ steps.request.outputs.notes }}
22
+ steps:
23
+ - uses: actions/checkout@v7
24
+ with:
25
+ fetch-depth: 0
26
+ - uses: actions/setup-python@v7
27
+ with:
28
+ python-version: '3.14'
29
+ - run: python -m pip install 'ruff>=0.16.10,<0.17'
30
+ - run: ruff format --check .
31
+ - run: ruff check .
32
+ - name: Validate the tag, the committed version and the changelog
33
+ id: request
34
+ env:
35
+ DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
36
+ run: python tools/release_tag.py
37
+
38
+ package:
39
+ needs: validate
40
+ runs-on: ${{ matrix.platform.runner }}
41
+ strategy:
42
+ fail-fast: false
43
+ matrix:
44
+ platform:
45
+ - name: linux-x86_64
46
+ runner: ubuntu-24.04
47
+ target: x86_64-unknown-linux-gnu
48
+ - name: linux-aarch64
49
+ runner: ubuntu-24.04-arm
50
+ target: aarch64-unknown-linux-gnu
51
+ - name: windows-x86_64
52
+ runner: windows-latest
53
+ target: x86_64-pc-windows-msvc
54
+ - name: macos-x86_64
55
+ runner: macos-15-intel
56
+ target: x86_64-apple-darwin
57
+ - name: macos-arm64
58
+ runner: macos-15
59
+ target: aarch64-apple-darwin
60
+ steps:
61
+ - uses: actions/checkout@v7
62
+ - uses: ./.github/actions/setup-supported-python
63
+ id: pythons
64
+ - uses: dtolnay/rust-toolchain@stable
65
+ with:
66
+ components: rustfmt, clippy
67
+ - run: cargo fmt -- --check
68
+ - run: cargo clippy --all-targets --locked -- -D warnings
69
+ - name: Build portable Linux wheels
70
+ if: runner.os == 'Linux'
71
+ uses: PyO3/maturin-action@v1
72
+ with:
73
+ maturin-version: 'v1.15.0'
74
+ rust-toolchain: 'stable'
75
+ target: ${{ matrix.platform.target }}
76
+ manylinux: '2_28'
77
+ args: >-
78
+ --release --locked --out dist --interpreter
79
+ /opt/python/cp311-cp311/bin/python
80
+ /opt/python/cp312-cp312/bin/python
81
+ /opt/python/cp313-cp313/bin/python
82
+ /opt/python/cp314-cp314/bin/python
83
+ /opt/python/cp315-cp315/bin/python
84
+ - name: Install the native package builder
85
+ if: runner.os != 'Linux'
86
+ run: python -m pip install 'maturin>=1.15,<2'
87
+ - name: Build Windows and macOS wheels
88
+ if: runner.os != 'Linux'
89
+ env:
90
+ PYTHON_PATHS: ${{ toJSON(steps.pythons.outputs) }}
91
+ run: python tools/platform_wheels.py build --target ${{ matrix.platform.target }}
92
+ - name: Test all five installed CPython wheels
93
+ env:
94
+ PYTHON_PATHS: ${{ toJSON(steps.pythons.outputs) }}
95
+ run: python tools/platform_wheels.py test
96
+ - uses: actions/upload-artifact@v7
97
+ with:
98
+ name: release-wheel-${{ matrix.platform.name }}
99
+ path: dist/*.whl
100
+ if-no-files-found: error
101
+
102
+ source:
103
+ needs: validate
104
+ runs-on: ubuntu-latest
105
+ steps:
106
+ - uses: actions/checkout@v7
107
+ - uses: actions/setup-python@v7
108
+ with:
109
+ python-version: '3.14'
110
+ - uses: dtolnay/rust-toolchain@stable
111
+ - run: python -m pip install 'maturin>=1.15,<2'
112
+ - run: maturin sdist --out dist
113
+ - name: Build and test from the source distribution
114
+ run: python tools/test_distribution.py '*.tar.gz'
115
+ - uses: actions/upload-artifact@v7
116
+ with:
117
+ name: release-source
118
+ path: dist/*.tar.gz
119
+ if-no-files-found: error
120
+
121
+ publish:
122
+ needs: [validate, package, source]
123
+ runs-on: ubuntu-latest
124
+ environment: pypi
125
+ permissions:
126
+ id-token: write
127
+ contents: read
128
+ steps:
129
+ - uses: actions/checkout@v7
130
+ with:
131
+ fetch-depth: 0
132
+ - uses: actions/setup-python@v7
133
+ with:
134
+ python-version: '3.14'
135
+ - uses: actions/download-artifact@v8
136
+ with:
137
+ pattern: release-*
138
+ path: dist
139
+ merge-multiple: true
140
+ - name: Verify artifact identities and safe retries
141
+ env:
142
+ RELEASE_VERSION: ${{ needs.validate.outputs.version }}
143
+ run: python tools/check_release_files.py "$RELEASE_VERSION"
144
+ - name: Publish to PyPI using OIDC
145
+ uses: pypa/gh-action-pypi-publish@release/v1
146
+ with:
147
+ packages-dir: dist/
148
+ skip-existing: true
149
+
150
+ release:
151
+ needs: [validate, publish]
152
+ runs-on: ubuntu-latest
153
+ permissions:
154
+ contents: write
155
+ steps:
156
+ - uses: actions/checkout@v7
157
+ with:
158
+ fetch-depth: 0
159
+ - uses: actions/setup-python@v7
160
+ with:
161
+ python-version: '3.14'
162
+ - uses: actions/download-artifact@v8
163
+ with:
164
+ pattern: release-*
165
+ path: dist
166
+ merge-multiple: true
167
+ - name: Recheck the version and create the GitHub release
168
+ env:
169
+ GH_TOKEN: ${{ github.token }}
170
+ GH_REPO: ${{ github.repository }}
171
+ RELEASE_VERSION: ${{ needs.validate.outputs.version }}
172
+ RELEASE_NOTES: ${{ needs.validate.outputs.notes }}
173
+ run: |
174
+ python tools/release.py check "$RELEASE_VERSION"
175
+ printf '%s\n' "$RELEASE_NOTES" > "$RUNNER_TEMP/release-notes.md"
176
+ if gh release view "$RELEASE_VERSION" > /dev/null 2>&1; then
177
+ gh release upload "$RELEASE_VERSION" dist/* --clobber
178
+ else
179
+ gh release create "$RELEASE_VERSION" dist/* --verify-tag \
180
+ --title "$RELEASE_VERSION" --notes-file "$RUNNER_TEMP/release-notes.md"
181
+ fi
@@ -0,0 +1,10 @@
1
+ /target/
2
+ /dist/
3
+ /.venv/
4
+ __pycache__/
5
+ .pytest_cache/
6
+ *.egg-info/
7
+
8
+ # Native extension built by maturin develop or pip install -e
9
+ python/crabxl/*.so
10
+ python/crabxl/*.pyd
@@ -0,0 +1,47 @@
1
+ # Changelog
2
+
3
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
4
+ Before version 1.0, a minor version can change the API.
5
+
6
+ Each release uses a pinned revision of the Rust core. The core has its own
7
+ [changelog](https://github.com/crabxl/crabxl/blob/main/CHANGELOG.md).
8
+
9
+ ## [0.1.0]
10
+
11
+ First release. It uses the Rust core 0.1.0.
12
+
13
+ ### Added
14
+
15
+ - `Workbook`, `load_workbook`, `Worksheet` and `Cell` with openpyxl call names,
16
+ in normal, `read_only` and `write_only` modes. Paths and file-like objects.
17
+ - Values, dates, formulas with cached results and rich text.
18
+ - Cell styles (`Font`, `PatternFill`, `GradientFill`, `Border`, `Alignment`,
19
+ `Protection`, number formats), named styles, themes, row and column styles,
20
+ `cell.quotePrefix` and `cell.pivotButton`.
21
+ - Merged cells, row and column dimensions, outline groups and hyperlinks,
22
+ including in `write_only` mode.
23
+ - Sheet create, copy, remove, rename and move; `append`, `insert_rows`,
24
+ `delete_rows`, `insert_cols`, `delete_cols` and `move_range`.
25
+ - Tables, auto filters, conditional formatting, data validation, comments,
26
+ sheet protection, headers and footers, print areas and titles, defined
27
+ names, scenarios, workbook protection and document properties.
28
+ - Images, charts, chartsheets, drawings, pivot tables, external links and
29
+ `wb.vba_archive`.
30
+ - `ws.sheet_properties` and `ws.sheet_format` in all modes.
31
+ - The openpyxl import paths, for example `crabxl.worksheet.table`,
32
+ `crabxl.formatting.rule` and `crabxl.comments`, and the deprecated openpyxl
33
+ 3.1 calls such as `get_sheet_by_name`, which emit a `DeprecationWarning`.
34
+ - `max_memory_bytes`, `ResourceOptions`, `ResourceLimits`,
35
+ `SharedStringOptions` and `AutoMemory` to control memory, temporary storage
36
+ and shared strings.
37
+ - `save(path, compression_level=...)`. Level 0 stores without compression;
38
+ levels 1–9 use Deflate; the default is 6.
39
+ - Rows of plain values (`None`, `bool`, `int`, `float`, `str` and dates and
40
+ times) are converted in Rust when appended.
41
+ - `tools/run_openpyxl_suite.py` runs the unchanged openpyxl 3.1.5 test suite
42
+ against crabxl and checks the result against a tracked list of expected
43
+ failures. `--report` writes the compatibility page of the documentation.
44
+ - The user guide, adapted from the openpyxl documentation, and pages for
45
+ memory limits, migration, supported features and compatibility.
46
+
47
+ [0.1.0]: https://github.com/crabxl/crabxl-python/releases/tag/0.1.0
@@ -0,0 +1,146 @@
1
+ # Contributing to CrabXL for Python
2
+
3
+ ## Ways to contribute
4
+
5
+ - **Report a bug.** Use the [issue forms](https://github.com/crabxl/crabxl-python/issues/new/choose).
6
+ Give the CrabXL version, the Python version and the smallest code or `.xlsx`
7
+ file that shows the problem. Remove confidential data first.
8
+ - **Report a difference from openpyxl.** CrabXL must behave like openpyxl
9
+ 3.1.5. Give the code, the openpyxl result and the CrabXL result.
10
+ - **Improve the documentation.** User documentation is in [docs](docs). The
11
+ documentation site is built from it by
12
+ [crabxl-docs](https://github.com/crabxl/crabxl-docs).
13
+ - **Contribute code.** For a change that is larger than a small fix, open an
14
+ issue first. Agree on the design before you start the work.
15
+
16
+ Do not report security problems in public issues. Read [SECURITY.md](SECURITY.md).
17
+
18
+ ## How this package works
19
+
20
+ This package does not implement spreadsheet features itself. The Rust core in
21
+ [crabxl](https://github.com/crabxl/crabxl) implements them. This package adapts
22
+ the core to the openpyxl API.
23
+
24
+ - If a feature is missing in the core, add it to the core first.
25
+ - `Cargo.toml` pins the core to an exact Git revision. A change that needs a
26
+ new core feature also updates this revision.
27
+ - An unsupported feature raises an explicit error, usually
28
+ `NotImplementedError`. The package never falls back to openpyxl.
29
+
30
+ ## Repository layout
31
+
32
+ | Path | Contents |
33
+ |---|---|
34
+ | `python/crabxl` | Public Python package. Its modules follow the openpyxl module layout. |
35
+ | `src/` | PyO3 bindings to the Rust core. |
36
+ | `tests/` | Tests. Files named `test_upstream_*.py` contain selected openpyxl tests. |
37
+ | `third_party/` | Provenance and licenses of the selected openpyxl tests. |
38
+ | `docs/` | Public user documentation. |
39
+ | `internal/` | Maintainer records: [status](internal/STATUS.md), roadmap, architecture, design decisions and release evidence. |
40
+ | `tools/` | Release, wheel and provenance scripts. |
41
+
42
+ ## Development setup
43
+
44
+ You need:
45
+
46
+ - Python 3.11 or later.
47
+ - Rust 1.99 or later, installed with [rustup](https://rustup.rs/). The
48
+ repository uses the latest stable toolchain. If the build reports that it
49
+ requires a newer `rustc`, run `rustup update stable`.
50
+
51
+ ```sh
52
+ git clone https://github.com/crabxl/crabxl-python.git
53
+ cd crabxl-python
54
+ python -m venv .venv
55
+ source .venv/bin/activate
56
+ python -m pip install "maturin>=1.15,<2"
57
+ python -m pip install -e ".[test,dev]"
58
+ ```
59
+
60
+ The editable install builds the native extension. After you change Rust code in
61
+ `src/`, build it again:
62
+
63
+ ```sh
64
+ maturin develop --release
65
+ ```
66
+
67
+ ## Checks
68
+
69
+ CI runs these checks. Run them before you open a pull request:
70
+
71
+ ```sh
72
+ ruff format --check .
73
+ ruff check .
74
+ cargo fmt -- --check
75
+ cargo clippy --all-targets --locked -- -D warnings
76
+ python -m pytest tests -q
77
+ ```
78
+
79
+ CI also builds and tests wheels for all supported Python versions on Linux,
80
+ Windows and macOS.
81
+
82
+ ## Code rules
83
+
84
+ - **Match openpyxl.** Use the same call names, arguments, object access and
85
+ observable behavior as openpyxl 3.1.5. Extensions are permitted, but keep
86
+ them separate from compatible calls.
87
+ - **Fail explicitly.** If a feature is not supported, raise an error. Do not
88
+ accept a property that is lost on save.
89
+ - **Keep work in Rust.** Convert values in batches. Release the GIL during
90
+ native work.
91
+ - **Write in English** in code, comments, documentation and commit messages.
92
+
93
+ ### Tests
94
+
95
+ - Add tests that compare the CrabXL result with the openpyxl result for the
96
+ same public calls.
97
+ - Do not change the assertions of the selected openpyxl tests. Their bodies are
98
+ recorded in `third_party/`. To verify them, run
99
+ `python tools/verify_python_test_provenance.py --reference-checkout <path>`
100
+ with a checkout of openpyxl 3.1.5.
101
+ - Mark expected failures explicitly. Do not count them as passes.
102
+ - Do not put timing limits in tests. Measure performance with [crabxl-benchmarks](https://github.com/crabxl/crabxl-benchmarks).
103
+
104
+ ### Performance changes
105
+
106
+ If you claim a speed or memory improvement, include the measurements. Compare
107
+ the same mode and the same behavior in CrabXL and openpyxl. Give the time, peak
108
+ memory and the commands that you used.
109
+
110
+ ### Code from other projects
111
+
112
+ You can copy code from another project, including openpyxl, if its license is
113
+ compatible with MIT. Keep the original copyright notice and add the project to
114
+ [third_party/](third_party).
115
+
116
+ ### Design decisions
117
+
118
+ Explain large design changes in the pull request.
119
+
120
+ ## Documentation style
121
+
122
+ Follow the [writing rules](https://github.com/crabxl/crabxl-docs/blob/main/WRITING.md)
123
+ and the [page format](https://github.com/crabxl/crabxl-docs/blob/main/CONTENT-SPEC.md).
124
+ CI checks them with Vale.
125
+
126
+ ## Commits and pull requests
127
+
128
+ - Use [Conventional Commits](https://www.conventionalcommits.org/):
129
+ `<type>(<scope>): <description>`. For example:
130
+ `fix(worksheet): keep merged cells after a row insertion`. Common types are
131
+ `feat`, `fix`, `docs`, `refactor`, `perf`, `test`, `build`, `ci` and `chore`.
132
+ - Put one coherent change in each commit. Related code, tests and documentation
133
+ can be in the same commit. Put unrelated changes in separate commits.
134
+ - Describe the result, not the work process.
135
+ - Complete the pull request template. Link the related issue. Make sure that CI
136
+ passes.
137
+
138
+ ## Releases
139
+
140
+ Maintainers make releases with the release workflow. You do not need to change
141
+ version numbers. [internal/releases.md](internal/releases.md) describes the
142
+ process.
143
+
144
+ ## License
145
+
146
+ You agree that your contributions are licensed under the [MIT License](LICENSE).