Flowfile 0.4.1__py3-none-any.whl → 0.5.3__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 (332) hide show
  1. build_backends/main.py +25 -22
  2. build_backends/main_prd.py +10 -19
  3. flowfile/__init__.py +179 -73
  4. flowfile/__main__.py +10 -7
  5. flowfile/api.py +52 -59
  6. flowfile/web/__init__.py +14 -9
  7. flowfile/web/static/assets/AdminView-49392a9a.js +713 -0
  8. flowfile/web/static/assets/AdminView-f53bad23.css +129 -0
  9. flowfile/web/static/assets/CloudConnectionView-36bcd6df.css +72 -0
  10. flowfile/web/static/assets/{CloudConnectionManager-d3248f8d.js → CloudConnectionView-f13f202b.js} +11 -11
  11. flowfile/web/static/assets/{CloudStorageReader-d65bf041.js → CloudStorageReader-0023d4a5.js} +10 -8
  12. flowfile/web/static/assets/{CloudStorageReader-29d14fcc.css → CloudStorageReader-24c54524.css} +27 -27
  13. flowfile/web/static/assets/{CloudStorageWriter-b0ee067f.css → CloudStorageWriter-60547855.css} +26 -26
  14. flowfile/web/static/assets/{CloudStorageWriter-e83be3ed.js → CloudStorageWriter-8e781e11.js} +10 -8
  15. flowfile/web/static/assets/{ColumnSelector-47996a16.css → ColumnSelector-371637fb.css} +2 -2
  16. flowfile/web/static/assets/{ColumnSelector-cce661cf.js → ColumnSelector-8ad68ea9.js} +3 -5
  17. flowfile/web/static/assets/{ContextMenu-c13f91d0.css → ContextMenu-26d4dd27.css} +6 -6
  18. flowfile/web/static/assets/{ContextMenu-11a4652a.js → ContextMenu-31ee57f0.js} +3 -3
  19. flowfile/web/static/assets/{ContextMenu-160afb08.js → ContextMenu-69a74055.js} +3 -3
  20. flowfile/web/static/assets/{ContextMenu-cf18d2cc.js → ContextMenu-8e2051c6.js} +3 -3
  21. flowfile/web/static/assets/{ContextMenu-4c74eef1.css → ContextMenu-8ec1729e.css} +6 -6
  22. flowfile/web/static/assets/{ContextMenu-63cfa99b.css → ContextMenu-9b310c60.css} +6 -6
  23. flowfile/web/static/assets/{CrossJoin-d395d38c.js → CrossJoin-03df6938.js} +12 -10
  24. flowfile/web/static/assets/{CrossJoin-1119d18e.css → CrossJoin-71b4cc10.css} +20 -20
  25. flowfile/web/static/assets/CustomNode-59e99a86.css +32 -0
  26. flowfile/web/static/assets/{CustomNode-b812dc0b.js → CustomNode-8479239b.js} +36 -24
  27. flowfile/web/static/assets/{DatabaseConnectionSettings-7000bf2c.js → DatabaseConnectionSettings-869e3efd.js} +5 -4
  28. flowfile/web/static/assets/{DatabaseConnectionSettings-0c04b2e5.css → DatabaseConnectionSettings-e91df89a.css} +13 -13
  29. flowfile/web/static/assets/{DatabaseReader-ae61773c.css → DatabaseReader-36898a00.css} +24 -24
  30. flowfile/web/static/assets/{DatabaseReader-4f035d0c.js → DatabaseReader-c58b9552.js} +25 -15
  31. flowfile/web/static/assets/DatabaseView-6655afd6.css +57 -0
  32. flowfile/web/static/assets/{DatabaseManager-9662ec5b.js → DatabaseView-d26a9140.js} +11 -11
  33. flowfile/web/static/assets/{DatabaseWriter-2f570e53.css → DatabaseWriter-217a99f1.css} +19 -19
  34. flowfile/web/static/assets/{DatabaseWriter-f65dcd54.js → DatabaseWriter-4d05ddc7.js} +17 -10
  35. flowfile/web/static/assets/{designer-e3c150ec.css → DesignerView-a6d0ee84.css} +629 -538
  36. flowfile/web/static/assets/{designer-f3656d8c.js → DesignerView-e6f5c0e8.js} +1214 -3209
  37. flowfile/web/static/assets/{documentation-52b241e7.js → DocumentationView-2e78ef1b.js} +5 -5
  38. flowfile/web/static/assets/{documentation-12216a74.css → DocumentationView-fd46c656.css} +7 -7
  39. flowfile/web/static/assets/{ExploreData-2d0cf4db.css → ExploreData-10c5acc8.css} +13 -12
  40. flowfile/web/static/assets/{ExploreData-94c43dfc.js → ExploreData-7b54caca.js} +18 -9
  41. flowfile/web/static/assets/{ExternalSource-ac04b3cc.js → ExternalSource-3fa399b2.js} +9 -7
  42. flowfile/web/static/assets/{ExternalSource-e37b6275.css → ExternalSource-47ab05a3.css} +17 -17
  43. flowfile/web/static/assets/Filter-7494ea97.css +48 -0
  44. flowfile/web/static/assets/Filter-8cbbdbf3.js +287 -0
  45. flowfile/web/static/assets/{Formula-bb96803d.css → Formula-53d58c43.css} +7 -7
  46. flowfile/web/static/assets/{Formula-71472193.js → Formula-aac42b1e.js} +13 -11
  47. flowfile/web/static/assets/{FuzzyMatch-1010f966.css → FuzzyMatch-ad6361d6.css} +68 -69
  48. flowfile/web/static/assets/{FuzzyMatch-b317f631.js → FuzzyMatch-cd9bbfca.js} +12 -10
  49. flowfile/web/static/assets/{Pivot-cf333e3d.css → GraphSolver-c24dec17.css} +5 -5
  50. flowfile/web/static/assets/{GraphSolver-754a234f.js → GraphSolver-c7e6780e.js} +13 -11
  51. flowfile/web/static/assets/{GroupBy-6c6f9802.js → GroupBy-93c5d22b.js} +9 -7
  52. flowfile/web/static/assets/{GroupBy-b9505323.css → GroupBy-be7ac0bf.css} +10 -10
  53. flowfile/web/static/assets/{Join-fd79b451.css → Join-28b5e18f.css} +22 -22
  54. flowfile/web/static/assets/{Join-a1b800be.js → Join-a19b2de2.js} +13 -11
  55. flowfile/web/static/assets/LoginView-0df4ed0a.js +134 -0
  56. flowfile/web/static/assets/LoginView-d325d632.css +172 -0
  57. flowfile/web/static/assets/ManualInput-3702e677.css +293 -0
  58. flowfile/web/static/assets/{ManualInput-a9640276.js → ManualInput-8d3374b2.js} +170 -116
  59. flowfile/web/static/assets/{MultiSelect-97213888.js → MultiSelect-ad1b6243.js} +2 -2
  60. flowfile/web/static/assets/{MultiSelect.vue_vue_type_script_setup_true_lang-6ffe088a.js → MultiSelect.vue_vue_type_script_setup_true_lang-e278950d.js} +1 -1
  61. flowfile/web/static/assets/NodeDesigner-40b647c9.js +2610 -0
  62. flowfile/web/static/assets/NodeDesigner-5f53be3f.css +1429 -0
  63. flowfile/web/static/assets/{NumericInput-e638088a.js → NumericInput-7100234c.js} +2 -2
  64. flowfile/web/static/assets/{NumericInput.vue_vue_type_script_setup_true_lang-90eb2cba.js → NumericInput.vue_vue_type_script_setup_true_lang-5130219f.js} +5 -2
  65. flowfile/web/static/assets/{Output-ddc9079f.css → Output-35e97000.css} +6 -6
  66. flowfile/web/static/assets/{Output-76750610.js → Output-f5efd2aa.js} +60 -38
  67. flowfile/web/static/assets/{GraphSolver-f0cb7bfb.css → Pivot-0eda81b4.css} +5 -5
  68. flowfile/web/static/assets/{Pivot-7814803f.js → Pivot-d981d23c.js} +11 -9
  69. flowfile/web/static/assets/PivotValidation-0e905b1a.css +13 -0
  70. flowfile/web/static/assets/{PivotValidation-f92137d2.js → PivotValidation-39386e95.js} +3 -3
  71. flowfile/web/static/assets/PivotValidation-41b57ad6.css +13 -0
  72. flowfile/web/static/assets/{PivotValidation-76dd431a.js → PivotValidation-63de1f73.js} +3 -3
  73. flowfile/web/static/assets/{PolarsCode-650322d1.css → PolarsCode-2b1f1f23.css} +4 -4
  74. flowfile/web/static/assets/{PolarsCode-889c3008.js → PolarsCode-f9d69217.js} +18 -9
  75. flowfile/web/static/assets/PopOver-b22f049e.js +939 -0
  76. flowfile/web/static/assets/PopOver-d96599db.css +33 -0
  77. flowfile/web/static/assets/{Read-6b17491f.css → Read-36e7bd51.css} +12 -12
  78. flowfile/web/static/assets/{Read-637b72a7.js → Read-aec2e377.js} +83 -105
  79. flowfile/web/static/assets/{RecordCount-2b050c41.js → RecordCount-78ed6845.js} +6 -4
  80. flowfile/web/static/assets/{RecordId-81df7784.js → RecordId-2156e890.js} +8 -6
  81. flowfile/web/static/assets/{SQLQueryComponent-36cef432.css → SQLQueryComponent-1c2f26b4.css} +5 -5
  82. flowfile/web/static/assets/{SQLQueryComponent-88dcfe53.js → SQLQueryComponent-48c72f5b.js} +3 -3
  83. flowfile/web/static/assets/{Sample-258ad2a9.js → Sample-1352ca74.js} +6 -4
  84. flowfile/web/static/assets/SecretSelector-22b5ff89.js +113 -0
  85. flowfile/web/static/assets/SecretSelector-6329f743.css +43 -0
  86. flowfile/web/static/assets/{SecretManager-2a2cb7e2.js → SecretsView-17df66ee.js} +35 -36
  87. flowfile/web/static/assets/SecretsView-aa291340.css +38 -0
  88. flowfile/web/static/assets/{Select-850215fd.js → Select-0aee4c54.js} +9 -7
  89. flowfile/web/static/assets/{SettingsSection-55bae608.js → SettingsSection-0784e157.js} +3 -3
  90. flowfile/web/static/assets/{SettingsSection-71e6b7e3.css → SettingsSection-07fbbc39.css} +4 -4
  91. flowfile/web/static/assets/{SettingsSection-5c696bee.css → SettingsSection-26fe48d4.css} +4 -4
  92. flowfile/web/static/assets/{SettingsSection-2e4d03c4.css → SettingsSection-8f980839.css} +4 -4
  93. flowfile/web/static/assets/{SettingsSection-0e8d9123.js → SettingsSection-cd341bb6.js} +3 -3
  94. flowfile/web/static/assets/{SettingsSection-29b4fa6b.js → SettingsSection-f2002a6d.js} +3 -3
  95. flowfile/web/static/assets/{SingleSelect-bebd408b.js → SingleSelect-460cc0ea.js} +2 -2
  96. flowfile/web/static/assets/{SingleSelect.vue_vue_type_script_setup_true_lang-6093741c.js → SingleSelect.vue_vue_type_script_setup_true_lang-30741bb2.js} +1 -1
  97. flowfile/web/static/assets/{SliderInput-6a05ab61.js → SliderInput-5d926864.js} +7 -4
  98. flowfile/web/static/assets/SliderInput-f2e4f23c.css +4 -0
  99. flowfile/web/static/assets/{Sort-10ab48ed.js → Sort-3cdc971b.js} +9 -7
  100. flowfile/web/static/assets/{Unique-f9fb0809.css → Sort-8a871341.css} +10 -10
  101. flowfile/web/static/assets/{TextInput-df9d6259.js → TextInput-a2d0bfbd.js} +2 -2
  102. flowfile/web/static/assets/{TextInput.vue_vue_type_script_setup_true_lang-000e1178.js → TextInput.vue_vue_type_script_setup_true_lang-abad1ca2.js} +5 -2
  103. flowfile/web/static/assets/{TextToRows-5d2c1190.css → TextToRows-12afb4f4.css} +10 -10
  104. flowfile/web/static/assets/{TextToRows-6c2d93d8.js → TextToRows-918945f7.js} +11 -10
  105. flowfile/web/static/assets/{ToggleSwitch-0ff7ac52.js → ToggleSwitch-f0ef5196.js} +2 -2
  106. flowfile/web/static/assets/{ToggleSwitch.vue_vue_type_script_setup_true_lang-c6dc3029.js → ToggleSwitch.vue_vue_type_script_setup_true_lang-5605c793.js} +1 -1
  107. flowfile/web/static/assets/{UnavailableFields-5edd5322.css → UnavailableFields-54d2f518.css} +6 -6
  108. flowfile/web/static/assets/{UnavailableFields-1bab97cb.js → UnavailableFields-bdad6144.js} +4 -4
  109. flowfile/web/static/assets/{Union-af6c3d9b.css → Union-d6a8d7d5.css} +7 -7
  110. flowfile/web/static/assets/{Union-b563478a.js → Union-e8ab8c86.js} +8 -6
  111. flowfile/web/static/assets/{Unique-f90db5db.js → Unique-8cd4f976.js} +13 -22
  112. flowfile/web/static/assets/{Sort-3643d625.css → Unique-9fb2f567.css} +10 -10
  113. flowfile/web/static/assets/{Unpivot-1e422df3.css → Unpivot-710a2948.css} +7 -7
  114. flowfile/web/static/assets/{Unpivot-bcb0025f.js → Unpivot-8da14095.js} +10 -8
  115. flowfile/web/static/assets/{UnpivotValidation-c4e73b04.js → UnpivotValidation-6f7d89ff.js} +3 -3
  116. flowfile/web/static/assets/UnpivotValidation-d5ca3b7b.css +13 -0
  117. flowfile/web/static/assets/{VueGraphicWalker-bb8535e2.js → VueGraphicWalker-3fb312e1.js} +4 -4
  118. flowfile/web/static/assets/{VueGraphicWalker-ed5ab88b.css → VueGraphicWalker-430f0b86.css} +1 -1
  119. flowfile/web/static/assets/{api-4c8e3822.js → api-24483f0d.js} +1 -1
  120. flowfile/web/static/assets/{api-2d6adc4f.js → api-8b81fa73.js} +1 -1
  121. flowfile/web/static/assets/{dropDown-35135ba8.css → dropDown-3d8dc5fa.css} +40 -40
  122. flowfile/web/static/assets/{dropDown-1bca8a74.js → dropDown-ac0fda9d.js} +3 -3
  123. flowfile/web/static/assets/{fullEditor-2985687e.js → fullEditor-5497a84a.js} +11 -10
  124. flowfile/web/static/assets/{fullEditor-178376bb.css → fullEditor-a0be62b3.css} +74 -62
  125. flowfile/web/static/assets/{genericNodeSettings-924759c7.css → genericNodeSettings-3b2507ea.css} +10 -10
  126. flowfile/web/static/assets/{genericNodeSettings-0476ba4e.js → genericNodeSettings-99014e1d.js} +5 -5
  127. flowfile/web/static/assets/index-07dda503.js +38 -0
  128. flowfile/web/static/assets/index-3ba44389.js +2696 -0
  129. flowfile/web/static/assets/{index-50508d4d.css → index-e6289dd0.css} +1945 -569
  130. flowfile/web/static/assets/{index-246f201c.js → index-fb6493ae.js} +41626 -40869
  131. flowfile/web/static/assets/node.types-2c15bb7e.js +82 -0
  132. flowfile/web/static/assets/nodeInput-0eb13f1a.js +2 -0
  133. flowfile/web/static/assets/{outputCsv-d686eeaf.js → outputCsv-8f8ba42d.js} +3 -3
  134. flowfile/web/static/assets/outputCsv-b9a072af.css +2499 -0
  135. flowfile/web/static/assets/{outputExcel-8809ea2f.js → outputExcel-393f4fef.js} +3 -3
  136. flowfile/web/static/assets/{outputExcel-b41305c0.css → outputExcel-f5d272b2.css} +26 -26
  137. flowfile/web/static/assets/{outputParquet-53ba645a.js → outputParquet-07c81f65.js} +4 -4
  138. flowfile/web/static/assets/outputParquet-54597c3c.css +4 -0
  139. flowfile/web/static/assets/{readCsv-053bf97b.js → readCsv-07f6d9ad.js} +21 -20
  140. flowfile/web/static/assets/{readCsv-bca3ed53.css → readCsv-3bfac4c3.css} +15 -15
  141. flowfile/web/static/assets/{readExcel-e1b381ea.css → readExcel-3db6b763.css} +13 -13
  142. flowfile/web/static/assets/{readExcel-ad531eab.js → readExcel-ed69bc8f.js} +10 -12
  143. flowfile/web/static/assets/{readParquet-cee068e2.css → readParquet-c5244ad5.css} +4 -4
  144. flowfile/web/static/assets/{readParquet-58e899a1.js → readParquet-e3ed4528.js} +4 -7
  145. flowfile/web/static/assets/secrets.api-002e7d7e.js +65 -0
  146. flowfile/web/static/assets/{selectDynamic-b38de2ba.js → selectDynamic-80b92899.js} +5 -5
  147. flowfile/web/static/assets/{selectDynamic-aa913ff4.css → selectDynamic-f2fb394f.css} +21 -20
  148. flowfile/web/static/assets/{vue-codemirror.esm-db9b8936.js → vue-codemirror.esm-0965f39f.js} +31 -637
  149. flowfile/web/static/assets/{vue-content-loader.es-b5f3ac30.js → vue-content-loader.es-c506ad97.js} +1 -1
  150. flowfile/web/static/index.html +2 -2
  151. {flowfile-0.4.1.dist-info → flowfile-0.5.3.dist-info}/METADATA +4 -4
  152. flowfile-0.5.3.dist-info/RECORD +402 -0
  153. {flowfile-0.4.1.dist-info → flowfile-0.5.3.dist-info}/WHEEL +1 -1
  154. {flowfile-0.4.1.dist-info → flowfile-0.5.3.dist-info}/entry_points.txt +1 -0
  155. flowfile_core/__init__.py +13 -3
  156. flowfile_core/auth/jwt.py +51 -16
  157. flowfile_core/auth/models.py +32 -7
  158. flowfile_core/auth/password.py +89 -0
  159. flowfile_core/auth/secrets.py +8 -6
  160. flowfile_core/configs/__init__.py +9 -7
  161. flowfile_core/configs/flow_logger.py +15 -14
  162. flowfile_core/configs/node_store/__init__.py +72 -4
  163. flowfile_core/configs/node_store/nodes.py +155 -172
  164. flowfile_core/configs/node_store/user_defined_node_registry.py +108 -27
  165. flowfile_core/configs/settings.py +28 -15
  166. flowfile_core/database/connection.py +7 -6
  167. flowfile_core/database/init_db.py +96 -2
  168. flowfile_core/database/models.py +3 -1
  169. flowfile_core/fileExplorer/__init__.py +17 -0
  170. flowfile_core/fileExplorer/funcs.py +123 -57
  171. flowfile_core/fileExplorer/utils.py +10 -11
  172. flowfile_core/flowfile/_extensions/real_time_interface.py +10 -8
  173. flowfile_core/flowfile/analytics/analytics_processor.py +27 -24
  174. flowfile_core/flowfile/analytics/graphic_walker.py +11 -12
  175. flowfile_core/flowfile/analytics/utils.py +1 -1
  176. flowfile_core/flowfile/code_generator/code_generator.py +391 -279
  177. flowfile_core/flowfile/connection_manager/_connection_manager.py +6 -5
  178. flowfile_core/flowfile/connection_manager/models.py +1 -1
  179. flowfile_core/flowfile/database_connection_manager/db_connections.py +60 -44
  180. flowfile_core/flowfile/database_connection_manager/models.py +1 -1
  181. flowfile_core/flowfile/extensions.py +17 -12
  182. flowfile_core/flowfile/flow_data_engine/cloud_storage_reader.py +34 -32
  183. flowfile_core/flowfile/flow_data_engine/create/funcs.py +152 -103
  184. flowfile_core/flowfile/flow_data_engine/flow_data_engine.py +526 -477
  185. flowfile_core/flowfile/flow_data_engine/flow_file_column/interface.py +2 -2
  186. flowfile_core/flowfile/flow_data_engine/flow_file_column/main.py +92 -52
  187. flowfile_core/flowfile/flow_data_engine/flow_file_column/polars_type.py +12 -11
  188. flowfile_core/flowfile/flow_data_engine/flow_file_column/type_registry.py +6 -6
  189. flowfile_core/flowfile/flow_data_engine/flow_file_column/utils.py +26 -30
  190. flowfile_core/flowfile/flow_data_engine/fuzzy_matching/prepare_for_fuzzy_match.py +43 -32
  191. flowfile_core/flowfile/flow_data_engine/join/__init__.py +1 -1
  192. flowfile_core/flowfile/flow_data_engine/join/utils.py +11 -9
  193. flowfile_core/flowfile/flow_data_engine/join/verify_integrity.py +15 -11
  194. flowfile_core/flowfile/flow_data_engine/pivot_table.py +5 -7
  195. flowfile_core/flowfile/flow_data_engine/polars_code_parser.py +95 -82
  196. flowfile_core/flowfile/flow_data_engine/read_excel_tables.py +66 -65
  197. flowfile_core/flowfile/flow_data_engine/sample_data.py +27 -21
  198. flowfile_core/flowfile/flow_data_engine/subprocess_operations/__init__.py +1 -1
  199. flowfile_core/flowfile/flow_data_engine/subprocess_operations/models.py +13 -11
  200. flowfile_core/flowfile/flow_data_engine/subprocess_operations/subprocess_operations.py +360 -191
  201. flowfile_core/flowfile/flow_data_engine/threaded_processes.py +8 -8
  202. flowfile_core/flowfile/flow_data_engine/utils.py +101 -67
  203. flowfile_core/flowfile/flow_graph.py +1011 -561
  204. flowfile_core/flowfile/flow_graph_utils.py +31 -49
  205. flowfile_core/flowfile/flow_node/flow_node.py +332 -232
  206. flowfile_core/flowfile/flow_node/models.py +54 -41
  207. flowfile_core/flowfile/flow_node/schema_callback.py +14 -19
  208. flowfile_core/flowfile/graph_tree/graph_tree.py +41 -41
  209. flowfile_core/flowfile/handler.py +82 -32
  210. flowfile_core/flowfile/manage/compatibility_enhancements.py +493 -47
  211. flowfile_core/flowfile/manage/io_flowfile.py +391 -0
  212. flowfile_core/flowfile/node_designer/__init__.py +15 -13
  213. flowfile_core/flowfile/node_designer/_type_registry.py +34 -37
  214. flowfile_core/flowfile/node_designer/custom_node.py +162 -36
  215. flowfile_core/flowfile/node_designer/ui_components.py +136 -35
  216. flowfile_core/flowfile/schema_callbacks.py +77 -54
  217. flowfile_core/flowfile/setting_generator/__init__.py +0 -1
  218. flowfile_core/flowfile/setting_generator/setting_generator.py +6 -5
  219. flowfile_core/flowfile/setting_generator/settings.py +72 -55
  220. flowfile_core/flowfile/sources/external_sources/base_class.py +12 -10
  221. flowfile_core/flowfile/sources/external_sources/custom_external_sources/external_source.py +27 -17
  222. flowfile_core/flowfile/sources/external_sources/custom_external_sources/sample_users.py +9 -9
  223. flowfile_core/flowfile/sources/external_sources/factory.py +0 -1
  224. flowfile_core/flowfile/sources/external_sources/sql_source/models.py +45 -31
  225. flowfile_core/flowfile/sources/external_sources/sql_source/sql_source.py +198 -73
  226. flowfile_core/flowfile/sources/external_sources/sql_source/utils.py +250 -196
  227. flowfile_core/flowfile/util/calculate_layout.py +9 -13
  228. flowfile_core/flowfile/util/execution_orderer.py +25 -17
  229. flowfile_core/flowfile/util/node_skipper.py +4 -4
  230. flowfile_core/flowfile/utils.py +19 -21
  231. flowfile_core/main.py +26 -19
  232. flowfile_core/routes/auth.py +284 -11
  233. flowfile_core/routes/cloud_connections.py +25 -25
  234. flowfile_core/routes/logs.py +21 -29
  235. flowfile_core/routes/public.py +3 -3
  236. flowfile_core/routes/routes.py +77 -43
  237. flowfile_core/routes/secrets.py +25 -27
  238. flowfile_core/routes/user_defined_components.py +483 -4
  239. flowfile_core/run_lock.py +0 -1
  240. flowfile_core/schemas/__init__.py +4 -6
  241. flowfile_core/schemas/analysis_schemas/graphic_walker_schemas.py +55 -55
  242. flowfile_core/schemas/cloud_storage_schemas.py +59 -55
  243. flowfile_core/schemas/input_schema.py +398 -154
  244. flowfile_core/schemas/output_model.py +50 -35
  245. flowfile_core/schemas/schemas.py +207 -67
  246. flowfile_core/schemas/transform_schema.py +1360 -435
  247. flowfile_core/schemas/yaml_types.py +117 -0
  248. flowfile_core/secret_manager/secret_manager.py +17 -13
  249. flowfile_core/{flowfile/node_designer/data_types.py → types.py} +33 -3
  250. flowfile_core/utils/arrow_reader.py +7 -6
  251. flowfile_core/utils/excel_file_manager.py +3 -3
  252. flowfile_core/utils/fileManager.py +7 -7
  253. flowfile_core/utils/fl_executor.py +8 -10
  254. flowfile_core/utils/utils.py +4 -4
  255. flowfile_core/utils/validate_setup.py +5 -4
  256. flowfile_frame/__init__.py +107 -50
  257. flowfile_frame/adapters.py +2 -9
  258. flowfile_frame/adding_expr.py +73 -32
  259. flowfile_frame/cloud_storage/frame_helpers.py +27 -23
  260. flowfile_frame/cloud_storage/secret_manager.py +12 -26
  261. flowfile_frame/config.py +2 -5
  262. flowfile_frame/expr.py +311 -218
  263. flowfile_frame/expr.pyi +160 -159
  264. flowfile_frame/expr_name.py +23 -23
  265. flowfile_frame/flow_frame.py +581 -489
  266. flowfile_frame/flow_frame.pyi +123 -104
  267. flowfile_frame/flow_frame_methods.py +236 -252
  268. flowfile_frame/group_frame.py +50 -20
  269. flowfile_frame/join.py +2 -2
  270. flowfile_frame/lazy.py +129 -87
  271. flowfile_frame/lazy_methods.py +83 -30
  272. flowfile_frame/list_name_space.py +55 -50
  273. flowfile_frame/selectors.py +148 -68
  274. flowfile_frame/series.py +9 -7
  275. flowfile_frame/utils.py +19 -21
  276. flowfile_worker/__init__.py +12 -4
  277. flowfile_worker/configs.py +11 -19
  278. flowfile_worker/create/__init__.py +14 -27
  279. flowfile_worker/create/funcs.py +143 -94
  280. flowfile_worker/create/models.py +139 -68
  281. flowfile_worker/create/pl_types.py +14 -15
  282. flowfile_worker/create/read_excel_tables.py +34 -41
  283. flowfile_worker/create/utils.py +22 -19
  284. flowfile_worker/external_sources/s3_source/main.py +18 -51
  285. flowfile_worker/external_sources/s3_source/models.py +34 -27
  286. flowfile_worker/external_sources/sql_source/main.py +8 -5
  287. flowfile_worker/external_sources/sql_source/models.py +13 -9
  288. flowfile_worker/flow_logger.py +10 -8
  289. flowfile_worker/funcs.py +214 -155
  290. flowfile_worker/main.py +11 -17
  291. flowfile_worker/models.py +35 -28
  292. flowfile_worker/process_manager.py +2 -3
  293. flowfile_worker/routes.py +121 -93
  294. flowfile_worker/secrets.py +9 -6
  295. flowfile_worker/spawner.py +80 -49
  296. flowfile_worker/utils.py +3 -2
  297. shared/__init__.py +2 -7
  298. shared/storage_config.py +25 -13
  299. test_utils/postgres/commands.py +3 -2
  300. test_utils/postgres/fixtures.py +9 -9
  301. test_utils/s3/commands.py +1 -1
  302. test_utils/s3/data_generator.py +3 -4
  303. test_utils/s3/demo_data_generator.py +4 -7
  304. test_utils/s3/fixtures.py +7 -5
  305. tools/migrate/README.md +56 -0
  306. tools/migrate/__init__.py +12 -0
  307. tools/migrate/__main__.py +118 -0
  308. tools/migrate/legacy_schemas.py +682 -0
  309. tools/migrate/migrate.py +610 -0
  310. tools/migrate/tests/__init__.py +0 -0
  311. tools/migrate/tests/conftest.py +21 -0
  312. tools/migrate/tests/test_migrate.py +622 -0
  313. tools/migrate/tests/test_migration_e2e.py +1009 -0
  314. tools/migrate/tests/test_node_migrations.py +843 -0
  315. flowfile/web/static/assets/CloudConnectionManager-2dfdce2f.css +0 -86
  316. flowfile/web/static/assets/CustomNode-74a37f74.css +0 -32
  317. flowfile/web/static/assets/DatabaseManager-30fa27e5.css +0 -64
  318. flowfile/web/static/assets/Filter-812dcbca.js +0 -164
  319. flowfile/web/static/assets/Filter-f62091b3.css +0 -20
  320. flowfile/web/static/assets/ManualInput-3246a08d.css +0 -96
  321. flowfile/web/static/assets/PivotValidation-891ddfb0.css +0 -13
  322. flowfile/web/static/assets/PivotValidation-c46cd420.css +0 -13
  323. flowfile/web/static/assets/SliderInput-b8fb6a8c.css +0 -4
  324. flowfile/web/static/assets/UnpivotValidation-0d240eeb.css +0 -13
  325. flowfile/web/static/assets/outputCsv-9cc59e0b.css +0 -2499
  326. flowfile/web/static/assets/outputParquet-cf8cf3f2.css +0 -4
  327. flowfile/web/static/assets/secretApi-538058f3.js +0 -46
  328. flowfile/web/static/assets/vue-codemirror-bccfde04.css +0 -32
  329. flowfile-0.4.1.dist-info/RECORD +0 -376
  330. flowfile_core/flowfile/manage/open_flowfile.py +0 -143
  331. {flowfile-0.4.1.dist-info → flowfile-0.5.3.dist-info}/licenses/LICENSE +0 -0
  332. /flowfile_core/flowfile/manage/manage_flowfile.py → /tools/__init__.py +0 -0
