xlwings-server 1.1.0__py3-none-any.whl

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 (313) hide show
  1. xlwings_server/.env.template +145 -0
  2. xlwings_server/__init__.py +12 -0
  3. xlwings_server/_version.py +34 -0
  4. xlwings_server/auth/__init__.py +0 -0
  5. xlwings_server/auth/custom/__init__.py +26 -0
  6. xlwings_server/auth/entraid/__init__.py +131 -0
  7. xlwings_server/auth/entraid/jwks.py +10 -0
  8. xlwings_server/azure_functions_templates/.funcignore +28 -0
  9. xlwings_server/azure_functions_templates/function_app.py +28 -0
  10. xlwings_server/azure_functions_templates/host.json +22 -0
  11. xlwings_server/azure_functions_templates/local.settings.json +8 -0
  12. xlwings_server/build_utils/__init__.py +9 -0
  13. xlwings_server/build_utils/static_file_hasher.py +212 -0
  14. xlwings_server/cli.py +1592 -0
  15. xlwings_server/config.py +228 -0
  16. xlwings_server/custom_functions/__init__.py +8 -0
  17. xlwings_server/custom_functions/examples.py +177 -0
  18. xlwings_server/custom_scripts/__init__.py +8 -0
  19. xlwings_server/custom_scripts/examples.py +94 -0
  20. xlwings_server/databases.py +19 -0
  21. xlwings_server/dependencies.py +126 -0
  22. xlwings_server/docker_templates/.dockerignore +15 -0
  23. xlwings_server/docker_templates/Dockerfile +60 -0
  24. xlwings_server/docker_templates/docker-compose.yaml +32 -0
  25. xlwings_server/hotreload.py +59 -0
  26. xlwings_server/main.py +242 -0
  27. xlwings_server/models/__init__.py +14 -0
  28. xlwings_server/models/user.py +53 -0
  29. xlwings_server/object_handles.py +142 -0
  30. xlwings_server/routers/__init__.py +0 -0
  31. xlwings_server/routers/manifest.py +82 -0
  32. xlwings_server/routers/root.py +16 -0
  33. xlwings_server/routers/socketio.py +69 -0
  34. xlwings_server/routers/taskpane.py +12 -0
  35. xlwings_server/routers/xlwings.py +197 -0
  36. xlwings_server/security_headers.json +53 -0
  37. xlwings_server/serializers/__init__.py +25 -0
  38. xlwings_server/serializers/default_serializer.py +19 -0
  39. xlwings_server/serializers/dictionary_serializer.py +25 -0
  40. xlwings_server/serializers/framework.py +50 -0
  41. xlwings_server/serializers/numpy_serializer.py +26 -0
  42. xlwings_server/serializers/pandas_serializer.py +95 -0
  43. xlwings_server/static/css/core.css +28 -0
  44. xlwings_server/static/css/style.css +0 -0
  45. xlwings_server/static/images/favicon.png +0 -0
  46. xlwings_server/static/images/xlwings-16.png +0 -0
  47. xlwings_server/static/images/xlwings-32.png +0 -0
  48. xlwings_server/static/images/xlwings-64.png +0 -0
  49. xlwings_server/static/images/xlwings-80.png +0 -0
  50. xlwings_server/static/js/auth.js +13 -0
  51. xlwings_server/static/js/config.js +4 -0
  52. xlwings_server/static/js/core/alpinejs-csp-boilerplate.js +11 -0
  53. xlwings_server/static/js/core/bootstrap-customizations.js +7 -0
  54. xlwings_server/static/js/core/custom-functions-code.js +296 -0
  55. xlwings_server/static/js/core/examples.js +62 -0
  56. xlwings_server/static/js/core/hotreload.js +3 -0
  57. xlwings_server/static/js/core/htmx-handlers.js +86 -0
  58. xlwings_server/static/js/core/officejs-history-fix-part1.js +3 -0
  59. xlwings_server/static/js/core/officejs-history-fix-part2.js +2 -0
  60. xlwings_server/static/js/core/reload-custom-functions.js +79 -0
  61. xlwings_server/static/js/core/socketio-handlers.js +34 -0
  62. xlwings_server/static/js/core/xlwings-alert.js +22 -0
  63. xlwings_server/static/js/core/xlwingsjs/alert.js +85 -0
  64. xlwings_server/static/js/core/xlwingsjs/auth.js +63 -0
  65. xlwings_server/static/js/core/xlwingsjs/sheet-buttons.js +133 -0
  66. xlwings_server/static/js/core/xlwingsjs/utils.js +119 -0
  67. xlwings_server/static/js/core/xlwingsjs/wasm.js +131 -0
  68. xlwings_server/static/js/core/xlwingsjs/xlwings.js +1060 -0
  69. xlwings_server/static/js/main.js +0 -0
  70. xlwings_server/static/js/ribbon.js +17 -0
  71. xlwings_server/static/vendor/@alpinejs/LICENSE +21 -0
  72. xlwings_server/static/vendor/@alpinejs/csp/dist/cdn.min.js +7 -0
  73. xlwings_server/static/vendor/@microsoft/office-js/LICENSE.md +76 -0
  74. xlwings_server/static/vendor/@microsoft/office-js/dist/af-za/office_strings.js +8 -0
  75. xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/agaveerrorux.js +18 -0
  76. xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/agavedefaulticon32x32.png +0 -0
  77. xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/agavedefaulticon96x96.png +0 -0
  78. xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/businessbarclose_16x16x32.png +0 -0
  79. xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/dropdownarrow_16x16x32.png +0 -0
  80. xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/ellipsis_16x16x32.png +0 -0
  81. xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/miniinfoblue_16x16x32.png +0 -0
  82. xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/moe_default_icon.png +0 -0
  83. xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/moe_status_icons.png +0 -0
  84. xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/office.png +0 -0
  85. xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/refresh_16x16x32.png +0 -0
  86. xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/index.html +16 -0
  87. xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/style/agaveerrorux.css +482 -0
  88. xlwings_server/static/vendor/@microsoft/office-js/dist/am-et/office_strings.js +1 -0
  89. xlwings_server/static/vendor/@microsoft/office-js/dist/ar-ae/office_strings.js +8 -0
  90. xlwings_server/static/vendor/@microsoft/office-js/dist/ar-bh/office_strings.js +8 -0
  91. xlwings_server/static/vendor/@microsoft/office-js/dist/ar-dz/office_strings.js +8 -0
  92. xlwings_server/static/vendor/@microsoft/office-js/dist/ar-eg/office_strings.js +8 -0
  93. xlwings_server/static/vendor/@microsoft/office-js/dist/ar-iq/office_strings.js +8 -0
  94. xlwings_server/static/vendor/@microsoft/office-js/dist/ar-jo/office_strings.js +8 -0
  95. xlwings_server/static/vendor/@microsoft/office-js/dist/ar-kw/office_strings.js +8 -0
  96. xlwings_server/static/vendor/@microsoft/office-js/dist/ar-lb/office_strings.js +8 -0
  97. xlwings_server/static/vendor/@microsoft/office-js/dist/ar-ly/office_strings.js +8 -0
  98. xlwings_server/static/vendor/@microsoft/office-js/dist/ar-ma/office_strings.js +8 -0
  99. xlwings_server/static/vendor/@microsoft/office-js/dist/ar-om/office_strings.js +8 -0
  100. xlwings_server/static/vendor/@microsoft/office-js/dist/ar-qa/office_strings.js +8 -0
  101. xlwings_server/static/vendor/@microsoft/office-js/dist/ar-sa/office_strings.js +1 -0
  102. xlwings_server/static/vendor/@microsoft/office-js/dist/ar-sy/office_strings.js +8 -0
  103. xlwings_server/static/vendor/@microsoft/office-js/dist/ar-tn/office_strings.js +8 -0
  104. xlwings_server/static/vendor/@microsoft/office-js/dist/ar-ye/office_strings.js +8 -0
  105. xlwings_server/static/vendor/@microsoft/office-js/dist/ariatelemetry/aria-web-telemetry-2.8.0.min.js +2 -0
  106. xlwings_server/static/vendor/@microsoft/office-js/dist/ariatelemetry/aria-web-telemetry-2.9.0.min.js +2 -0
  107. xlwings_server/static/vendor/@microsoft/office-js/dist/ariatelemetry/aria-web-telemetry.js +1 -0
  108. xlwings_server/static/vendor/@microsoft/office-js/dist/az-latn-az/office_strings.js +8 -0
  109. xlwings_server/static/vendor/@microsoft/office-js/dist/be-by/office_strings.js +8 -0
  110. xlwings_server/static/vendor/@microsoft/office-js/dist/bg-bg/office_strings.js +1 -0
  111. xlwings_server/static/vendor/@microsoft/office-js/dist/bn-in/office_strings.js +1 -0
  112. xlwings_server/static/vendor/@microsoft/office-js/dist/bs-latn-ba/office_strings.js +8 -0
  113. xlwings_server/static/vendor/@microsoft/office-js/dist/ca-es/office_strings.js +1 -0
  114. xlwings_server/static/vendor/@microsoft/office-js/dist/cs-cz/office_strings.js +1 -0
  115. xlwings_server/static/vendor/@microsoft/office-js/dist/cy-gb/office_strings.js +1 -0
  116. xlwings_server/static/vendor/@microsoft/office-js/dist/da-dk/office_strings.js +1 -0
  117. xlwings_server/static/vendor/@microsoft/office-js/dist/de-at/office_strings.js +8 -0
  118. xlwings_server/static/vendor/@microsoft/office-js/dist/de-ch/office_strings.js +8 -0
  119. xlwings_server/static/vendor/@microsoft/office-js/dist/de-de/office_strings.js +1 -0
  120. xlwings_server/static/vendor/@microsoft/office-js/dist/de-li/office_strings.js +8 -0
  121. xlwings_server/static/vendor/@microsoft/office-js/dist/de-lu/office_strings.js +8 -0
  122. xlwings_server/static/vendor/@microsoft/office-js/dist/el-gr/office_strings.js +1 -0
  123. xlwings_server/static/vendor/@microsoft/office-js/dist/en-029/office_strings.js +8 -0
  124. xlwings_server/static/vendor/@microsoft/office-js/dist/en-au/office_strings.js +8 -0
  125. xlwings_server/static/vendor/@microsoft/office-js/dist/en-bz/office_strings.js +8 -0
  126. xlwings_server/static/vendor/@microsoft/office-js/dist/en-ca/office_strings.js +8 -0
  127. xlwings_server/static/vendor/@microsoft/office-js/dist/en-gb/office_strings.js +8 -0
  128. xlwings_server/static/vendor/@microsoft/office-js/dist/en-ie/office_strings.js +8 -0
  129. xlwings_server/static/vendor/@microsoft/office-js/dist/en-in/office_strings.js +8 -0
  130. xlwings_server/static/vendor/@microsoft/office-js/dist/en-jm/office_strings.js +8 -0
  131. xlwings_server/static/vendor/@microsoft/office-js/dist/en-my/office_strings.js +8 -0
  132. xlwings_server/static/vendor/@microsoft/office-js/dist/en-nz/office_strings.js +8 -0
  133. xlwings_server/static/vendor/@microsoft/office-js/dist/en-ph/office_strings.js +8 -0
  134. xlwings_server/static/vendor/@microsoft/office-js/dist/en-sg/office_strings.js +8 -0
  135. xlwings_server/static/vendor/@microsoft/office-js/dist/en-tt/office_strings.js +8 -0
  136. xlwings_server/static/vendor/@microsoft/office-js/dist/en-us/office_strings.js +8 -0
  137. xlwings_server/static/vendor/@microsoft/office-js/dist/en-za/office_strings.js +8 -0
  138. xlwings_server/static/vendor/@microsoft/office-js/dist/en-zw/office_strings.js +8 -0
  139. xlwings_server/static/vendor/@microsoft/office-js/dist/es-ar/office_strings.js +8 -0
  140. xlwings_server/static/vendor/@microsoft/office-js/dist/es-bo/office_strings.js +8 -0
  141. xlwings_server/static/vendor/@microsoft/office-js/dist/es-cl/office_strings.js +8 -0
  142. xlwings_server/static/vendor/@microsoft/office-js/dist/es-co/office_strings.js +8 -0
  143. xlwings_server/static/vendor/@microsoft/office-js/dist/es-cr/office_strings.js +8 -0
  144. xlwings_server/static/vendor/@microsoft/office-js/dist/es-do/office_strings.js +8 -0
  145. xlwings_server/static/vendor/@microsoft/office-js/dist/es-ec/office_strings.js +8 -0
  146. xlwings_server/static/vendor/@microsoft/office-js/dist/es-es/office_strings.js +1 -0
  147. xlwings_server/static/vendor/@microsoft/office-js/dist/es-gt/office_strings.js +8 -0
  148. xlwings_server/static/vendor/@microsoft/office-js/dist/es-hn/office_strings.js +8 -0
  149. xlwings_server/static/vendor/@microsoft/office-js/dist/es-mx/office_strings.js +1 -0
  150. xlwings_server/static/vendor/@microsoft/office-js/dist/es-ni/office_strings.js +8 -0
  151. xlwings_server/static/vendor/@microsoft/office-js/dist/es-pa/office_strings.js +8 -0
  152. xlwings_server/static/vendor/@microsoft/office-js/dist/es-pe/office_strings.js +8 -0
  153. xlwings_server/static/vendor/@microsoft/office-js/dist/es-pr/office_strings.js +8 -0
  154. xlwings_server/static/vendor/@microsoft/office-js/dist/es-py/office_strings.js +8 -0
  155. xlwings_server/static/vendor/@microsoft/office-js/dist/es-sv/office_strings.js +8 -0
  156. xlwings_server/static/vendor/@microsoft/office-js/dist/es-us/office_strings.js +8 -0
  157. xlwings_server/static/vendor/@microsoft/office-js/dist/es-uy/office_strings.js +8 -0
  158. xlwings_server/static/vendor/@microsoft/office-js/dist/es-ve/office_strings.js +8 -0
  159. xlwings_server/static/vendor/@microsoft/office-js/dist/es6-promise.js +5 -0
  160. xlwings_server/static/vendor/@microsoft/office-js/dist/et-ee/office_strings.js +1 -0
  161. xlwings_server/static/vendor/@microsoft/office-js/dist/eu-es/office_strings.js +1 -0
  162. xlwings_server/static/vendor/@microsoft/office-js/dist/excel-15.01.js +11 -0
  163. xlwings_server/static/vendor/@microsoft/office-js/dist/excel-15.02.js +11 -0
  164. xlwings_server/static/vendor/@microsoft/office-js/dist/excel-15.js +11 -0
  165. xlwings_server/static/vendor/@microsoft/office-js/dist/excel-mac-16.00-core.js +11 -0
  166. xlwings_server/static/vendor/@microsoft/office-js/dist/excel-mac-16.00.js +25 -0
  167. xlwings_server/static/vendor/@microsoft/office-js/dist/excel-web-16.00-core.js +11 -0
  168. xlwings_server/static/vendor/@microsoft/office-js/dist/excel-web-16.00.js +25 -0
  169. xlwings_server/static/vendor/@microsoft/office-js/dist/excel-win32-16.00.js +19 -0
  170. xlwings_server/static/vendor/@microsoft/office-js/dist/excel-win32-16.01-core.js +11 -0
  171. xlwings_server/static/vendor/@microsoft/office-js/dist/excel-win32-16.01.js +25 -0
  172. xlwings_server/static/vendor/@microsoft/office-js/dist/excel-winrt-16.00.js +25 -0
  173. xlwings_server/static/vendor/@microsoft/office-js/dist/excelios-15.js +11 -0
  174. xlwings_server/static/vendor/@microsoft/office-js/dist/excelwebapp-15.01.js +11 -0
  175. xlwings_server/static/vendor/@microsoft/office-js/dist/excelwebapp-15.02.js +11 -0
  176. xlwings_server/static/vendor/@microsoft/office-js/dist/excelwebapp-15.js +11 -0
  177. xlwings_server/static/vendor/@microsoft/office-js/dist/fa-ir/office_strings.js +1 -0
  178. xlwings_server/static/vendor/@microsoft/office-js/dist/fi-fi/office_strings.js +1 -0
  179. xlwings_server/static/vendor/@microsoft/office-js/dist/fil-ph/office_strings.js +1 -0
  180. xlwings_server/static/vendor/@microsoft/office-js/dist/fr-be/office_strings.js +8 -0
  181. xlwings_server/static/vendor/@microsoft/office-js/dist/fr-ca/office_strings.js +1 -0
  182. xlwings_server/static/vendor/@microsoft/office-js/dist/fr-ch/office_strings.js +8 -0
  183. xlwings_server/static/vendor/@microsoft/office-js/dist/fr-fr/office_strings.js +1 -0
  184. xlwings_server/static/vendor/@microsoft/office-js/dist/fr-lu/office_strings.js +8 -0
  185. xlwings_server/static/vendor/@microsoft/office-js/dist/fr-mc/office_strings.js +8 -0
  186. xlwings_server/static/vendor/@microsoft/office-js/dist/ga-ie/office_strings.js +8 -0
  187. xlwings_server/static/vendor/@microsoft/office-js/dist/gl-es/office_strings.js +1 -0
  188. xlwings_server/static/vendor/@microsoft/office-js/dist/gu-in/office_strings.js +1 -0
  189. xlwings_server/static/vendor/@microsoft/office-js/dist/he-il/office_strings.js +1 -0
  190. xlwings_server/static/vendor/@microsoft/office-js/dist/hi-in/office_strings.js +1 -0
  191. xlwings_server/static/vendor/@microsoft/office-js/dist/hr-ba/office_strings.js +8 -0
  192. xlwings_server/static/vendor/@microsoft/office-js/dist/hr-hr/office_strings.js +1 -0
  193. xlwings_server/static/vendor/@microsoft/office-js/dist/html2canvas.js +8 -0
  194. xlwings_server/static/vendor/@microsoft/office-js/dist/hu-hu/office_strings.js +1 -0
  195. xlwings_server/static/vendor/@microsoft/office-js/dist/hy-am/office_strings.js +8 -0
  196. xlwings_server/static/vendor/@microsoft/office-js/dist/id-id/office_strings.js +1 -0
  197. xlwings_server/static/vendor/@microsoft/office-js/dist/is-is/office_strings.js +1 -0
  198. xlwings_server/static/vendor/@microsoft/office-js/dist/it-ch/office_strings.js +8 -0
  199. xlwings_server/static/vendor/@microsoft/office-js/dist/it-it/office_strings.js +1 -0
  200. xlwings_server/static/vendor/@microsoft/office-js/dist/ja-jp/office_strings.js +1 -0
  201. xlwings_server/static/vendor/@microsoft/office-js/dist/ka-ge/office_strings.js +8 -0
  202. xlwings_server/static/vendor/@microsoft/office-js/dist/kk-kz/office_strings.js +1 -0
  203. xlwings_server/static/vendor/@microsoft/office-js/dist/km-kh/office_strings.js +8 -0
  204. xlwings_server/static/vendor/@microsoft/office-js/dist/kn-in/office_strings.js +1 -0
  205. xlwings_server/static/vendor/@microsoft/office-js/dist/ko-kr/office_strings.js +1 -0
  206. xlwings_server/static/vendor/@microsoft/office-js/dist/lb-lu/office_strings.js +8 -0
  207. xlwings_server/static/vendor/@microsoft/office-js/dist/lo-la/office_strings.js +1 -0
  208. xlwings_server/static/vendor/@microsoft/office-js/dist/lt-lt/office_strings.js +1 -0
  209. xlwings_server/static/vendor/@microsoft/office-js/dist/lv-lv/office_strings.js +1 -0
  210. xlwings_server/static/vendor/@microsoft/office-js/dist/mk-mk/office_strings.js +8 -0
  211. xlwings_server/static/vendor/@microsoft/office-js/dist/ml-in/office_strings.js +1 -0
  212. xlwings_server/static/vendor/@microsoft/office-js/dist/mn-mn/office_strings.js +8 -0
  213. xlwings_server/static/vendor/@microsoft/office-js/dist/mr-in/office_strings.js +1 -0
  214. xlwings_server/static/vendor/@microsoft/office-js/dist/ms-bn/office_strings.js +8 -0
  215. xlwings_server/static/vendor/@microsoft/office-js/dist/ms-my/office_strings.js +1 -0
  216. xlwings_server/static/vendor/@microsoft/office-js/dist/mt-mt/office_strings.js +8 -0
  217. xlwings_server/static/vendor/@microsoft/office-js/dist/nb-no/office_strings.js +1 -0
  218. xlwings_server/static/vendor/@microsoft/office-js/dist/ne-np/office_strings.js +8 -0
  219. xlwings_server/static/vendor/@microsoft/office-js/dist/nl-be/office_strings.js +8 -0
  220. xlwings_server/static/vendor/@microsoft/office-js/dist/nl-nl/office_strings.js +1 -0
  221. xlwings_server/static/vendor/@microsoft/office-js/dist/nn-no/office_strings.js +1 -0
  222. xlwings_server/static/vendor/@microsoft/office-js/dist/o15apptofilemappingtable.js +11 -0
  223. xlwings_server/static/vendor/@microsoft/office-js/dist/office-vsdoc.js +28596 -0
  224. xlwings_server/static/vendor/@microsoft/office-js/dist/office.js +84 -0
  225. xlwings_server/static/vendor/@microsoft/office-js/dist/pl-pl/office_strings.js +1 -0
  226. xlwings_server/static/vendor/@microsoft/office-js/dist/pt-br/office_strings.js +1 -0
  227. xlwings_server/static/vendor/@microsoft/office-js/dist/pt-pt/office_strings.js +1 -0
  228. xlwings_server/static/vendor/@microsoft/office-js/dist/ro-ro/office_strings.js +1 -0
  229. xlwings_server/static/vendor/@microsoft/office-js/dist/ru-ru/office_strings.js +1 -0
  230. xlwings_server/static/vendor/@microsoft/office-js/dist/si-lk/office_strings.js +8 -0
  231. xlwings_server/static/vendor/@microsoft/office-js/dist/sk-sk/office_strings.js +1 -0
  232. xlwings_server/static/vendor/@microsoft/office-js/dist/sl-si/office_strings.js +1 -0
  233. xlwings_server/static/vendor/@microsoft/office-js/dist/sq-al/office_strings.js +8 -0
  234. xlwings_server/static/vendor/@microsoft/office-js/dist/sr-cyrl-cs/office_strings.js +1 -0
  235. xlwings_server/static/vendor/@microsoft/office-js/dist/sr-cyrl-rs/office_strings.js +1 -0
  236. xlwings_server/static/vendor/@microsoft/office-js/dist/sr-latn-cs/office_strings.js +1 -0
  237. xlwings_server/static/vendor/@microsoft/office-js/dist/sr-latn-rs/office_strings.js +1 -0
  238. xlwings_server/static/vendor/@microsoft/office-js/dist/sv-fi/office_strings.js +8 -0
  239. xlwings_server/static/vendor/@microsoft/office-js/dist/sv-se/office_strings.js +1 -0
  240. xlwings_server/static/vendor/@microsoft/office-js/dist/sw-ke/office_strings.js +1 -0
  241. xlwings_server/static/vendor/@microsoft/office-js/dist/ta-in/office_strings.js +1 -0
  242. xlwings_server/static/vendor/@microsoft/office-js/dist/te-in/office_strings.js +1 -0
  243. xlwings_server/static/vendor/@microsoft/office-js/dist/telemetry/oteljs.js +1 -0
  244. xlwings_server/static/vendor/@microsoft/office-js/dist/telemetry/oteljs_agave.js +1 -0
  245. xlwings_server/static/vendor/@microsoft/office-js/dist/th-th/office_strings.js +1 -0
  246. xlwings_server/static/vendor/@microsoft/office-js/dist/tr-tr/office_strings.js +1 -0
  247. xlwings_server/static/vendor/@microsoft/office-js/dist/uk-ua/office_strings.js +1 -0
  248. xlwings_server/static/vendor/@microsoft/office-js/dist/ur-pk/office_strings.js +1 -0
  249. xlwings_server/static/vendor/@microsoft/office-js/dist/vi-vn/office_strings.js +1 -0
  250. xlwings_server/static/vendor/@microsoft/office-js/dist/webauth/webauth.browserauth.js +77 -0
  251. xlwings_server/static/vendor/@microsoft/office-js/dist/webauth/webauth.implicit.js +35 -0
  252. xlwings_server/static/vendor/@microsoft/office-js/dist/zh-cn/office_strings.js +1 -0
  253. xlwings_server/static/vendor/@microsoft/office-js/dist/zh-hk/office_strings.js +8 -0
  254. xlwings_server/static/vendor/@microsoft/office-js/dist/zh-mo/office_strings.js +8 -0
  255. xlwings_server/static/vendor/@microsoft/office-js/dist/zh-sg/office_strings.js +8 -0
  256. xlwings_server/static/vendor/@microsoft/office-js/dist/zh-tw/office_strings.js +1 -0
  257. xlwings_server/static/vendor/axios/dist/axios.min.js +3 -0
  258. xlwings_server/static/vendor/axios/dist/axios.min.js.map +1 -0
  259. xlwings_server/static/vendor/bootstrap/LICENSE +21 -0
  260. xlwings_server/static/vendor/bootstrap/dist/js/bootstrap.bundle.min.js +7 -0
  261. xlwings_server/static/vendor/bootstrap/dist/js/bootstrap.bundle.min.js.map +1 -0
  262. xlwings_server/static/vendor/bootstrap-xlwings/dist/bootstrap-xlwings.min.css +12 -0
  263. xlwings_server/static/vendor/bootstrap-xlwings/dist/bootstrap-xlwings.min.css.map +1 -0
  264. xlwings_server/static/vendor/htmx-ext-head-support/head-support.js +144 -0
  265. xlwings_server/static/vendor/htmx-ext-loading-states/loading-states.js +184 -0
  266. xlwings_server/static/vendor/htmx.org/LICENSE +13 -0
  267. xlwings_server/static/vendor/htmx.org/dist/htmx.min.js +1 -0
  268. xlwings_server/static/vendor/socket.io/LICENSE +22 -0
  269. xlwings_server/static/vendor/socket.io/client-dist/socket.io.min.js +7 -0
  270. xlwings_server/static/vendor/socket.io/client-dist/socket.io.min.js.map +1 -0
  271. xlwings_server/templates/_book.html +8 -0
  272. xlwings_server/templates/alert_base.html +16 -0
  273. xlwings_server/templates/base.html +117 -0
  274. xlwings_server/templates/examples/alpine/README.md +26 -0
  275. xlwings_server/templates/examples/alpine/taskpane.html +47 -0
  276. xlwings_server/templates/examples/auth/README.md +38 -0
  277. xlwings_server/templates/examples/auth/protected.html +8 -0
  278. xlwings_server/templates/examples/auth/public.html +11 -0
  279. xlwings_server/templates/examples/excel_object_model/README.md +49 -0
  280. xlwings_server/templates/examples/excel_object_model/add_name_form.html +27 -0
  281. xlwings_server/templates/examples/hello_world/README.md +9 -0
  282. xlwings_server/templates/examples/hello_world/taskpane_hello.html +24 -0
  283. xlwings_server/templates/examples/htmx_form/README.md +44 -0
  284. xlwings_server/templates/examples/htmx_form/_greeting.html +6 -0
  285. xlwings_server/templates/examples/htmx_form/taskpane_htmx_form.html +21 -0
  286. xlwings_server/templates/examples/live_form_validation/README.md +60 -0
  287. xlwings_server/templates/examples/live_form_validation/add_name_form.html +33 -0
  288. xlwings_server/templates/examples/multi_app/README.md +34 -0
  289. xlwings_server/templates/examples/multi_app/taskpane1.html +7 -0
  290. xlwings_server/templates/examples/multi_app/taskpane2.html +7 -0
  291. xlwings_server/templates/examples/multi_app/taskpane_loader.html +5 -0
  292. xlwings_server/templates/examples/navigation/README.md +28 -0
  293. xlwings_server/templates/examples/navigation/_navigation.html +16 -0
  294. xlwings_server/templates/examples/navigation/taskpane_one.html +8 -0
  295. xlwings_server/templates/examples/navigation/taskpane_three.html +8 -0
  296. xlwings_server/templates/examples/navigation/taskpane_two.html +8 -0
  297. xlwings_server/templates/examples/pictures/README.md +42 -0
  298. xlwings_server/templates/examples/pictures/_picture.html +4 -0
  299. xlwings_server/templates/examples/pictures/taskpane_pictures.html +26 -0
  300. xlwings_server/templates/manifest.xml +155 -0
  301. xlwings_server/templates/taskpane.html +1 -0
  302. xlwings_server/templates/xlwings_alert.html +27 -0
  303. xlwings_server/templates.py +61 -0
  304. xlwings_server/utils.py +32 -0
  305. xlwings_server/wasm/__init__.py +0 -0
  306. xlwings_server/wasm/config.py +24 -0
  307. xlwings_server/wasm/main.py +236 -0
  308. xlwings_server/wasm/requirements.txt +5 -0
  309. xlwings_server-1.1.0.dist-info/METADATA +61 -0
  310. xlwings_server-1.1.0.dist-info/RECORD +313 -0
  311. xlwings_server-1.1.0.dist-info/WHEEL +4 -0
  312. xlwings_server-1.1.0.dist-info/entry_points.txt +2 -0
  313. xlwings_server-1.1.0.dist-info/licenses/LICENSE.md +223 -0