@@ -0,0 +1,391 @@
1
+ import json
2
+ from pathlib import Path
3
+
4
+ from flowfile_core.configs.node_store import CUSTOM_NODE_STORE
5
+ from flowfile_core.configs.settings import is_docker_mode
6
+ from flowfile_core.flowfile.flow_graph import FlowGraph
7
+ from flowfile_core.flowfile.manage.compatibility_enhancements import ensure_compatibility, load_flowfile_pickle
8
+ from flowfile_core.schemas import input_schema, schemas
9
+ from shared.storage_config import storage
10
+
11
+ try:
12
+ import yaml
13
+ except ImportError:
14
+ yaml = None
15
+
16
+
17
+ def _validate_flow_path(flow_path: Path) -> Path:
18
+ """Validate flow path is within allowed directories or is an explicit absolute path."""
19
+ resolved = flow_path.resolve()
20
+
21
+ # Check extension
22
+ allowed_extensions = {".yaml", ".yml", ".json", ".flowfile"}
23
+ if resolved.suffix.lower() not in allowed_extensions:
24
+ raise ValueError(f"Unsupported file extension: {resolved.suffix}")
25
+
26
+ # Check file exists
27
+ if not resolved.is_file():
28
+ raise FileNotFoundError(f"Flow file not found: {resolved}")
29
+
30
+ # Allow paths within known safe directories
31
+
32
+ if is_docker_mode():
33
+ safe_directories = [
34
+ storage.flows_directory,
35
+ storage.uploads_directory,
36
+ storage.temp_directory_for_flows,
37
+ ]
38
+ is_safe = any(resolved.is_relative_to(safe_dir) for safe_dir in safe_directories)
39
+ else:
40
+ is_safe = True
41
+
42
+ if not is_safe and not flow_path.is_absolute():
43
+ raise ValueError(
44
+ f"Relative paths must be within flows or uploads directory. "
45
+ f"Use absolute path or place file in: {storage.flows_directory}"
46
+ )
47
+
48
+ return resolved
49
+
50
+
51
+ def _derive_connections_from_nodes(nodes: list[schemas.FlowfileNode]) -> list[tuple[int, int]]:
52
+ """Derive node connections from the outputs stored in each node."""
53
+ connections = []
54
+ for node in nodes:
55
+ if node.outputs:
56
+ for output_id in node.outputs:
57
+ connections.append((node.id, output_id))
58
+ return connections
59
+
60
+
61
+ def determine_insertion_order(node_storage: schemas.FlowInformation):
62
+ ingest_order: list[int] = []
63
+ ingest_order_set: set[int] = set()
64
+ all_nodes = set(node_storage.data.keys())
65
+
66
+ def assure_output_id(input_node: schemas.NodeInformation, output_node: schemas.NodeInformation):
67
+ # assure the output id is in the list with outputs of the input node this is a quick fix
68
+ if output_node.id not in input_node.outputs:
69
+ input_node.outputs.append(output_node.id)
70
+
71
+ def determine_order(node_id: int):
72
+ current_node = node_storage.data.get(node_id)
73
+ if current_node is None:
74
+ return
75
+ output_ids = current_node.outputs
76
+ main_input_ids = current_node.input_ids if current_node.input_ids else []
77
+ input_ids = [
78
+ n
79
+ for n in [current_node.left_input_id, current_node.right_input_id] + main_input_ids
80
+ if (n is not None and n not in ingest_order_set)
81
+ ]
82
+ if len(input_ids) > 0:
83
+ for input_id in input_ids:
84
+ new_node = node_storage.data.get(input_id)
85
+ if new_node is None:
86
+ ingest_order.append(current_node.id)
87
+ ingest_order_set.add(current_node.id)
88
+ continue
89
+ assure_output_id(new_node, current_node)
90
+ if new_node.id not in ingest_order_set:
91
+ determine_order(input_id)
92
+ elif current_node.id not in ingest_order_set:
93
+ ingest_order.append(current_node.id)
94
+ ingest_order_set.add(current_node.id)
95
+
96
+ for output_id in output_ids:
97
+ if output_id not in ingest_order_set:
98
+ determine_order(output_id)
99
+
100
+ if len(node_storage.node_starts) > 0:
101
+ determine_order(node_storage.node_starts[0])
102
+ # add the random not connected nodes
103
+ else:
104
+ for node_id in all_nodes:
105
+ determine_order(node_id)
106
+ ingest_order += list(all_nodes - ingest_order_set)
107
+ return ingest_order
108
+
109
+
110
+ def _load_flowfile_yaml(flow_path: Path) -> schemas.FlowInformation:
111
+ """
112
+ Load a flowfile from YAML format and convert to FlowInformation.
113
+
114
+ Args:
115
+ flow_path: Path to the YAML file
116
+
117
+ Returns:
118
+ FlowInformation object
119
+ """
120
+ if yaml is None:
121
+ raise ImportError("PyYAML is required for YAML files. Install with: pip install pyyaml")
122
+ flow_path = _validate_flow_path(flow_path)
123
+ with open(flow_path, encoding="utf-8") as f:
124
+ data = yaml.safe_load(f)
125
+ # Load as FlowfileData first (handles setting_input validation via node type)
126
+ flowfile_data = schemas.FlowfileData.model_validate(data)
127
+ # Convert to FlowInformation
128
+ return _flowfile_data_to_flow_information(flowfile_data)
129
+
130
+
131
+ def _load_flowfile_json(flow_path: Path) -> schemas.FlowInformation:
132
+ """
133
+ Load a flowfile from JSON format and convert to FlowInformation.
134
+
135
+ Args:
136
+ flow_path: Path to the JSON file
137
+
138
+ Returns:
139
+ FlowInformation object
140
+ """
141
+ flow_path = _validate_flow_path(flow_path)
142
+ with open(flow_path, encoding="utf-8") as f:
143
+ data = json.load(f)
144
+
145
+ # Load as FlowfileData first (handles setting_input validation via node type)
146
+ flowfile_data = schemas.FlowfileData.model_validate(data)
147
+
148
+ # Convert to FlowInformation
149
+ return _flowfile_data_to_flow_information(flowfile_data)
150
+
151
+
152
+ def _flowfile_data_to_flow_information(flowfile_data: schemas.FlowfileData) -> schemas.FlowInformation:
153
+ from flowfile_core.schemas.schemas import get_settings_class_for_node_type
154
+
155
+ nodes_dict = {}
156
+ node_starts = []
157
+ for node in flowfile_data.nodes:
158
+ setting_input = None
159
+ if node.setting_input is not None:
160
+ model_class = get_settings_class_for_node_type(node.type)
161
+
162
+ if model_class is None:
163
+ raise ValueError(f"Unknown node type: {node.type}")
164
+
165
+ is_user_defined = model_class == input_schema.UserDefinedNode
166
+
167
+ # Inject fields that were excluded during serialization
168
+ setting_data = (
169
+ node.setting_input if isinstance(node.setting_input, dict) else node.setting_input.model_dump()
170
+ )
171
+ setting_data["flow_id"] = flowfile_data.flowfile_id
172
+ setting_data["node_id"] = node.id
173
+ setting_data["pos_x"] = float(node.x_position or 0)
174
+ setting_data["pos_y"] = float(node.y_position or 0)
175
+ setting_data["description"] = node.description or ""
176
+ setting_data["is_setup"] = True
177
+
178
+ if is_user_defined:
179
+ setting_data["is_user_defined"] = True
180
+ depending_ids = list(node.input_ids or [])
181
+ if node.left_input_id:
182
+ depending_ids.append(node.left_input_id)
183
+ if node.right_input_id:
184
+ depending_ids.append(node.right_input_id)
185
+ setting_data["depending_on_ids"] = depending_ids
186
+ else:
187
+ if "depending_on_id" in model_class.model_fields:
188
+ setting_data["depending_on_id"] = node.input_ids[0] if node.input_ids else -1
189
+ if "depending_on_ids" in model_class.model_fields:
190
+ depending_ids = list(node.input_ids or [])
191
+ if node.left_input_id:
192
+ depending_ids.append(node.left_input_id)
193
+ if node.right_input_id:
194
+ depending_ids.append(node.right_input_id)
195
+ setting_data["depending_on_ids"] = depending_ids
196
+
197
+ if node.type == "output" and "output_settings" in setting_data:
198
+ output_settings = setting_data["output_settings"]
199
+ file_type = output_settings.get("file_type", None)
200
+ if file_type is None:
201
+ raise ValueError("Output node's output_settings must include 'file_type'")
202
+ if "table_settings" not in output_settings:
203
+ output_settings["table_settings"] = {"file_type": file_type}
204
+
205
+ setting_input = model_class.model_validate(setting_data)
206
+
207
+ node_info = schemas.NodeInformation(
208
+ id=node.id,
209
+ type=node.type,
210
+ is_setup=setting_input is not None,
211
+ description=node.description,
212
+ x_position=node.x_position,
213
+ y_position=node.y_position,
214
+ left_input_id=node.left_input_id,
215
+ right_input_id=node.right_input_id,
216
+ input_ids=node.input_ids,
217
+ outputs=node.outputs,
218
+ setting_input=setting_input,
219
+ )
220
+ nodes_dict[node.id] = node_info
221
+ if node.is_start_node:
222
+ node_starts.append(node.id)
223
+
224
+ connections = _derive_connections_from_nodes(flowfile_data.nodes)
225
+
226
+ flow_settings = schemas.FlowSettings(
227
+ flow_id=flowfile_data.flowfile_id,
228
+ name=flowfile_data.flowfile_name,
229
+ description=flowfile_data.flowfile_settings.description,
230
+ execution_mode=flowfile_data.flowfile_settings.execution_mode,
231
+ execution_location=flowfile_data.flowfile_settings.execution_location,
232
+ auto_save=flowfile_data.flowfile_settings.auto_save,
233
+ show_detailed_progress=flowfile_data.flowfile_settings.show_detailed_progress,
234
+ )
235
+
236
+ return schemas.FlowInformation(
237
+ flow_id=flowfile_data.flowfile_id,
238
+ flow_name=flowfile_data.flowfile_name,
239
+ flow_settings=flow_settings,
240
+ data=nodes_dict,
241
+ node_starts=node_starts,
242
+ node_connections=connections,
243
+ )
244
+
245
+
246
+ def _load_flow_storage(flow_path: Path) -> schemas.FlowInformation:
247
+ """
248
+ Load flow storage from any supported format.
249
+
250
+ Supports:
251
+ - .flowfile (pickle) - legacy format
252
+ - .yaml / .yml - new YAML format
253
+ - .json - JSON format
254
+
255
+ Args:
256
+ flow_path: Path to the flowfile
257
+
258
+ Returns:
259
+ FlowInformation object
260
+ """
261
+ flow_path = _validate_flow_path(flow_path)
262
+ suffix = flow_path.suffix.lower()
263
+ if suffix == ".flowfile":
264
+ try:
265
+ flow_storage_obj = load_flowfile_pickle(str(flow_path))
266
+ ensure_compatibility(flow_storage_obj, str(flow_path))
267
+ return flow_storage_obj
268
+ except Exception as e:
269
+ raise ValueError(
270
+ f"Failed to open legacy .flowfile: {e}\n\n" f"Try migrating: migrate_flowfile('{flow_path}')"
271
+ ) from e
272
+
273
+ elif suffix in (".yaml", ".yml"):
274
+ return _load_flowfile_yaml(flow_path)
275
+
276
+ elif suffix == ".json":
277
+ return _load_flowfile_json(flow_path)
278
+ else:
279
+ raise ValueError(f"Unsupported file format: {suffix}")
280
+
281
+
282
+ def open_flow(flow_path: Path) -> FlowGraph:
283
+ """
284
+ Open a flowfile from a given path.
285
+
286
+ Supports multiple formats:
287
+ - .flowfile (pickle) - legacy format, auto-migrated
288
+ - .yaml / .yml - new YAML format
289
+ - .json - JSON format
290
+
291
+ Args:
292
+ flow_path (Path): The absolute or relative path to the flowfile
293
+
294
+ Returns:
295
+ FlowGraph: The flowfile object
296
+ """
297
+ # Load flow storage (handles format detection)
298
+ flow_path = _validate_flow_path(flow_path)
299
+ flow_storage_obj = _load_flow_storage(flow_path)
300
+ flow_storage_obj.flow_settings.path = str(flow_path)
301
+ flow_storage_obj.flow_settings.name = str(flow_path.stem)
302
+ flow_storage_obj.flow_name = str(flow_path.stem)
303
+
304
+ # Determine node insertion order
305
+ ingestion_order = determine_insertion_order(flow_storage_obj)
306
+ new_flow = FlowGraph(name=flow_storage_obj.flow_name, flow_settings=flow_storage_obj.flow_settings)
307
+ # Create new FlowGraph
308
+
309
+ # First pass: add node promises
310
+ for node_id in ingestion_order:
311
+ node_info: schemas.NodeInformation = flow_storage_obj.data[node_id]
312
+ node_promise = input_schema.NodePromise(
313
+ flow_id=new_flow.flow_id,
314
+ node_id=node_info.id,
315
+ pos_x=node_info.x_position,
316
+ pos_y=node_info.y_position,
317
+ node_type=node_info.type,
318
+ )
319
+ if hasattr(node_info.setting_input, "cache_results"):
320
+ node_promise.cache_results = node_info.setting_input.cache_results
321
+ new_flow.add_node_promise(node_promise)
322
+
323
+ for node_id in ingestion_order:
324
+ node_info: schemas.NodeInformation = flow_storage_obj.data[node_id]
325
+ if node_info.is_setup:
326
+ if hasattr(node_info.setting_input, "is_user_defined") and node_info.setting_input.is_user_defined:
327
+ if node_info.type not in CUSTOM_NODE_STORE:
328
+ continue
329
+ user_defined_node_class = CUSTOM_NODE_STORE[node_info.type]
330
+ new_flow.add_user_defined_node(
331
+ custom_node=user_defined_node_class.from_settings(node_info.setting_input.settings),
332
+ user_defined_node_settings=node_info.setting_input,
333
+ )
334
+ else:
335
+ getattr(new_flow, "add_" + node_info.type)(node_info.setting_input)
336
+
337
+ # Setup connections
338
+ from_node = new_flow.get_node(node_id)
339
+ for output_node_id in node_info.outputs or []:
340
+ to_node = new_flow.get_node(output_node_id)
341
+ if to_node is not None:
342
+ output_node_obj = flow_storage_obj.data[output_node_id]
343
+ is_left_input = (output_node_obj.left_input_id == node_id) and (
344
+ to_node.left_input.node_id != node_id if to_node.left_input is not None else True
345
+ )
346
+ is_right_input = (output_node_obj.right_input_id == node_id) and (
347
+ to_node.right_input.node_id != node_id if to_node.right_input is not None else True
348
+ )
349
+ is_main_input = node_id in (output_node_obj.input_ids or [])
350
+
351
+ if is_left_input:
352
+ insert_type = "left"
353
+ elif is_right_input:
354
+ insert_type = "right"
355
+ elif is_main_input:
356
+ insert_type = "main"
357
+ else:
358
+ continue
359
+ to_node.add_node_connection(from_node, insert_type)
360
+ else:
361
+ from_node.delete_lead_to_node(output_node_id)
362
+ if (from_node.node_id, output_node_id) not in flow_storage_obj.node_connections:
363
+ continue
364
+ flow_storage_obj.node_connections.pop(
365
+ flow_storage_obj.node_connections.index((from_node.node_id, output_node_id))
366
+ )
367
+
368
+ # Handle any missing connections
369
+ for missing_connection in set(flow_storage_obj.node_connections) - set(new_flow.node_connections):
370
+ to_node = new_flow.get_node(missing_connection[1])
371
+ if not to_node.has_input:
372
+ test_if_circular_connection(missing_connection, new_flow)
373
+ from_node = new_flow.get_node(missing_connection[0])
374
+ if from_node:
375
+ to_node.add_node_connection(from_node)
376
+
377
+ return new_flow
378
+
379
+
380
+ def test_if_circular_connection(connection: tuple[int, int], flow: FlowGraph):
381
+ to_node = flow.get_node(connection[1])
382
+ leads_to_nodes_queue = [n for n in to_node.leads_to_nodes]
383
+ circular_connection: bool = False
384
+ while len(leads_to_nodes_queue) > 0:
385
+ leads_to_node = leads_to_nodes_queue.pop(0)
386
+ if leads_to_node.node_id == connection[0]:
387
+ circular_connection = True
388
+ break
389
+ for leads_to_node_leads_to in leads_to_node.leads_to_nodes:
390
+ leads_to_nodes_queue.append(leads_to_node_leads_to)
391
+ return circular_connection
@@ -8,40 +8,42 @@ custom nodes, define their UI, and implement their data processing logic.
8
8
  """
9
9
 
10
10
  # Import the core base class for creating a new node
11
+ # Import the main `Types` object for filtering in ColumnSelector
12
+ from flowfile_core.types import Types
13
+
11
14
  from .custom_node import CustomNodeBase, NodeSettings
12
15
 
13
16
  # Import all UI components so they can be used directly
14
17
  from .ui_components import (
18
+ AvailableSecrets,
19
+ ColumnSelector,
20
+ IncomingColumns,
21
+ MultiSelect,
22
+ NumericInput,
23
+ SecretSelector,
15
24
  Section,
25
+ SingleSelect,
26
+ SliderInput,
16
27
  TextInput,
17
- NumericInput,
18
28
  ToggleSwitch,
19
- SingleSelect,
20
- MultiSelect,
21
- ColumnSelector,
22
- IncomingColumns, # Important marker class for dynamic dropdowns
23
29
  )
24
30
 
25
- # Import the main `Types` object for filtering in ColumnSelector
26
- from .data_types import Types
27
-
28
-
29
31
  # Define the public API of this package
30
32
  __all__ = [
31
33
  # Core Node Class
32
34
  "CustomNodeBase",
33
-
34
35
  # UI Components & Layout
35
36
  "Section",
36
37
  "TextInput",
37
38
  "NumericInput",
39
+ "SliderInput",
38
40
  "ToggleSwitch",
39
41
  "SingleSelect",
40
42
  "MultiSelect",
41
43
  "NodeSettings",
42
44
  "ColumnSelector",
43
45
  "IncomingColumns",
44
-
45
- # Data Type Filtering
46
+ "AvailableSecrets",
47
+ "SecretSelector",
46
48
  "Types",
47
- ]
49
+ ]
@@ -5,18 +5,20 @@ This module should not be imported directly by users.
5
5
  """