@@ -0,0 +1,60 @@
1
+ # Dockerfile for xlwings Server
2
+ # Use a Python image with uv pre-installed
3
+ FROM ghcr.io/astral-sh/uv:0.9.26-python3.14-trixie-slim
4
+
5
+ # Setup a non-root user
6
+ RUN groupadd --system --gid 999 nonroot \
7
+ && useradd --system --gid 999 --uid 999 --create-home nonroot
8
+
9
+ # Install the project into `/app`
10
+ WORKDIR /app
11
+
12
+ # Enable bytecode compilation
13
+ ENV UV_COMPILE_BYTECODE=1
14
+
15
+ # Copy from the cache instead of linking since it's a mounted volume
16
+ ENV UV_LINK_MODE=copy
17
+
18
+ # Ensure installed tools can be executed out of the box
19
+ ENV UV_TOOL_BIN_DIR=/usr/local/bin
20
+
21
+ # Copy dependency files first for better layer caching
22
+ COPY pyproject.toml uv.lock ./
23
+
24
+ # Install the project's dependencies using the lockfile and settings
25
+ RUN uv sync --locked --no-install-project --no-dev
26
+
27
+ # Then, add the rest of the project source code and install it
28
+ # Installing separately from its dependencies allows optimal layer caching
29
+ COPY . /app
30
+ RUN uv sync --locked --no-dev
31
+
32
+ # Build static files with hashed filenames for cache-busting
33
+ RUN uv run xlwings-server build static
34
+
35
+ # Place executables in the environment at the front of the path
36
+ ENV PATH="/app/.venv/bin:$PATH"
37
+
38
+ # Python settings
39
+ ENV PYTHONUNBUFFERED=1
40
+
41
+ # Default port and workers
42
+ ENV PORT=8000
43
+ ENV WORKERS=1
44
+ EXPOSE 8000
45
+
46
+ # Reset the entrypoint, don't invoke `uv`
47
+ ENTRYPOINT []
48
+
49
+ # Use the non-root user to run our application
50
+ USER nonroot
51
+
52
+ # Uses shell to set PORT and WORKERS, then exec for proper signal handling
53
+ CMD ["sh", "-c", "\
54
+ exec uvicorn \
55
+ --host 0.0.0.0 \
56
+ --port $PORT \
57
+ --workers $WORKERS \
58
+ --access-log \
59
+ xlwings_server.main:main_app \
60
+ "]
@@ -0,0 +1,32 @@
1
+ # This is for local development
2
+ services:
3
+ xlwings-server:
4
+ build: .
5
+ # Allow the server to run with or without certs
6
+ command: >
7
+ sh -c '
8
+ SSL_OPTS=""
9
+ if [ -f /app/certs/localhost+2-key.pem ] && [ -f /app/certs/localhost+2.pem ]; then
10
+ SSL_OPTS="--ssl-keyfile /app/certs/localhost+2-key.pem --ssl-certfile /app/certs/localhost+2.pem"
11
+ fi
12
+ exec uvicorn \
13
+ --host 0.0.0.0 \
14
+ --port 8000 \
15
+ --reload \
16
+ --access-log \
17
+ $$SSL_OPTS \
18
+ xlwings_server.main:main_app
19
+ '
20
+ ports:
21
+ - "8000:8000"
22
+ env_file:
23
+ - .env
24
+ restart: "no"
25
+ init: true # Helps with reload
26
+ volumes:
27
+ - ./certs:/app/certs
28
+ - ./custom_functions:/app/custom_functions
29
+ - ./custom_scripts:/app/custom_scripts
30
+ - ./dist:/app/dist
31
+ - ./static:/app/static
32
+ - ./templates:/app/templates
@@ -0,0 +1,59 @@
1
+ """
2
+ Uvicorn reloads the app when the Python code is changed. So, we need to reload the
3
+ browser when the app starts to show any changes caused the Python code.
4
+ But, restarting the app takes a lot of time (especially in Docker), so we
5
+ watch HTML, CSS, and JS files separately via Watchfiles. If these files change, we just
6
+ reload the browser without having to restart the Python app.
7
+ """
8
+
9
+ from pathlib import Path
10
+
11
+ from watchfiles import Change, DefaultFilter, awatch
12
+
13
+ from .config import settings
14
+
15
+ browser_reload_triggered_by_backend = False
16
+ watching_frontend_files = False
17
+
18
+
19
+ class WebFilter(DefaultFilter):
20
+ allowed_extensions = (".html", ".css", ".js", ".py", ".txt", ".env")
21
+
22
+ def __call__(self, change: Change, path: str) -> bool:
23
+ if not super().__call__(change, path):
24
+ return False
25
+
26
+ path = Path(path)
27
+ if path.suffix not in self.allowed_extensions:
28
+ return False
29
+
30
+ # Allow HTML/CSS/JS files anywhere
31
+ if path.suffix in (".html", ".css", ".js"):
32
+ return True
33
+
34
+ # xlwings Wasm
35
+ if settings.enable_wasm and path.suffix in (".py", ".txt", ".env"):
36
+ allowed_dirs = ("wasm", "custom_scripts", "custom_functions")
37
+ return any(dir_name in path.parts for dir_name in allowed_dirs)
38
+
39
+ return False
40
+
41
+
42
+ async def watch_frontend_files(sio, directory):
43
+ async for changes in awatch(
44
+ directory,
45
+ watch_filter=WebFilter(),
46
+ ):
47
+ await sio.emit("reload")
48
+
49
+
50
+ async def start_browser_reload_watcher(sio, directory):
51
+ """Needs to be called from the sio connect event on the backend"""
52
+ global browser_reload_triggered_by_backend
53
+ global watching_frontend_files
54
+ if not browser_reload_triggered_by_backend and not settings.enable_wasm:
55
+ await sio.emit("reload")
56
+ browser_reload_triggered_by_backend = True
57
+ if not watching_frontend_files:
58
+ sio.start_background_task(watch_frontend_files, sio=sio, directory=directory)
59
+ watching_frontend_files = True
xlwings_server/main.py ADDED
@@ -0,0 +1,242 @@
1
+ import json
2
+ import logging
3
+ import os
4
+ import sys
5
+ from functools import cache
6
+ from pathlib import Path
7
+
8
+ import socketio
9
+ from fastapi import FastAPI, status
10
+ from fastapi.middleware.cors import CORSMiddleware
11
+ from fastapi.responses import PlainTextResponse
12
+ from fastapi.staticfiles import StaticFiles
13
+ from xlwings import XlwingsError
14
+
15
+ # CRITICAL: Setup sys.path for user overrides BEFORE importing user modules
16
+ # This is done here (not in CLI) so it works when uvicorn imports this module
17
+ if project_dir := os.getenv("XLWINGS_PROJECT_DIR"):
18
+ project_dir = Path(project_dir)
19
+ if str(project_dir) not in sys.path:
20
+ sys.path.insert(0, str(project_dir))
21
+
22
+ from xlwings_server.config import PACKAGE_DIR, PROJECT_DIR, settings
23
+ from xlwings_server.object_handles import ObjectCacheConverter
24
+ from xlwings_server.routers import socketio as socketio_router
25
+ from xlwings_server.routers.manifest import router as manifest_router
26
+ from xlwings_server.routers.root import router as root_router
27
+ from xlwings_server.routers.taskpane import router as taskpane_router
28
+ from xlwings_server.routers.xlwings import router as xlwings_router
29
+ from xlwings_server.templates import templates
30
+
31
+ # Logging
32
+ logging.basicConfig(level=settings.log_level.upper())
33
+ logger = logging.getLogger(__name__)
34
+
35
+ logger.info(f"Running in '{'Wasm' if settings.enable_wasm else 'Server'}' mode.")
36
+
37
+ # App
38
+ app = FastAPI(docs_url=None, redoc_url=None, openapi_url=None)
39
+
40
+
41
+ # Starlette's url_for returns fully qualified URLs causing issues if the reverse proxy
42
+ # handles TLS and the app runs on http (https://github.com/encode/starlette/issues/843)
43
+ def url_for(name: str, **path_params):
44
+ """Wrapper around url_path_for that strips leading slashes from path parameters"""
45
+ if name == "static" and "path" in path_params:
46
+ # Strip leading slash from static file paths to avoid double slashes
47
+ path_params["path"] = path_params["path"].lstrip("/")
48
+ return app.url_path_for(name, **path_params)
49
+
50
+
51
+ templates.env.globals["url_for"] = url_for
52
+
53
+ # Register Converter
54
+ ObjectCacheConverter.register(object, "object", "obj")
55
+
56
+
57
+ # Custom StaticFiles with file-level override support
58
+ class OverridableStaticFiles(StaticFiles):
59
+ """StaticFiles that checks user directory first, then falls back to package directory"""
60
+
61
+ def __init__(self, package_directory: Path, user_directory: Path, **kwargs):
62
+ self.package_dir = package_directory
63
+ self.user_dir = user_directory
64
+ super().__init__(directory=str(package_directory), **kwargs)
65
+
66
+ def lookup_path(self, path: str) -> tuple[str, os.stat_result | None]:
67
+ # Check user directory first
68
+ user_path = self.user_dir / path
69
+ if user_path.is_file():
70
+ return str(user_path), os.stat(user_path)
71
+ # Fallback to package directory
72
+ return super().lookup_path(path)
73
+
74
+
75
+ # CORS: Office Scripts and custom functions in Excel on the web require CORS
76
+ # Using app.add_middleware won't add the CORS headers if you handle the root "Exception"
77
+ # in an exception handler (it would require a specific exception type).
78
+ cors_app = CORSMiddleware(
79
+ app=app,
80
+ allow_origins=settings.cors_allow_origins,
81
+ allow_methods=["POST"],
82
+ allow_headers=["*"],
83
+ allow_credentials=False,
84
+ )
85
+ main_app = cors_app
86
+
87
+ # Socket.io
88
+ if settings.enable_socketio:
89
+ sio_app = socketio.ASGIApp(
90
+ socketio_router.sio,
91
+ # Only forward ASGI traffic if there's no message queue and hence 1 worker setup
92
+ cors_app if not settings.socketio_message_queue_url else None,
93
+ socketio_path=f"{settings.app_path}/socket.io",
94
+ )
95
+ main_app = sio_app if not settings.socketio_message_queue_url else cors_app
96
+
97
+
98
+ # Routers
99
+ app.include_router(root_router)
100
+ app.include_router(xlwings_router)
101
+ app.include_router(taskpane_router)
102
+ app.include_router(manifest_router)
103
+
104
+
105
+ # User routers (optional custom router)
106
+ try:
107
+ from routers import custom
108
+
109
+ if hasattr(custom, "router"):
110
+ app.include_router(custom.router)
111
+ logger.info("Registered custom user router")
112
+ except ModuleNotFoundError:
113
+ logger.debug("No custom router found (routers/custom.py)")
114
+ except Exception:
115
+ logger.exception("Failed to load custom router from routers/custom.py")
116
+
117
+
118
+ # Security headers
119
+ @cache
120
+ def read_security_headers():
121
+ with open(settings.base_dir / "security_headers.json", "r") as f:
122
+ data = json.load(f)
123
+ return data
124
+
125
+
126
+ @app.middleware("http")
127
+ async def add_security_headers(request, call_next):
128
+ # https://owasp.org/www-project-secure-headers/index.html#configuration-proposal
129
+ # https://owasp.org/www-project-secure-headers/ci/headers_add.json
130
+ response = await call_next(request)
131
+ if not settings.add_security_headers and settings.environment == "dev":
132
+ # Prevent caching in dev even if security headers are switched off
133
+ response.headers["Cache-Control"] = "no-store, max-age=0"
134
+ if settings.add_security_headers:
135
+ data = read_security_headers()
136
+
137
+ # Extract file extension from request URL
138
+ file_ext = request.url.path.split(".")[-1].lower()
139
+ image_ext = ("jpg", "jpeg", "png", "gif", "bmp", "webp", "svg")
140
+
141
+ for header in data["headers"]:
142
+ # Excel on Windows doesn't display the icons in the ribbon otherwise
143
+ if header["name"] == "Cache-Control" and file_ext in image_ext:
144
+ continue
145
+ if header["name"] not in (
146
+ "Permissions-Policy", # experimental
147
+ "Clear-Site-Data", # too aggressive
148
+ "Content-Security-Policy", # provide via XLWINGS_CUSTOM_HEADERS
149
+ ):
150
+ # Permissions-Policy headers are experimental
151
+ # Clear-Site-Data is too aggressive
152
+ response.headers[header["name"]] = header["value"]
153
+
154
+ if settings.enable_excel_online:
155
+ response.headers["Cross-Origin-Resource-Policy"] = "cross-origin"
156
+ del response.headers["X-Frame-Options"]
157
+ if settings.cdn_officejs:
158
+ response.headers["Cross-Origin-Resource-Policy"] = "cross-origin"
159
+ del response.headers["Cross-Origin-Embedder-Policy"]
160
+
161
+ # Custom headers
162
+ for header_name, header_value in settings.custom_headers.items():
163
+ response.headers[header_name] = header_value
164
+
165
+ return response
166
+
167
+
168
+ # Static files: in prod might be served by something like nginx or via
169
+ # https://github.com/matthiask/blacknoise or https://github.com/Archmonger/ServeStatic
170
+ # Auto-detect dist/ directory for production builds with hashed filenames
171
+ dist_static = PROJECT_DIR / "dist" / "static"
172
+ if dist_static.exists() and settings.environment != "dev":
173
+ app.mount(
174
+ settings.static_url_path,
175
+ StaticFiles(directory=dist_static),
176
+ name="static",
177
+ )
178
+ logger.info("Serving static files from dist/ (production build)")
179
+ else:
180
+ app.mount(
181
+ settings.static_url_path,
182
+ OverridableStaticFiles(
183
+ package_directory=PACKAGE_DIR / "static",
184
+ user_directory=PROJECT_DIR / "static",
185
+ ),
186
+ name="static",
187
+ )
188
+
189
+
190
+ if settings.enable_wasm:
191
+ # For xlwings Wasm development
192
+ app.mount(
193
+ # Use the same path prefix as for static files
194
+ settings.static_url_path.replace("static", "wasm"),
195
+ OverridableStaticFiles(
196
+ package_directory=PACKAGE_DIR / "wasm",
197
+ user_directory=PROJECT_DIR / "wasm",
198
+ ),
199
+ name="wasm",
200
+ )
201
+
202
+ app.mount(
203
+ # Use the same path prefix as for static files
204
+ settings.static_url_path.replace("static", "custom_functions"),
205
+ OverridableStaticFiles(
206
+ package_directory=PACKAGE_DIR / "custom_functions",
207
+ user_directory=PROJECT_DIR / "custom_functions",
208
+ ),
209
+ name="custom_functions",
210
+ )
211
+
212
+ app.mount(
213
+ # Use the same path prefix as for static files
214
+ settings.static_url_path.replace("static", "custom_scripts"),
215
+ OverridableStaticFiles(
216
+ package_directory=PACKAGE_DIR / "custom_scripts",
217
+ user_directory=PROJECT_DIR / "custom_scripts",
218
+ ),
219
+ name="custom_scripts",
220
+ )
221
+
222
+ if settings.environment == "dev":
223
+ # Don't cache static files
224
+ StaticFiles.is_not_modified = lambda *args, **kwargs: False
225
+
226
+
227
+ # Exception handlers
228
+ @app.exception_handler(XlwingsError)
229
+ async def xlwings_exception_handler(request, exception):
230
+ logger.error(exception)
231
+ msg = str(exception)
232
+ return PlainTextResponse(msg, status_code=status.HTTP_500_INTERNAL_SERVER_ERROR)
233
+
234
+
235
+ @app.exception_handler(Exception)
236
+ async def exception_handler(request, exception):
237
+ logger.error(exception)
238
+ if settings.environment == "dev":
239
+ msg = repr(exception)
240
+ else:
241
+ msg = "An error occurred."
242
+ return PlainTextResponse(msg, status_code=status.HTTP_500_INTERNAL_SERVER_ERROR)
@@ -0,0 +1,14 @@
1
+ # Try to import User model from project directory first (user override)
2
+ # Fall back to package location (default implementation)
3
+ try:
4
+ from models.user import User
5
+ except ModuleNotFoundError:
6
+ from .user import User
7
+
8
+
9
+ class CurrentUser(User):
10
+ """
11
+ Will deliver the current user when used as type hint in a custom function/script
12
+ """
13
+
14
+ pass
@@ -0,0 +1,53 @@
1
+ import logging
2
+ from typing import Any
3
+
4
+ from pydantic import BaseModel, Field, model_validator
5
+
6
+ try:
7
+ from typing import Self
8
+ except ImportError:
9
+ # Python < 3.11
10
+ from typing_extensions import Self
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ class User(BaseModel):
16
+ """
17
+ User model. This is set up so that it populates most fields from claims
18
+ as returned by OIDC tokens (e.g., Entra ID). You can also use the model by setting
19
+ the attributes directly though.
20
+ """
21
+
22
+ id: str | None = None
23
+ name: str | None = None
24
+ domain: str | None = None
25
+ email: str | None = None
26
+ roles: list[str] = []
27
+ ip_address: str | None = None
28
+ claims: dict[str, Any] = Field({}, repr=False)
29
+
30
+ @model_validator(mode="after")
31
+ def populate_from_claims(self) -> Self:
32
+ if self.claims:
33
+ self.id = self.id or self.claims.get("oid")
34
+ self.name = self.name or self.claims.get("name")
35
+ self.email = self.email or self.claims.get("preferred_username")
36
+ self.roles = self.roles or self.claims.get("roles", [])
37
+ if not self.domain and self.email and "@" in self.email:
38
+ self.domain = self.email.split("@")[1]
39
+ return self
40
+
41
+ async def has_required_roles(self, required_roles: list[str] | None = None):
42
+ if required_roles:
43
+ if set(required_roles).issubset(self.roles):
44
+ logger.info(f"User has required roles: {self.name}")
45
+ return True
46
+ else:
47
+ return False
48
+ else:
49
+ return True
50
+
51
+ async def is_authorized(self):
52
+ """Method that can be overridden to implement a global authorization logic"""
53
+ return True
@@ -0,0 +1,142 @@
1
+ import logging
2
+ import zlib
3
+
4
+ try:
5
+ import numpy as np
6
+ except ImportError:
7
+ np = None
8
+ try:
9
+ import pandas as pd
10
+ except ImportError:
11
+ pd = None
12
+ import redis
13
+ from croniter import croniter
14
+ from xlwings import XlwingsError
15
+ from xlwings.constants import ObjectHandleIcons
16
+ from xlwings.conversion import Converter
17
+
18
+ from .config import settings
19
+ from .routers import xlwings as xlwings_router
20
+ from .serializers import deserialize, serialize
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+ # Used if XLWINGS_OBJECT_CACHE_URL, i.e., Redis isn't configured.
25
+ # Only useful with a single worker e.g., during development.
26
+ cache = {}
27
+
28
+
29
+ class ObjectCacheConverter(Converter):
30
+ @staticmethod
31
+ def read_value(cell_address, options):
32
+ # For custom function args of type Entity, the frontend sends the cell address
33
+ # instead of the cell value
34
+ redis_client: redis.Redis = xlwings_router.redis_client_context.get()
35
+ if settings.object_cache_url and not redis_client:
36
+ raise XlwingsError("Failed to connect to Redis")
37
+ key = f"object:{cell_address}"
38
+ if settings.object_cache_url:
39
+ value = redis_client.get(key)
40
+ if not value:
41
+ raise XlwingsError("Object cache is empty")
42
+ if settings.object_cache_enable_compression:
43
+ value = zlib.decompress(value).decode()
44
+ else:
45
+ value = value.decode()
46
+ else:
47
+ value = cache.get(key)
48
+ if not value:
49
+ raise XlwingsError("Object cache is empty")
50
+ obj = deserialize(value)
51
+ return obj
52
+
53
+ @staticmethod
54
+ def write_value(obj, options):
55
+ redis_client: redis.Redis = xlwings_router.redis_client_context.get()
56
+ if settings.object_cache_url and not redis_client:
57
+ raise XlwingsError("Failed to connect to Redis")
58
+ key = f"object:{xlwings_router.caller_address_context.get()}"
59
+ values = serialize(obj)
60
+ if settings.object_cache_url:
61
+ expire_at = None
62
+ if settings.object_cache_expire_at:
63
+ cron = croniter(settings.object_cache_expire_at)
64
+ expire_at = int(cron.get_next())
65
+ if settings.object_cache_enable_compression:
66
+ values = zlib.compress(values.encode())
67
+ redis_client.set(key, values, exat=expire_at)
68
+ else:
69
+ logger.warning(
70
+ "Storing objects in memory. Configure XLWINGS_OBJECT_CACHE_URL "
71
+ "for production use!"
72
+ )
73
+ cache[key] = values
74
+
75
+ obj_type = type(obj).__name__
76
+
77
+ result = {
78
+ "type": "Entity",
79
+ "text": options.get("text", obj_type) or obj_type,
80
+ "properties": {
81
+ "Type": {
82
+ "type": "String",
83
+ "basicValue": obj_type,
84
+ },
85
+ },
86
+ "layouts": {
87
+ "compact": {
88
+ "icon": options.get("icon", ObjectHandleIcons.generic)
89
+ or ObjectHandleIcons.generic
90
+ }
91
+ },
92
+ }
93
+
94
+ # Shape
95
+ def get_shape(obj):
96
+ if pd and isinstance(obj, pd.DataFrame):
97
+ return f"{obj.shape}"
98
+ if np and isinstance(obj, np.ndarray):
99
+ return f"{obj.shape}"
100
+ elif isinstance(obj, (list, tuple)):
101
+ if obj and isinstance(obj[0], (list, tuple)):
102
+ return f"({len(obj)}, {len(obj[0])})"
103
+ return f"({len(obj)},)"
104
+ else:
105
+ try:
106
+ return f"{len(obj)} (length)"
107
+ except Exception:
108
+ return None
109
+
110
+ shape_value = get_shape(obj)
111
+ if shape_value:
112
+ result["properties"]["Shape"] = {
113
+ "type": "String",
114
+ "basicValue": shape_value,
115
+ }
116
+
117
+ # Columns
118
+ cols_info = None
119
+ if pd and isinstance(obj, pd.DataFrame):
120
+ cols_info = ", ".join(f"{col} [{obj[col].dtype}]" for col in obj.columns)
121
+ if cols_info:
122
+ result["properties"]["Columns"] = {
123
+ "type": "String",
124
+ "basicValue": cols_info,
125
+ }
126
+
127
+ # Index
128
+ index_info = None
129
+ if pd and isinstance(obj, pd.DataFrame):
130
+ index_type = type(obj.index).__name__
131
+ index_length = len(obj.index)
132
+ index_start = obj.index[0]
133
+ index_end = obj.index[-1]
134
+ index_info = (
135
+ f"{index_type}: {index_length} entries, {index_start} to {index_end}"
136
+ )
137
+ if index_info:
138
+ result["properties"]["Index"] = {
139
+ "type": "String",
140
+ "basicValue": index_info,
141
+ }
142
+ return result
File without changes
@@ -0,0 +1,82 @@
1
+ import logging
2
+ import os
3
+ import re
4
+
5
+ from fastapi import APIRouter, Header, Request
6
+
7
+ from ..config import settings
8
+ from ..templates import TemplateResponse
9
+
10
+ router = APIRouter(prefix=settings.app_path)
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ def _get_manifest_response(
16
+ request: Request, protocol: str, download: bool = False
17
+ ) -> TemplateResponse:
18
+ if settings.hostname:
19
+ # Settings
20
+ base_url = f"https://{settings.hostname}"
21
+ elif os.getenv("RENDER_EXTERNAL_URL"):
22
+ # Render.com
23
+ base_url = os.getenv("RENDER_EXTERNAL_URL")
24
+ elif os.getenv("WEBSITE_HOSTNAME"):
25
+ # Azure Functions and Azure App Service
26
+ base_url = f"https://{os.getenv('WEBSITE_HOSTNAME')}"
27
+ elif os.getenv("CODESPACES"):
28
+ # GitHub Codespaces
29
+ base_url = f"https://{os.getenv('CODESPACE_NAME')}-8000.{os.getenv('GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN')}"
30
+ else:
31
+ base_url = str(request.base_url)
32
+ if protocol == "https":
33
+ base_url = base_url.replace("http://", "https://")
34
+
35
+ base_url = str(base_url).rstrip("/")
36
+
37
+ manifest_ids = {
38
+ "dev": settings.manifest_id_dev,
39
+ "qa": settings.manifest_id_qa,
40
+ "uat": settings.manifest_id_uat,
41
+ "staging": settings.manifest_id_staging,
42
+ "prod": settings.manifest_id_prod,
43
+ }
44
+ manifest_id = manifest_ids[settings.environment]
45
+
46
+ headers = {}
47
+ if download:
48
+ sanitized_name = re.sub(
49
+ r"[^a-z0-9]+", "-", settings.project_name.lower()
50
+ ).strip("-")
51
+ filename = f"{sanitized_name}-{settings.environment}.xml"
52
+ headers["Content-Disposition"] = f"attachment; filename={filename}"
53
+
54
+ return TemplateResponse(
55
+ request=request,
56
+ name="manifest.xml",
57
+ context={
58
+ "settings": settings,
59
+ "base_url": base_url,
60
+ "base_url_with_app_path": f"{base_url}{settings.app_path}",
61
+ "manifest_id": manifest_id,
62
+ },
63
+ media_type="text/plain",
64
+ headers=headers,
65
+ )
66
+
67
+
68
+ @router.get("/manifest")
69
+ @router.get("/manifest.xml")
70
+ async def manifest(
71
+ request: Request,
72
+ protocol: str = Header(default="", alias="X-Forwarded-Proto"),
73
+ ):
74
+ return _get_manifest_response(request, protocol, download=False)
75
+
76
+
77
+ @router.get("/manifest/download")
78
+ async def manifest_download(
79
+ request: Request,
80
+ protocol: str = Header(default="", alias="X-Forwarded-Proto"),
81
+ ):
82
+ return _get_manifest_response(request, protocol, download=True)