6
6
 
7
7
  from dataclasses import dataclass
8
- from typing import Type, List, Dict, Set, Any, Union
8
+ from typing import Any
9
+
9
10
  import polars as pl
10
11
 
11
12
  # Import public types
12
- from flowfile_core.flowfile.node_designer.data_types import TypeGroup, DataType
13
+ from flowfile_core.types import DataType, TypeGroup
13
14
 
14
15
 
15
16
  @dataclass(frozen=True)
16
17
  class TypeMapping:
17
18
  """Internal mapping between type representations."""
19
+
18
20
  data_type: DataType
19
- polars_type: Type[pl.DataType]
21
+ polars_type: type[pl.DataType]
20
22
  type_group: TypeGroup
21
23
  aliases: tuple[str, ...] = ()
22
24
 
@@ -28,42 +30,32 @@ class TypeRegistry:
28
30
  """
29
31
 
30
32
  def __init__(self):
31
- self._mappings: List[TypeMapping] = [
33
+ self._mappings: list[TypeMapping] = [
32
34
  # Numeric types
33
35
  TypeMapping(DataType.Int8, pl.Int8, TypeGroup.Numeric, ("i8",)),
34
36
  TypeMapping(DataType.Int16, pl.Int16, TypeGroup.Numeric, ("i16",)),
35
37
  TypeMapping(DataType.Int32, pl.Int32, TypeGroup.Numeric, ("i32", "int32")),
36
- TypeMapping(DataType.Int64, pl.Int64, TypeGroup.Numeric,
37
- ("i64", "int64", "int", "integer", "bigint")),
38
+ TypeMapping(DataType.Int64, pl.Int64, TypeGroup.Numeric, ("i64", "int64", "int", "integer", "bigint")),
38
39
  TypeMapping(DataType.UInt8, pl.UInt8, TypeGroup.Numeric, ("u8",)),
39
40
  TypeMapping(DataType.UInt16, pl.UInt16, TypeGroup.Numeric, ("u16",)),
40
41
  TypeMapping(DataType.UInt32, pl.UInt32, TypeGroup.Numeric, ("u32", "uint32")),
41
42
  TypeMapping(DataType.UInt64, pl.UInt64, TypeGroup.Numeric, ("u64", "uint64")),
42
43
  TypeMapping(DataType.Float32, pl.Float32, TypeGroup.Numeric, ("f32", "float32")),
43
- TypeMapping(DataType.Float64, pl.Float64, TypeGroup.Numeric,
44
- ("f64", "float64", "float", "double")),
45
- TypeMapping(DataType.Decimal, pl.Decimal, TypeGroup.Numeric,
46
- ("decimal", "numeric", "dec")),
47
-
44
+ TypeMapping(DataType.Float64, pl.Float64, TypeGroup.Numeric, ("f64", "float64", "float", "double")),
45
+ TypeMapping(DataType.Decimal, pl.Decimal, TypeGroup.Numeric, ("decimal", "dec")),
48
46
  # String types
49
- TypeMapping(DataType.String, pl.String, TypeGroup.String,
50
- ("str", "string", "utf8", "varchar", "text")),
51
- TypeMapping(DataType.Categorical, pl.Categorical, TypeGroup.String,
52
- ("cat", "categorical", "enum", "factor")),
53
-
47
+ TypeMapping(DataType.String, pl.String, TypeGroup.String, ("str", "string", "utf8", "varchar", "text")),
48
+ TypeMapping(
49
+ DataType.Categorical, pl.Categorical, TypeGroup.String, ("cat", "categorical", "enum", "factor")
50
+ ),
54
51
  # Date types
55
52
  TypeMapping(DataType.Date, pl.Date, TypeGroup.Date, ("date",)),
56
- TypeMapping(DataType.Datetime, pl.Datetime, TypeGroup.Date,
57
- ("datetime", "timestamp")),
53
+ TypeMapping(DataType.Datetime, pl.Datetime, TypeGroup.Date, ("datetime", "timestamp")),
58
54
  TypeMapping(DataType.Time, pl.Time, TypeGroup.Date, ("time",)),
59
- TypeMapping(DataType.Duration, pl.Duration, TypeGroup.Date,
60
- ("duration", "timedelta")),
61
-
55
+ TypeMapping(DataType.Duration, pl.Duration, TypeGroup.Date, ("duration", "timedelta")),
62
56
  # Other types
63
- TypeMapping(DataType.Boolean, pl.Boolean, TypeGroup.Boolean,
64
- ("bool", "boolean")),
65
- TypeMapping(DataType.Binary, pl.Binary, TypeGroup.Binary,
66
- ("binary", "bytes", "bytea")),
57
+ TypeMapping(DataType.Boolean, pl.Boolean, TypeGroup.Boolean, ("bool", "boolean")),
58
+ TypeMapping(DataType.Binary, pl.Binary, TypeGroup.Binary, ("binary", "bytes", "bytea")),
67
59
  TypeMapping(DataType.List, pl.List, TypeGroup.Complex, ("list", "array")),
68
60
  TypeMapping(DataType.Struct, pl.Struct, TypeGroup.Complex, ("struct", "object")),
69
61
  TypeMapping(DataType.Array, pl.Array, TypeGroup.Complex, ("fixed_array",)),
@@ -73,10 +65,10 @@ class TypeRegistry:
73
65
 
74
66
  def _build_indices(self):
75
67
  """Build lookup indices for fast access."""
76
- self._by_data_type: Dict[DataType, TypeMapping] = {}
77
- self._by_polars_type: Dict[Type[pl.DataType], TypeMapping] = {}
78
- self._by_alias: Dict[str, TypeMapping] = {}
79
- self._by_group: Dict[TypeGroup, List[TypeMapping]] = {g: [] for g in TypeGroup}
68
+ self._by_data_type: dict[DataType, TypeMapping] = {}
69
+ self._by_polars_type: dict[type[pl.DataType], TypeMapping] = {}
70
+ self._by_alias: dict[str, TypeMapping] = {}
71
+ self._by_group: dict[TypeGroup, list[TypeMapping]] = {g: [] for g in TypeGroup}
80
72
 
81
73
  for mapping in self._mappings:
82
74
  self._by_data_type[mapping.data_type] = mapping
@@ -96,12 +88,13 @@ class TypeRegistry:
96
88
  # Register "pl.TypeName" format
97
89
  self._by_alias[f"pl.{mapping.polars_type.__name__}".lower()] = mapping
98
90
 
99
- def normalize(self, type_spec: Any) -> Set[DataType]:
91
+ def normalize(self, type_spec: Any) -> set[DataType]:
100
92
  """
101
93
  Normalize any type specification to a set of DataType enums.
102
94
  This is the main internal API for type resolution.
103
95
  """
104
96
  # Handle special case: All types
97
+
105
98
  if type_spec == TypeGroup.All or type_spec == "ALL":
106
99
  return set(self._by_data_type.keys())
107
100
 
@@ -121,7 +114,7 @@ class TypeRegistry:
121
114
 
122
115
  # Handle Polars type instance
123
116
  if isinstance(type_spec, pl.DataType):
124
- base_type = type_spec.base_type() if hasattr(type_spec, 'base_type') else type(type_spec)
117
+ base_type = type_spec.base_type() if hasattr(type_spec, "base_type") else type(type_spec)
125
118
  mapping = self._by_polars_type.get(base_type)
126
119
  if mapping:
127
120
  return {mapping.data_type}
@@ -129,6 +122,10 @@ class TypeRegistry:
129
122
  # Handle string aliases
130
123
  if isinstance(type_spec, str):
131
124
  type_spec_lower = type_spec.lower()
125
+ group: TypeGroup
126
+ for group in TypeGroup:
127
+ if group.lower() == type_spec_lower:
128
+ return {m.data_type for m in (self._by_group.get(group) or [])}
132
129
 
133
130
  # Try TypeGroup name
134
131
  try:
@@ -152,14 +149,14 @@ class TypeRegistry:
152
149
  # Default to empty set if unrecognized
153
150
  return set()
154
151
 
155
- def normalize_list(self, type_specs: List[Any]) -> Set[DataType]:
152
+ def normalize_list(self, type_specs: list[Any]) -> set[DataType]:
156
153
  """Normalize a list of type specifications."""
157
154
  result = set()
158
155
  for spec in type_specs:
159
156
  result.update(self.normalize(spec))
160
157
  return result
161
158
 
162
- def get_polars_types(self, data_types: Set[DataType]) -> Set[Type[pl.DataType]]:
159
+ def get_polars_types(self, data_types: set[DataType]) -> set[type[pl.DataType]]:
163
160
  """Convert a set of DataType enums to Polars types."""
164
161
  result = set()
165
162
  for dt in data_types:
@@ -168,7 +165,7 @@ class TypeRegistry:
168
165
  result.add(mapping.polars_type)
169
166
  return result
170
167
 
171
- def get_polars_type(self, data_type: DataType) -> Type[pl.DataType]:
168
+ def get_polars_type(self, data_type: DataType) -> type[pl.DataType]:
172
169
  """Get the Polars type for a single DataType."""
173
170
  mapping = self._by_data_type.get(data_type)
174
171
  return mapping.polars_type if mapping else pl.String # Default fallback
@@ -179,19 +176,19 @@ _registry = TypeRegistry()
179
176
 
180
177
 
181
178
  # Internal API functions (not for public use)
182
- def normalize_type_spec(type_spec: Any) -> Set[DataType]:
179
+ def normalize_type_spec(type_spec: Any) -> set[DataType]:
183
180
  """Internal function to normalize type specifications."""
184
181
  if isinstance(type_spec, list):
185
182
  return _registry.normalize_list(type_spec)
186
183
  return _registry.normalize(type_spec)
187
184
 
188
185
 
189
- def get_polars_types(data_types: Set[DataType]) -> Set[Type[pl.DataType]]:
186
+ def get_polars_types(data_types: set[DataType]) -> set[type[pl.DataType]]:
190
187
  """Internal function to get Polars types."""
191
188
  return _registry.get_polars_types(data_types)
192
189
 
193
190
 
194
- def check_column_type(column_dtype: pl.DataType, accepted_types: Set[DataType]) -> bool:
191
+ def check_column_type(column_dtype: pl.DataType, accepted_types: set[DataType]) -> bool:
195
192
  """Check if a column's dtype matches the accepted types."""
196
193
  normalized = _registry.normalize(column_dtype)
197
194
  return bool(normalized & accepted_types)