mage-ai 0.9.69__py3-none-any.whl → 0.9.71__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.

Potentially problematic release.


This version of mage-ai might be problematic. Click here for more details.

Files changed (624) hide show
  1. mage_ai/ai/utils/xgboost.py +222 -0
  2. mage_ai/api/errors.py +37 -25
  3. mage_ai/api/operations/base.py +13 -1
  4. mage_ai/api/parsers/PipelineScheduleParser.py +1 -1
  5. mage_ai/api/policies/BackfillPolicy.py +1 -0
  6. mage_ai/api/policies/BlockOutputPolicy.py +40 -17
  7. mage_ai/api/policies/GlobalDataProductPolicy.py +91 -41
  8. mage_ai/api/policies/KernelPolicy.py +55 -32
  9. mage_ai/api/policies/KernelProcessPolicy.py +56 -0
  10. mage_ai/api/policies/OutputPolicy.py +73 -41
  11. mage_ai/api/policies/PipelinePolicy.py +206 -137
  12. mage_ai/api/policies/WorkspacePolicy.py +1 -0
  13. mage_ai/api/presenters/BackfillPresenter.py +1 -0
  14. mage_ai/api/presenters/BlockLayoutItemPresenter.py +9 -7
  15. mage_ai/api/presenters/BlockPresenter.py +1 -1
  16. mage_ai/api/presenters/GlobalDataProductPresenter.py +6 -1
  17. mage_ai/api/presenters/KernelPresenter.py +5 -26
  18. mage_ai/api/presenters/KernelProcessPresenter.py +28 -0
  19. mage_ai/api/presenters/PipelinePresenter.py +18 -5
  20. mage_ai/api/presenters/StatusPresenter.py +2 -0
  21. mage_ai/api/presenters/SyncPresenter.py +25 -0
  22. mage_ai/api/resources/AutocompleteItemResource.py +1 -1
  23. mage_ai/api/resources/BlockLayoutItemResource.py +90 -44
  24. mage_ai/api/resources/BlockOutputResource.py +42 -9
  25. mage_ai/api/resources/BlockResource.py +4 -3
  26. mage_ai/api/resources/BlockRunResource.py +27 -22
  27. mage_ai/api/resources/ClusterResource.py +4 -1
  28. mage_ai/api/resources/CustomTemplateResource.py +34 -14
  29. mage_ai/api/resources/DataProviderResource.py +1 -1
  30. mage_ai/api/resources/ExecutionStateResource.py +3 -1
  31. mage_ai/api/resources/FileContentResource.py +8 -2
  32. mage_ai/api/resources/FileResource.py +10 -4
  33. mage_ai/api/resources/FileVersionResource.py +3 -1
  34. mage_ai/api/resources/GitBranchResource.py +101 -31
  35. mage_ai/api/resources/GitCustomBranchResource.py +29 -1
  36. mage_ai/api/resources/GlobalDataProductResource.py +44 -7
  37. mage_ai/api/resources/GlobalHookResource.py +4 -1
  38. mage_ai/api/resources/IntegrationDestinationResource.py +6 -2
  39. mage_ai/api/resources/IntegrationSourceResource.py +8 -4
  40. mage_ai/api/resources/IntegrationSourceStreamResource.py +6 -2
  41. mage_ai/api/resources/KernelProcessResource.py +44 -0
  42. mage_ai/api/resources/KernelResource.py +25 -3
  43. mage_ai/api/resources/OauthResource.py +1 -1
  44. mage_ai/api/resources/OutputResource.py +33 -11
  45. mage_ai/api/resources/PageBlockLayoutResource.py +34 -23
  46. mage_ai/api/resources/PipelineInteractionResource.py +31 -15
  47. mage_ai/api/resources/PipelineResource.py +258 -125
  48. mage_ai/api/resources/PipelineRunResource.py +52 -7
  49. mage_ai/api/resources/PipelineScheduleResource.py +11 -2
  50. mage_ai/api/resources/PipelineTriggerResource.py +6 -1
  51. mage_ai/api/resources/ProjectResource.py +18 -7
  52. mage_ai/api/resources/PullRequestResource.py +6 -4
  53. mage_ai/api/resources/SecretResource.py +1 -1
  54. mage_ai/api/resources/SeedResource.py +8 -1
  55. mage_ai/api/resources/StatusResource.py +21 -6
  56. mage_ai/api/resources/SyncResource.py +6 -8
  57. mage_ai/api/resources/VariableResource.py +46 -26
  58. mage_ai/api/resources/VersionControlProjectResource.py +9 -2
  59. mage_ai/api/resources/WidgetResource.py +1 -1
  60. mage_ai/api/resources/WorkspaceResource.py +6 -5
  61. mage_ai/api/views.py +47 -40
  62. mage_ai/authentication/permissions/seed.py +16 -2
  63. mage_ai/authentication/providers/oidc.py +21 -1
  64. mage_ai/autocomplete/utils.py +13 -9
  65. mage_ai/cache/base.py +1 -1
  66. mage_ai/cache/block.py +18 -12
  67. mage_ai/cache/block_action_object/__init__.py +33 -5
  68. mage_ai/cache/file.py +22 -19
  69. mage_ai/cache/pipeline.py +18 -12
  70. mage_ai/cli/main.py +1 -0
  71. mage_ai/cluster_manager/aws/emr_cluster_manager.py +9 -5
  72. mage_ai/cluster_manager/config.py +2 -2
  73. mage_ai/cluster_manager/kubernetes/workload_manager.py +52 -1
  74. mage_ai/cluster_manager/manage.py +1 -1
  75. mage_ai/cluster_manager/workspace/base.py +7 -1
  76. mage_ai/cluster_manager/workspace/kubernetes.py +22 -1
  77. mage_ai/command_center/applications/factory.py +10 -7
  78. mage_ai/command_center/applications/utils.py +2 -2
  79. mage_ai/command_center/files/factory.py +17 -15
  80. mage_ai/command_center/presenters/text.py +1 -1
  81. mage_ai/command_center/utils.py +25 -13
  82. mage_ai/data/__init__.py +0 -0
  83. mage_ai/data/constants.py +45 -0
  84. mage_ai/data/models/__init__.py +0 -0
  85. mage_ai/data/models/base.py +119 -0
  86. mage_ai/data/models/constants.py +1 -0
  87. mage_ai/data/models/generator.py +115 -0
  88. mage_ai/data/models/manager.py +168 -0
  89. mage_ai/data/models/pyarrow/__init__.py +0 -0
  90. mage_ai/data/models/pyarrow/record_batch.py +55 -0
  91. mage_ai/data/models/pyarrow/shared.py +21 -0
  92. mage_ai/data/models/pyarrow/table.py +8 -0
  93. mage_ai/data/models/reader.py +103 -0
  94. mage_ai/data/models/utils.py +59 -0
  95. mage_ai/data/models/writer.py +91 -0
  96. mage_ai/data/tabular/__init__.py +0 -0
  97. mage_ai/data/tabular/constants.py +23 -0
  98. mage_ai/data/tabular/mocks.py +19 -0
  99. mage_ai/data/tabular/models.py +126 -0
  100. mage_ai/data/tabular/reader.py +602 -0
  101. mage_ai/data/tabular/utils.py +102 -0
  102. mage_ai/data/tabular/writer.py +266 -0
  103. mage_ai/data/variables/__init__.py +0 -0
  104. mage_ai/data/variables/wrapper.py +54 -0
  105. mage_ai/data_cleaner/analysis/charts.py +61 -39
  106. mage_ai/data_cleaner/column_types/column_type_detector.py +53 -31
  107. mage_ai/data_cleaner/estimators/encoders.py +5 -2
  108. mage_ai/data_integrations/utils/scheduler.py +16 -11
  109. mage_ai/data_preparation/decorators.py +1 -0
  110. mage_ai/data_preparation/executors/block_executor.py +237 -155
  111. mage_ai/data_preparation/executors/k8s_block_executor.py +30 -7
  112. mage_ai/data_preparation/executors/k8s_pipeline_executor.py +30 -7
  113. mage_ai/data_preparation/executors/streaming_pipeline_executor.py +2 -2
  114. mage_ai/data_preparation/git/__init__.py +77 -29
  115. mage_ai/data_preparation/git/api.py +69 -8
  116. mage_ai/data_preparation/git/utils.py +64 -34
  117. mage_ai/data_preparation/logging/logger_manager.py +4 -3
  118. mage_ai/data_preparation/models/block/__init__.py +1562 -879
  119. mage_ai/data_preparation/models/block/data_integration/mixins.py +4 -3
  120. mage_ai/data_preparation/models/block/dynamic/__init__.py +17 -6
  121. mage_ai/data_preparation/models/block/dynamic/child.py +41 -102
  122. mage_ai/data_preparation/models/block/dynamic/constants.py +1 -0
  123. mage_ai/data_preparation/models/block/dynamic/counter.py +296 -0
  124. mage_ai/data_preparation/models/block/dynamic/data.py +16 -0
  125. mage_ai/data_preparation/models/block/dynamic/factory.py +163 -0
  126. mage_ai/data_preparation/models/block/dynamic/models.py +19 -0
  127. mage_ai/data_preparation/models/block/dynamic/shared.py +92 -0
  128. mage_ai/data_preparation/models/block/dynamic/utils.py +295 -167
  129. mage_ai/data_preparation/models/block/dynamic/variables.py +384 -144
  130. mage_ai/data_preparation/models/block/dynamic/wrappers.py +77 -0
  131. mage_ai/data_preparation/models/block/extension/utils.py +10 -1
  132. mage_ai/data_preparation/models/block/global_data_product/__init__.py +35 -3
  133. mage_ai/data_preparation/models/block/integration/__init__.py +6 -2
  134. mage_ai/data_preparation/models/block/outputs.py +722 -0
  135. mage_ai/data_preparation/models/block/platform/mixins.py +7 -8
  136. mage_ai/data_preparation/models/block/r/__init__.py +56 -38
  137. mage_ai/data_preparation/models/block/remote/__init__.py +0 -0
  138. mage_ai/data_preparation/models/block/remote/models.py +58 -0
  139. mage_ai/data_preparation/models/block/settings/__init__.py +0 -0
  140. mage_ai/data_preparation/models/block/settings/dynamic/__init__.py +0 -0
  141. mage_ai/data_preparation/models/block/settings/dynamic/constants.py +7 -0
  142. mage_ai/data_preparation/models/block/settings/dynamic/mixins.py +118 -0
  143. mage_ai/data_preparation/models/block/settings/dynamic/models.py +31 -0
  144. mage_ai/data_preparation/models/block/settings/global_data_products/__init__.py +0 -0
  145. mage_ai/data_preparation/models/block/settings/global_data_products/mixins.py +20 -0
  146. mage_ai/data_preparation/models/block/settings/global_data_products/models.py +46 -0
  147. mage_ai/data_preparation/models/block/settings/variables/__init__.py +0 -0
  148. mage_ai/data_preparation/models/block/settings/variables/mixins.py +74 -0
  149. mage_ai/data_preparation/models/block/settings/variables/models.py +49 -0
  150. mage_ai/data_preparation/models/block/spark/mixins.py +2 -1
  151. mage_ai/data_preparation/models/block/sql/__init__.py +30 -5
  152. mage_ai/data_preparation/models/block/sql/utils/shared.py +21 -3
  153. mage_ai/data_preparation/models/block/utils.py +164 -69
  154. mage_ai/data_preparation/models/constants.py +21 -14
  155. mage_ai/data_preparation/models/custom_templates/custom_block_template.py +18 -13
  156. mage_ai/data_preparation/models/custom_templates/custom_pipeline_template.py +33 -16
  157. mage_ai/data_preparation/models/custom_templates/utils.py +1 -1
  158. mage_ai/data_preparation/models/file.py +41 -28
  159. mage_ai/data_preparation/models/global_data_product/__init__.py +100 -58
  160. mage_ai/data_preparation/models/global_hooks/models.py +1 -0
  161. mage_ai/data_preparation/models/interfaces.py +29 -0
  162. mage_ai/data_preparation/models/pipeline.py +374 -185
  163. mage_ai/data_preparation/models/pipelines/integration_pipeline.py +1 -2
  164. mage_ai/data_preparation/models/pipelines/seed.py +1 -1
  165. mage_ai/data_preparation/models/project/__init__.py +66 -18
  166. mage_ai/data_preparation/models/project/constants.py +2 -0
  167. mage_ai/data_preparation/models/triggers/__init__.py +124 -26
  168. mage_ai/data_preparation/models/utils.py +467 -17
  169. mage_ai/data_preparation/models/variable.py +1028 -137
  170. mage_ai/data_preparation/models/variables/__init__.py +0 -0
  171. mage_ai/data_preparation/models/variables/cache.py +149 -0
  172. mage_ai/data_preparation/models/variables/constants.py +72 -0
  173. mage_ai/data_preparation/models/variables/summarizer.py +336 -0
  174. mage_ai/data_preparation/models/variables/utils.py +77 -0
  175. mage_ai/data_preparation/models/widget/__init__.py +63 -41
  176. mage_ai/data_preparation/models/widget/charts.py +40 -27
  177. mage_ai/data_preparation/models/widget/constants.py +2 -0
  178. mage_ai/data_preparation/models/widget/utils.py +3 -3
  179. mage_ai/data_preparation/preferences.py +3 -3
  180. mage_ai/data_preparation/repo_manager.py +55 -21
  181. mage_ai/data_preparation/storage/base_storage.py +2 -2
  182. mage_ai/data_preparation/storage/gcs_storage.py +7 -4
  183. mage_ai/data_preparation/storage/local_storage.py +18 -9
  184. mage_ai/data_preparation/storage/s3_storage.py +5 -2
  185. mage_ai/data_preparation/templates/data_exporters/streaming/oracledb.yaml +8 -0
  186. mage_ai/data_preparation/variable_manager.py +281 -76
  187. mage_ai/io/base.py +3 -2
  188. mage_ai/io/bigquery.py +1 -0
  189. mage_ai/io/redshift.py +7 -5
  190. mage_ai/kernels/__init__.py +0 -0
  191. mage_ai/kernels/models.py +188 -0
  192. mage_ai/kernels/utils.py +169 -0
  193. mage_ai/orchestration/concurrency.py +6 -2
  194. mage_ai/orchestration/db/__init__.py +1 -0
  195. mage_ai/orchestration/db/migrations/versions/0227396a216c_add_userproject_table.py +38 -0
  196. mage_ai/orchestration/db/migrations/versions/42a14d6143f1_update_token_column_type.py +54 -0
  197. mage_ai/orchestration/db/models/dynamic/__init__.py +0 -0
  198. mage_ai/orchestration/db/models/dynamic/controller.py +67 -0
  199. mage_ai/orchestration/db/models/oauth.py +12 -18
  200. mage_ai/orchestration/db/models/projects.py +10 -0
  201. mage_ai/orchestration/db/models/schedules.py +225 -187
  202. mage_ai/orchestration/db/models/schedules_project_platform.py +18 -12
  203. mage_ai/orchestration/db/models/utils.py +46 -5
  204. mage_ai/orchestration/metrics/pipeline_run.py +8 -9
  205. mage_ai/orchestration/notification/sender.py +38 -15
  206. mage_ai/orchestration/pipeline_scheduler_original.py +64 -33
  207. mage_ai/orchestration/pipeline_scheduler_project_platform.py +1 -1
  208. mage_ai/orchestration/run_status_checker.py +11 -4
  209. mage_ai/orchestration/triggers/api.py +41 -2
  210. mage_ai/orchestration/triggers/global_data_product.py +9 -4
  211. mage_ai/orchestration/triggers/utils.py +10 -1
  212. mage_ai/orchestration/utils/resources.py +3 -0
  213. mage_ai/presenters/charts/data_sources/base.py +4 -2
  214. mage_ai/presenters/charts/data_sources/block.py +15 -9
  215. mage_ai/presenters/charts/data_sources/chart_code.py +8 -5
  216. mage_ai/presenters/charts/data_sources/constants.py +1 -0
  217. mage_ai/presenters/charts/data_sources/system_metrics.py +22 -0
  218. mage_ai/presenters/interactions/models.py +11 -7
  219. mage_ai/presenters/pages/loaders/pipelines.py +5 -3
  220. mage_ai/presenters/pages/models/page_components/pipeline_schedules.py +3 -1
  221. mage_ai/presenters/utils.py +2 -0
  222. mage_ai/server/api/blocks.py +2 -1
  223. mage_ai/server/api/downloads.py +9 -2
  224. mage_ai/server/api/runs.py +151 -0
  225. mage_ai/server/api/triggers.py +3 -1
  226. mage_ai/server/constants.py +1 -1
  227. mage_ai/server/frontend_dist/404.html +8 -8
  228. mage_ai/server/frontend_dist/_next/static/UZLabyPgcxtZvp0O0EUUS/_buildManifest.js +1 -0
  229. mage_ai/server/frontend_dist/_next/static/chunks/1376-22de38b4ad008d8a.js +1 -0
  230. mage_ai/server/frontend_dist/_next/static/chunks/1557-25a7d985d5564fd3.js +1 -0
  231. mage_ai/server/frontend_dist/_next/static/chunks/1668-30b4619b9534519b.js +1 -0
  232. mage_ai/server/frontend_dist/_next/static/chunks/1799-c42db95a015689ee.js +1 -0
  233. mage_ai/server/frontend_dist/_next/static/chunks/2996-2108b53b9d371d8d.js +1 -0
  234. mage_ai/server/frontend_dist/_next/static/chunks/{3548-fa0792ddb88f4646.js → 3548-9d26185b3fb663b1.js} +1 -1
  235. mage_ai/server/frontend_dist/_next/static/chunks/{3763-61b542dafdbf5754.js → 3763-40780c6d1e4b261d.js} +1 -1
  236. mage_ai/server/frontend_dist/_next/static/chunks/3782-129dd2a2448a2e36.js +1 -0
  237. mage_ai/server/frontend_dist/_next/static/chunks/3958-bcdfa414ccfa1eb2.js +1 -0
  238. mage_ai/server/frontend_dist/_next/static/chunks/4168-97fd1578d1a38315.js +1 -0
  239. mage_ai/server/frontend_dist/_next/static/chunks/4982-fa5a238b139fbdd2.js +1 -0
  240. mage_ai/server/frontend_dist/_next/static/chunks/5699-176f445e1313f001.js +1 -0
  241. mage_ai/server/frontend_dist/_next/static/chunks/7162-7dd03f0f605de721.js +1 -0
  242. mage_ai/server/frontend_dist/_next/static/chunks/7779-68d2b72a90c5f925.js +1 -0
  243. mage_ai/server/frontend_dist/_next/static/chunks/7966-5446a8e43711e2f9.js +1 -0
  244. mage_ai/server/frontend_dist/_next/static/chunks/8023-6c2f172f48dcb99b.js +1 -0
  245. mage_ai/server/frontend_dist/_next/static/chunks/8095-c351b8a735d73e0c.js +1 -0
  246. mage_ai/server/frontend_dist/_next/static/chunks/9624-8b8e100079ab69e1.js +1 -0
  247. mage_ai/server/frontend_dist/_next/static/chunks/{main-77fe248a6fbd12d8.js → main-b99d4e30a88d9dc7.js} +1 -1
  248. mage_ai/server/frontend_dist/_next/static/chunks/pages/_app-9fe2d9d07c94e968.js +1 -0
  249. mage_ai/server/frontend_dist/_next/static/chunks/pages/{block-layout-14f952f66964022f.js → block-layout-7f4b735c67115df5.js} +1 -1
  250. mage_ai/server/frontend_dist/_next/static/chunks/pages/global-data-products/[...slug]-e7d48e6b0c3068ac.js +1 -0
  251. mage_ai/server/frontend_dist/_next/static/chunks/pages/global-data-products-b943f31f050fc3a4.js +1 -0
  252. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage-4bfc84ff07d7656f.js +1 -0
  253. mage_ai/server/frontend_dist/_next/static/chunks/pages/{overview-597b74828bf105db.js → overview-9f1ac4ec003884f3.js} +1 -1
  254. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/pipeline-runs-3edc6270c5b0e962.js → frontend_dist/_next/static/chunks/pages/pipeline-runs-6d183f91a2ff6668.js} +1 -1
  255. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-7e737f6fc7e83e9b.js +1 -0
  256. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills-38e1fbcfbfc1014e.js +1 -0
  257. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/dashboard-d94488e3f2eeef36.js +1 -0
  258. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/edit-cc641a7fa8473796.js +1 -0
  259. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/monitors/{block-runs-a5c0362763a21fa8.js → block-runs-284309877f3c5a5a.js} +1 -1
  260. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/monitors/block-runtime-26250e5335194ade.js +1 -0
  261. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/monitors-7acc7afc00df17c2.js → frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/monitors-5f4c8128b2413fd8.js} +1 -1
  262. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/runs/[run]-4ebfc8e400315dda.js +1 -0
  263. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/settings-e5e0150a256aadb3.js +1 -0
  264. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-eb11c5390c982b49.js +1 -0
  265. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/{triggers-1bdfda8edc9cf4a8.js → triggers-4612d15a65c35912.js} +1 -1
  266. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/account/{profile-3f0df3decc856ee9.js → profile-3ae43c932537b254.js} +1 -1
  267. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/preferences-b603d7fe4b175256.js +1 -0
  268. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/settings-c2e9ef989c8bfa73.js → frontend_dist/_next/static/chunks/pages/settings/platform/settings-319ddbabc239e91b.js} +1 -1
  269. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/permissions/{[...slug]-47b64ced27c24985.js → [...slug]-5c360f72e4498855.js} +1 -1
  270. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/{permissions-e5a4d3d815cec25d.js → permissions-fb29fa6c2bd90bb0.js} +1 -1
  271. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/preferences-3b76fa959ffa09d3.js +1 -0
  272. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/roles/{[...slug]-379e1ee292504842.js → [...slug]-3b787b42f1093b1f.js} +1 -1
  273. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/roles-0b83fbdd39e85f5b.js +1 -0
  274. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/sync-data-a1e6950974d643a8.js +1 -0
  275. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/users/{[...slug]-2af9afbe727d88aa.js → [...slug]-0aa019d87db8b0b8.js} +1 -1
  276. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/{users-a4db8710f703c729.js → users-88c694d19207f2ec.js} +1 -1
  277. mage_ai/server/frontend_dist/_next/static/chunks/pages/{triggers-9cba3211434a8966.js → triggers-a599c6ac89be8c8d.js} +1 -1
  278. mage_ai/server/frontend_dist/_next/static/chunks/pages/version-control-31d0d50f7f30462b.js +1 -0
  279. mage_ai/server/frontend_dist/_next/static/chunks/{webpack-d079359c241db804.js → webpack-ac7fdc472bedf682.js} +1 -1
  280. mage_ai/server/frontend_dist/block-layout.html +3 -3
  281. mage_ai/server/frontend_dist/compute.html +6 -6
  282. mage_ai/server/frontend_dist/files.html +6 -6
  283. mage_ai/server/frontend_dist/global-data-products/[...slug].html +6 -6
  284. mage_ai/server/frontend_dist/global-data-products.html +6 -6
  285. mage_ai/server/frontend_dist/global-hooks/[...slug].html +6 -6
  286. mage_ai/server/frontend_dist/global-hooks.html +6 -6
  287. mage_ai/server/frontend_dist/index.html +3 -3
  288. mage_ai/server/frontend_dist/manage/files.html +6 -6
  289. mage_ai/server/frontend_dist/manage/settings.html +6 -6
  290. mage_ai/server/frontend_dist/manage/users/[user].html +6 -6
  291. mage_ai/server/frontend_dist/manage/users/new.html +6 -6
  292. mage_ai/server/frontend_dist/manage/users.html +6 -6
  293. mage_ai/server/frontend_dist/manage.html +6 -6
  294. mage_ai/server/frontend_dist/oauth.html +5 -5
  295. mage_ai/server/frontend_dist/overview.html +6 -6
  296. mage_ai/server/frontend_dist/pipeline-runs.html +6 -6
  297. mage_ai/server/frontend_dist/pipelines/[pipeline]/backfills/[...slug].html +6 -6
  298. mage_ai/server/frontend_dist/pipelines/[pipeline]/backfills.html +6 -6
  299. mage_ai/server/frontend_dist/pipelines/[pipeline]/dashboard.html +6 -6
  300. mage_ai/server/frontend_dist/pipelines/[pipeline]/edit.html +3 -3
  301. mage_ai/server/frontend_dist/pipelines/[pipeline]/logs.html +6 -6
  302. mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors/block-runs.html +6 -6
  303. mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors/block-runtime.html +6 -6
  304. mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors.html +6 -6
  305. mage_ai/server/frontend_dist/pipelines/[pipeline]/runs/[run].html +6 -6
  306. mage_ai/server/frontend_dist/pipelines/[pipeline]/runs.html +6 -6
  307. mage_ai/server/frontend_dist/pipelines/[pipeline]/settings.html +6 -6
  308. mage_ai/server/frontend_dist/pipelines/[pipeline]/syncs.html +6 -6
  309. mage_ai/server/frontend_dist/pipelines/[pipeline]/triggers/[...slug].html +6 -6
  310. mage_ai/server/frontend_dist/pipelines/[pipeline]/triggers.html +6 -6
  311. mage_ai/server/frontend_dist/pipelines/[pipeline].html +3 -3
  312. mage_ai/server/frontend_dist/pipelines.html +6 -6
  313. mage_ai/server/frontend_dist/platform/global-hooks/[...slug].html +6 -6
  314. mage_ai/server/frontend_dist/platform/global-hooks.html +6 -6
  315. mage_ai/server/frontend_dist/settings/account/profile.html +6 -6
  316. mage_ai/server/frontend_dist/settings/platform/preferences.html +6 -6
  317. mage_ai/server/frontend_dist/settings/platform/settings.html +6 -6
  318. mage_ai/server/frontend_dist/settings/workspace/permissions/[...slug].html +6 -6
  319. mage_ai/server/frontend_dist/settings/workspace/permissions.html +6 -6
  320. mage_ai/server/frontend_dist/settings/workspace/preferences.html +6 -6
  321. mage_ai/server/frontend_dist/settings/workspace/roles/[...slug].html +6 -6
  322. mage_ai/server/frontend_dist/settings/workspace/roles.html +6 -6
  323. mage_ai/server/frontend_dist/settings/workspace/sync-data.html +6 -6
  324. mage_ai/server/frontend_dist/settings/workspace/users/[...slug].html +6 -6
  325. mage_ai/server/frontend_dist/settings/workspace/users.html +6 -6
  326. mage_ai/server/frontend_dist/settings.html +3 -3
  327. mage_ai/server/frontend_dist/sign-in.html +15 -15
  328. mage_ai/server/frontend_dist/templates/[...slug].html +6 -6
  329. mage_ai/server/frontend_dist/templates.html +6 -6
  330. mage_ai/server/frontend_dist/terminal.html +6 -6
  331. mage_ai/server/frontend_dist/test.html +3 -3
  332. mage_ai/server/frontend_dist/triggers.html +6 -6
  333. mage_ai/server/frontend_dist/version-control.html +6 -6
  334. mage_ai/server/frontend_dist_base_path_template/404.html +8 -8
  335. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1376-22de38b4ad008d8a.js +1 -0
  336. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1557-25a7d985d5564fd3.js +1 -0
  337. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1668-30b4619b9534519b.js +1 -0
  338. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1799-c42db95a015689ee.js +1 -0
  339. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2996-2108b53b9d371d8d.js +1 -0
  340. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/{3548-fa0792ddb88f4646.js → 3548-9d26185b3fb663b1.js} +1 -1
  341. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/{3763-61b542dafdbf5754.js → 3763-40780c6d1e4b261d.js} +1 -1
  342. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3782-129dd2a2448a2e36.js +1 -0
  343. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3958-bcdfa414ccfa1eb2.js +1 -0
  344. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/4168-97fd1578d1a38315.js +1 -0
  345. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/4982-fa5a238b139fbdd2.js +1 -0
  346. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5699-176f445e1313f001.js +1 -0
  347. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7162-7dd03f0f605de721.js +1 -0
  348. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7779-68d2b72a90c5f925.js +1 -0
  349. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7966-5446a8e43711e2f9.js +1 -0
  350. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/8023-6c2f172f48dcb99b.js +1 -0
  351. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/8095-c351b8a735d73e0c.js +1 -0
  352. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9624-8b8e100079ab69e1.js +1 -0
  353. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/{main-70b78159c2bb3fe1.js → main-384298e9133cec76.js} +1 -1
  354. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/_app-13a578bce3b7f30c.js +1 -0
  355. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/{block-layout-14f952f66964022f.js → block-layout-7f4b735c67115df5.js} +1 -1
  356. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/global-data-products/[...slug]-e7d48e6b0c3068ac.js +1 -0
  357. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/global-data-products-b943f31f050fc3a4.js +1 -0
  358. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage-4bfc84ff07d7656f.js +1 -0
  359. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/{overview-597b74828bf105db.js → overview-9f1ac4ec003884f3.js} +1 -1
  360. mage_ai/server/{frontend_dist/_next/static/chunks/pages/pipeline-runs-3edc6270c5b0e962.js → frontend_dist_base_path_template/_next/static/chunks/pages/pipeline-runs-6d183f91a2ff6668.js} +1 -1
  361. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-7e737f6fc7e83e9b.js +1 -0
  362. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills-38e1fbcfbfc1014e.js +1 -0
  363. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/dashboard-d94488e3f2eeef36.js +1 -0
  364. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/edit-cc641a7fa8473796.js +1 -0
  365. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/monitors/{block-runs-a5c0362763a21fa8.js → block-runs-284309877f3c5a5a.js} +1 -1
  366. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/monitors/block-runtime-26250e5335194ade.js +1 -0
  367. mage_ai/server/{frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/monitors-7acc7afc00df17c2.js → frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/monitors-5f4c8128b2413fd8.js} +1 -1
  368. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/runs/[run]-4ebfc8e400315dda.js +1 -0
  369. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/settings-e5e0150a256aadb3.js +1 -0
  370. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-eb11c5390c982b49.js +1 -0
  371. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/{triggers-1bdfda8edc9cf4a8.js → triggers-4612d15a65c35912.js} +1 -1
  372. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/account/{profile-3f0df3decc856ee9.js → profile-3ae43c932537b254.js} +1 -1
  373. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/preferences-b603d7fe4b175256.js +1 -0
  374. mage_ai/server/{frontend_dist/_next/static/chunks/pages/settings/platform/settings-c2e9ef989c8bfa73.js → frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/settings-319ddbabc239e91b.js} +1 -1
  375. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/permissions/{[...slug]-47b64ced27c24985.js → [...slug]-5c360f72e4498855.js} +1 -1
  376. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/{permissions-e5a4d3d815cec25d.js → permissions-fb29fa6c2bd90bb0.js} +1 -1
  377. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/preferences-3b76fa959ffa09d3.js +1 -0
  378. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/roles/{[...slug]-379e1ee292504842.js → [...slug]-3b787b42f1093b1f.js} +1 -1
  379. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/roles-0b83fbdd39e85f5b.js +1 -0
  380. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/sync-data-a1e6950974d643a8.js +1 -0
  381. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/users/{[...slug]-2af9afbe727d88aa.js → [...slug]-0aa019d87db8b0b8.js} +1 -1
  382. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/{users-a4db8710f703c729.js → users-88c694d19207f2ec.js} +1 -1
  383. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/{triggers-9cba3211434a8966.js → triggers-a599c6ac89be8c8d.js} +1 -1
  384. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/version-control-31d0d50f7f30462b.js +1 -0
  385. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/{webpack-68c003fb6a175cd7.js → webpack-481689d9989710cd.js} +1 -1
  386. mage_ai/server/frontend_dist_base_path_template/_next/static/kcptwoOU-JJJg6Vwpkfmx/_buildManifest.js +1 -0
  387. mage_ai/server/frontend_dist_base_path_template/block-layout.html +3 -3
  388. mage_ai/server/frontend_dist_base_path_template/compute.html +6 -6
  389. mage_ai/server/frontend_dist_base_path_template/files.html +6 -6
  390. mage_ai/server/frontend_dist_base_path_template/global-data-products/[...slug].html +6 -6
  391. mage_ai/server/frontend_dist_base_path_template/global-data-products.html +6 -6
  392. mage_ai/server/frontend_dist_base_path_template/global-hooks/[...slug].html +6 -6
  393. mage_ai/server/frontend_dist_base_path_template/global-hooks.html +6 -6
  394. mage_ai/server/frontend_dist_base_path_template/index.html +3 -3
  395. mage_ai/server/frontend_dist_base_path_template/manage/files.html +6 -6
  396. mage_ai/server/frontend_dist_base_path_template/manage/settings.html +6 -6
  397. mage_ai/server/frontend_dist_base_path_template/manage/users/[user].html +6 -6
  398. mage_ai/server/frontend_dist_base_path_template/manage/users/new.html +6 -6
  399. mage_ai/server/frontend_dist_base_path_template/manage/users.html +6 -6
  400. mage_ai/server/frontend_dist_base_path_template/manage.html +6 -6
  401. mage_ai/server/frontend_dist_base_path_template/oauth.html +5 -5
  402. mage_ai/server/frontend_dist_base_path_template/overview.html +6 -6
  403. mage_ai/server/frontend_dist_base_path_template/pipeline-runs.html +6 -6
  404. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/backfills/[...slug].html +6 -6
  405. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/backfills.html +6 -6
  406. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/dashboard.html +6 -6
  407. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/edit.html +3 -3
  408. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/logs.html +6 -6
  409. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors/block-runs.html +6 -6
  410. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors/block-runtime.html +6 -6
  411. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors.html +6 -6
  412. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/runs/[run].html +6 -6
  413. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/runs.html +6 -6
  414. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/settings.html +6 -6
  415. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/syncs.html +6 -6
  416. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/triggers/[...slug].html +6 -6
  417. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/triggers.html +6 -6
  418. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline].html +3 -3
  419. mage_ai/server/frontend_dist_base_path_template/pipelines.html +6 -6
  420. mage_ai/server/frontend_dist_base_path_template/platform/global-hooks/[...slug].html +6 -6
  421. mage_ai/server/frontend_dist_base_path_template/platform/global-hooks.html +6 -6
  422. mage_ai/server/frontend_dist_base_path_template/settings/account/profile.html +6 -6
  423. mage_ai/server/frontend_dist_base_path_template/settings/platform/preferences.html +6 -6
  424. mage_ai/server/frontend_dist_base_path_template/settings/platform/settings.html +6 -6
  425. mage_ai/server/frontend_dist_base_path_template/settings/workspace/permissions/[...slug].html +6 -6
  426. mage_ai/server/frontend_dist_base_path_template/settings/workspace/permissions.html +6 -6
  427. mage_ai/server/frontend_dist_base_path_template/settings/workspace/preferences.html +6 -6
  428. mage_ai/server/frontend_dist_base_path_template/settings/workspace/roles/[...slug].html +6 -6
  429. mage_ai/server/frontend_dist_base_path_template/settings/workspace/roles.html +6 -6
  430. mage_ai/server/frontend_dist_base_path_template/settings/workspace/sync-data.html +6 -6
  431. mage_ai/server/frontend_dist_base_path_template/settings/workspace/users/[...slug].html +6 -6
  432. mage_ai/server/frontend_dist_base_path_template/settings/workspace/users.html +6 -6
  433. mage_ai/server/frontend_dist_base_path_template/settings.html +3 -3
  434. mage_ai/server/frontend_dist_base_path_template/sign-in.html +15 -15
  435. mage_ai/server/frontend_dist_base_path_template/templates/[...slug].html +6 -6
  436. mage_ai/server/frontend_dist_base_path_template/templates.html +6 -6
  437. mage_ai/server/frontend_dist_base_path_template/terminal.html +6 -6
  438. mage_ai/server/frontend_dist_base_path_template/test.html +3 -3
  439. mage_ai/server/frontend_dist_base_path_template/triggers.html +6 -6
  440. mage_ai/server/frontend_dist_base_path_template/version-control.html +6 -6
  441. mage_ai/server/kernel_output_parser.py +4 -1
  442. mage_ai/server/scheduler_manager.py +12 -1
  443. mage_ai/server/server.py +69 -42
  444. mage_ai/server/utils/custom_output.py +284 -0
  445. mage_ai/server/utils/execute_custom_code.py +245 -0
  446. mage_ai/server/utils/output_display.py +123 -289
  447. mage_ai/server/websocket_server.py +116 -69
  448. mage_ai/services/aws/ecs/ecs.py +1 -0
  449. mage_ai/services/k8s/config.py +27 -4
  450. mage_ai/services/k8s/job_manager.py +6 -1
  451. mage_ai/services/k8s/utils.py +97 -0
  452. mage_ai/services/ssh/aws/emr/utils.py +8 -8
  453. mage_ai/settings/keys/auth.py +1 -0
  454. mage_ai/settings/platform/__init__.py +159 -38
  455. mage_ai/settings/platform/constants.py +5 -0
  456. mage_ai/settings/platform/utils.py +53 -10
  457. mage_ai/settings/repo.py +26 -12
  458. mage_ai/settings/server.py +128 -37
  459. mage_ai/shared/array.py +24 -1
  460. mage_ai/shared/complex.py +45 -0
  461. mage_ai/shared/config.py +2 -1
  462. mage_ai/shared/custom_logger.py +11 -0
  463. mage_ai/shared/dates.py +10 -6
  464. mage_ai/shared/files.py +63 -8
  465. mage_ai/shared/hash.py +33 -9
  466. mage_ai/shared/io.py +9 -5
  467. mage_ai/shared/models.py +82 -24
  468. mage_ai/shared/outputs.py +87 -0
  469. mage_ai/shared/parsers.py +144 -13
  470. mage_ai/shared/path_fixer.py +11 -7
  471. mage_ai/shared/singletons/__init__.py +0 -0
  472. mage_ai/shared/singletons/base.py +47 -0
  473. mage_ai/shared/singletons/memory.py +38 -0
  474. mage_ai/shared/strings.py +34 -1
  475. mage_ai/shared/yaml.py +24 -0
  476. mage_ai/streaming/sinks/oracledb.py +57 -0
  477. mage_ai/streaming/sinks/sink_factory.py +4 -0
  478. mage_ai/system/__init__.py +0 -0
  479. mage_ai/system/constants.py +14 -0
  480. mage_ai/system/memory/__init__.py +0 -0
  481. mage_ai/system/memory/constants.py +1 -0
  482. mage_ai/system/memory/manager.py +174 -0
  483. mage_ai/system/memory/presenters.py +158 -0
  484. mage_ai/system/memory/process.py +216 -0
  485. mage_ai/system/memory/samples.py +13 -0
  486. mage_ai/system/memory/utils.py +656 -0
  487. mage_ai/system/memory/wrappers.py +177 -0
  488. mage_ai/system/models.py +58 -0
  489. mage_ai/system/storage/__init__.py +0 -0
  490. mage_ai/system/storage/utils.py +29 -0
  491. mage_ai/tests/api/endpoints/mixins.py +2 -2
  492. mage_ai/tests/api/endpoints/test_blocks.py +2 -1
  493. mage_ai/tests/api/endpoints/test_custom_designs.py +4 -4
  494. mage_ai/tests/api/endpoints/test_pipeline_runs.py +2 -2
  495. mage_ai/tests/api/endpoints/test_projects.py +2 -1
  496. mage_ai/tests/api/operations/base/mixins.py +1 -1
  497. mage_ai/tests/api/operations/base/test_base.py +27 -27
  498. mage_ai/tests/api/operations/base/test_base_with_user_authentication.py +27 -27
  499. mage_ai/tests/api/operations/base/test_base_with_user_permissions.py +23 -23
  500. mage_ai/tests/api/operations/test_syncs.py +6 -4
  501. mage_ai/tests/api/resources/test_pipeline_resource.py +11 -4
  502. mage_ai/tests/authentication/oauth/test_utils.py +1 -1
  503. mage_ai/tests/authentication/providers/test_oidc.py +59 -0
  504. mage_ai/tests/base_test.py +2 -2
  505. mage_ai/tests/data/__init__.py +0 -0
  506. mage_ai/tests/data/models/__init__.py +0 -0
  507. mage_ai/tests/data_preparation/executors/test_block_executor.py +23 -16
  508. mage_ai/tests/data_preparation/git/test_git.py +4 -1
  509. mage_ai/tests/data_preparation/models/block/dynamic/test_combos.py +305 -0
  510. mage_ai/tests/data_preparation/models/block/dynamic/test_counter.py +212 -0
  511. mage_ai/tests/data_preparation/models/block/dynamic/test_factory.py +360 -0
  512. mage_ai/tests/data_preparation/models/block/dynamic/test_variables.py +332 -0
  513. mage_ai/tests/data_preparation/models/block/hook/test_hook_block.py +2 -2
  514. mage_ai/tests/data_preparation/models/block/platform/test_mixins.py +1 -1
  515. mage_ai/tests/data_preparation/models/block/sql/utils/test_shared.py +26 -1
  516. mage_ai/tests/data_preparation/models/block/test_global_data_product.py +5 -2
  517. mage_ai/tests/data_preparation/models/custom_templates/test_utils.py +5 -4
  518. mage_ai/tests/data_preparation/models/global_hooks/test_hook.py +3 -0
  519. mage_ai/tests/data_preparation/models/global_hooks/test_predicates.py +9 -3
  520. mage_ai/tests/data_preparation/models/test_block.py +115 -120
  521. mage_ai/tests/data_preparation/models/test_blocks_helper.py +114 -0
  522. mage_ai/tests/data_preparation/models/test_global_data_product.py +41 -24
  523. mage_ai/tests/data_preparation/models/test_pipeline.py +9 -6
  524. mage_ai/tests/data_preparation/models/test_project.py +4 -1
  525. mage_ai/tests/data_preparation/models/test_utils.py +80 -0
  526. mage_ai/tests/data_preparation/models/test_variable.py +242 -69
  527. mage_ai/tests/data_preparation/models/variables/__init__.py +0 -0
  528. mage_ai/tests/data_preparation/models/variables/test_summarizer.py +481 -0
  529. mage_ai/tests/data_preparation/storage/shared/__init__.py +0 -0
  530. mage_ai/tests/data_preparation/test_repo_manager.py +6 -7
  531. mage_ai/tests/data_preparation/test_variable_manager.py +57 -48
  532. mage_ai/tests/factory.py +64 -43
  533. mage_ai/tests/orchestration/db/models/test_schedules.py +3 -3
  534. mage_ai/tests/orchestration/db/models/test_schedules_dynamic_blocks.py +279 -0
  535. mage_ai/tests/orchestration/test_pipeline_scheduler.py +1 -0
  536. mage_ai/tests/orchestration/triggers/test_global_data_product.py +141 -138
  537. mage_ai/tests/orchestration/triggers/test_utils.py +3 -2
  538. mage_ai/tests/server/test_server.py +19 -0
  539. mage_ai/tests/services/k8s/test_job_manager.py +27 -6
  540. mage_ai/tests/streaming/sinks/test_oracledb.py +38 -0
  541. mage_ai/tests/test_shared.py +61 -0
  542. mage_ai/usage_statistics/logger.py +7 -2
  543. mage_ai/utils/code.py +33 -19
  544. mage_ai/version_control/branch/utils.py +2 -1
  545. mage_ai/version_control/models.py +3 -2
  546. {mage_ai-0.9.69.dist-info → mage_ai-0.9.71.dist-info}/METADATA +6 -3
  547. {mage_ai-0.9.69.dist-info → mage_ai-0.9.71.dist-info}/RECORD +555 -454
  548. mage_ai/data_preparation/models/global_data_product/constants.py +0 -6
  549. mage_ai/server/frontend_dist/_next/static/_krrrgup_C-dPOpX36S8I/_buildManifest.js +0 -1
  550. mage_ai/server/frontend_dist/_next/static/chunks/1557-df144fbd8b2208c3.js +0 -1
  551. mage_ai/server/frontend_dist/_next/static/chunks/2631-b9f9bea3f1cf906d.js +0 -1
  552. mage_ai/server/frontend_dist/_next/static/chunks/3782-ef4cd4f0b52072d0.js +0 -1
  553. mage_ai/server/frontend_dist/_next/static/chunks/4783-422429203610c318.js +0 -1
  554. mage_ai/server/frontend_dist/_next/static/chunks/5699-6d708c6b2153ea08.js +0 -1
  555. mage_ai/server/frontend_dist/_next/static/chunks/635-0d6b7c8804bcd2dc.js +0 -1
  556. mage_ai/server/frontend_dist/_next/static/chunks/7022-0d52dd8868621fb0.js +0 -1
  557. mage_ai/server/frontend_dist/_next/static/chunks/7361-8a23dd8360593e7a.js +0 -1
  558. mage_ai/server/frontend_dist/_next/static/chunks/7966-f07b2913f7326b50.js +0 -1
  559. mage_ai/server/frontend_dist/_next/static/chunks/8095-bdce03896ef9639a.js +0 -1
  560. mage_ai/server/frontend_dist/_next/static/chunks/8146-6bed4e7401e067e6.js +0 -1
  561. mage_ai/server/frontend_dist/_next/static/chunks/9265-d2a1aaec75ec69b8.js +0 -1
  562. mage_ai/server/frontend_dist/_next/static/chunks/9440-4069842b90d4b801.js +0 -1
  563. mage_ai/server/frontend_dist/_next/static/chunks/9624-59b2f803f9c88cd6.js +0 -1
  564. mage_ai/server/frontend_dist/_next/static/chunks/9832-67896490f6e8a014.js +0 -1
  565. mage_ai/server/frontend_dist/_next/static/chunks/pages/_app-d9c89527266296f7.js +0 -1
  566. mage_ai/server/frontend_dist/_next/static/chunks/pages/global-data-products/[...slug]-591abd392dc50ed4.js +0 -1
  567. mage_ai/server/frontend_dist/_next/static/chunks/pages/global-data-products-78e8e88f2a757a18.js +0 -1
  568. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage-852d403c7bda21b3.js +0 -1
  569. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-ff4bd7a8ec3bab40.js +0 -1
  570. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills-a8b61d8d239fd16f.js +0 -1
  571. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/dashboard-95ffcd3e2b27e567.js +0 -1
  572. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/edit-e1dd1ed71d26c10d.js +0 -1
  573. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/monitors/block-runtime-1ed9045b2f1dfd65.js +0 -1
  574. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/runs/[run]-1417ad1c821d720a.js +0 -1
  575. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/settings-59aca25a5b1d3998.js +0 -1
  576. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-f028ef3880ed856c.js +0 -1
  577. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/preferences-503049734a8b082f.js +0 -1
  578. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/preferences-5b26eeda8aed8a7b.js +0 -1
  579. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/roles-36fa165a48af586b.js +0 -1
  580. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/sync-data-8b793b3b696a2cd3.js +0 -1
  581. mage_ai/server/frontend_dist/_next/static/chunks/pages/version-control-5753fac7c1bfdc88.js +0 -1
  582. mage_ai/server/frontend_dist_base_path_template/_next/static/KLL5mirre9d7_ZeEpaw3s/_buildManifest.js +0 -1
  583. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1557-df144fbd8b2208c3.js +0 -1
  584. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2631-b9f9bea3f1cf906d.js +0 -1
  585. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3782-ef4cd4f0b52072d0.js +0 -1
  586. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/4783-422429203610c318.js +0 -1
  587. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5699-6d708c6b2153ea08.js +0 -1
  588. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/635-0d6b7c8804bcd2dc.js +0 -1
  589. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7022-0d52dd8868621fb0.js +0 -1
  590. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7361-8a23dd8360593e7a.js +0 -1
  591. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7966-f07b2913f7326b50.js +0 -1
  592. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/8095-bdce03896ef9639a.js +0 -1
  593. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/8146-6bed4e7401e067e6.js +0 -1
  594. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9265-d2a1aaec75ec69b8.js +0 -1
  595. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9440-4069842b90d4b801.js +0 -1
  596. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9624-59b2f803f9c88cd6.js +0 -1
  597. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9832-67896490f6e8a014.js +0 -1
  598. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/_app-d9c89527266296f7.js +0 -1
  599. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/global-data-products/[...slug]-591abd392dc50ed4.js +0 -1
  600. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/global-data-products-78e8e88f2a757a18.js +0 -1
  601. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage-852d403c7bda21b3.js +0 -1
  602. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-ff4bd7a8ec3bab40.js +0 -1
  603. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills-a8b61d8d239fd16f.js +0 -1
  604. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/dashboard-95ffcd3e2b27e567.js +0 -1
  605. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/edit-e1dd1ed71d26c10d.js +0 -1
  606. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/monitors/block-runtime-1ed9045b2f1dfd65.js +0 -1
  607. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/runs/[run]-1417ad1c821d720a.js +0 -1
  608. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/settings-59aca25a5b1d3998.js +0 -1
  609. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-f028ef3880ed856c.js +0 -1
  610. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/preferences-503049734a8b082f.js +0 -1
  611. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/preferences-5b26eeda8aed8a7b.js +0 -1
  612. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/roles-36fa165a48af586b.js +0 -1
  613. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/sync-data-8b793b3b696a2cd3.js +0 -1
  614. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/version-control-5753fac7c1bfdc88.js +0 -1
  615. mage_ai/shared/memory.py +0 -90
  616. mage_ai/tests/data_preparation/models/block/dynamic/test_dynamic_helpers.py +0 -48
  617. /mage_ai/{tests/data_preparation/shared → ai/utils}/__init__.py +0 -0
  618. /mage_ai/server/frontend_dist/_next/static/{_krrrgup_C-dPOpX36S8I → UZLabyPgcxtZvp0O0EUUS}/_ssgManifest.js +0 -0
  619. /mage_ai/server/frontend_dist_base_path_template/_next/static/{KLL5mirre9d7_ZeEpaw3s → kcptwoOU-JJJg6Vwpkfmx}/_ssgManifest.js +0 -0
  620. /mage_ai/tests/data_preparation/{shared → storage/shared}/test_secrets.py +0 -0
  621. {mage_ai-0.9.69.dist-info → mage_ai-0.9.71.dist-info}/LICENSE +0 -0
  622. {mage_ai-0.9.69.dist-info → mage_ai-0.9.71.dist-info}/WHEEL +0 -0
  623. {mage_ai-0.9.69.dist-info → mage_ai-0.9.71.dist-info}/entry_points.txt +0 -0
  624. {mage_ai-0.9.69.dist-info → mage_ai-0.9.71.dist-info}/top_level.txt +0 -0
@@ -7,16 +7,17 @@ import tempfile
7
7
  import zipfile
8
8
  from datetime import datetime, timezone
9
9
  from io import BytesIO
10
- from typing import Any, Callable, Dict, List, Tuple, Union
10
+ from typing import Any, Callable, Dict, List, Optional, Tuple, Union
11
11
 
12
12
  import aiofiles
13
13
  import pytz
14
- import yaml
15
14
  from jinja2 import Template
16
15
 
17
16
  from mage_ai.authentication.permissions.constants import EntityName
18
17
  from mage_ai.cache.block import BlockCache
19
18
  from mage_ai.cache.pipeline import PipelineCache
19
+ from mage_ai.data.constants import InputDataType
20
+ from mage_ai.data.tabular.models import BatchSettings
20
21
  from mage_ai.data_preparation.models.block import Block, run_blocks, run_blocks_sync
21
22
  from mage_ai.data_preparation.models.block.block_factory import BlockFactory
22
23
  from mage_ai.data_preparation.models.block.data_integration.utils import (
@@ -27,6 +28,10 @@ from mage_ai.data_preparation.models.block.dynamic.utils import (
27
28
  is_dynamic_block_child,
28
29
  )
29
30
  from mage_ai.data_preparation.models.block.errors import HasDownstreamDependencies
31
+ from mage_ai.data_preparation.models.block.extension.utils import compare_extension
32
+ from mage_ai.data_preparation.models.block.settings.variables.models import (
33
+ ChunkKeyTypeUnion,
34
+ )
30
35
  from mage_ai.data_preparation.models.constants import (
31
36
  DATA_INTEGRATION_CATALOG_FILE,
32
37
  PIPELINE_CONFIG_FILE,
@@ -47,7 +52,10 @@ from mage_ai.data_preparation.models.file import File
47
52
  from mage_ai.data_preparation.models.pipelines.models import PipelineSettings
48
53
  from mage_ai.data_preparation.models.project import Project
49
54
  from mage_ai.data_preparation.models.project.constants import FeatureUUID
50
- from mage_ai.data_preparation.models.utils import is_yaml_serializable
55
+ from mage_ai.data_preparation.models.utils import (
56
+ is_yaml_serializable,
57
+ warn_for_repo_path,
58
+ )
51
59
  from mage_ai.data_preparation.models.variable import Variable
52
60
  from mage_ai.data_preparation.repo_manager import (
53
61
  RepoConfig,
@@ -73,6 +81,7 @@ from mage_ai.shared.io import safe_write, safe_write_async
73
81
  from mage_ai.shared.path_fixer import remove_base_repo_path
74
82
  from mage_ai.shared.strings import format_enum
75
83
  from mage_ai.shared.utils import clean_name
84
+ from mage_ai.shared.yaml import load_yaml, yaml
76
85
 
77
86
  CYCLE_DETECTION_ERR_MESSAGE = 'A cycle was detected in this pipeline'
78
87
 
@@ -81,10 +90,11 @@ class Pipeline:
81
90
  def __init__(
82
91
  self,
83
92
  uuid,
84
- repo_path=None,
93
+ repo_path: str = None,
85
94
  config=None,
86
95
  repo_config=None,
87
96
  catalog=None,
97
+ context_data: Dict = None,
88
98
  use_repo_path: bool = False,
89
99
  description: str = None,
90
100
  tags: List[str] = None,
@@ -99,9 +109,18 @@ class Pipeline:
99
109
  self.description = description
100
110
  self.executor_config = dict()
101
111
  self.executor_type = None
112
+ self._rendered_executor_type = None # Render template variables
102
113
  self.extensions = {}
103
114
  self.name = None
104
115
  self.notification_config = dict()
116
+
117
+ # For multi project
118
+ warn_for_repo_path(repo_path)
119
+ self.context_data = context_data
120
+ if self.context_data is None:
121
+ self.context_data = dict()
122
+ self._project = None
123
+
105
124
  self.repo_path = repo_path or get_repo_path()
106
125
  self.retry_config = {}
107
126
  self.run_pipeline_in_one_process = False
@@ -129,17 +148,28 @@ class Pipeline:
129
148
  # Used for showing the operation history. For example: recently viewed pipelines.
130
149
  self.history = []
131
150
 
151
+ # Path of the pipeline metadata.yaml file
152
+ self._config_path = None
132
153
  if config is None:
133
154
  self.load_config_from_yaml()
134
155
  else:
135
156
  self.load_config(config, catalog=catalog)
136
157
 
137
- @classmethod
138
- def build_config_path(self, uuid: str, repo_path: str, use_repo_path: bool = False) -> str:
158
+ def build_config_path(
159
+ self,
160
+ uuid: str,
161
+ repo_path: str,
162
+ context_data: Dict = None,
163
+ use_repo_path: bool = False,
164
+ ) -> str:
139
165
  if project_platform_activated() and not use_repo_path:
140
166
  from mage_ai.settings.platform.utils import get_pipeline_config_path
141
167
 
142
- config_path, _repo_path = get_pipeline_config_path(uuid)
168
+ config_path, _repo_path = get_pipeline_config_path(
169
+ uuid,
170
+ context_data=context_data,
171
+ repo_path=repo_path,
172
+ )
143
173
  if config_path:
144
174
  return config_path
145
175
 
@@ -152,14 +182,25 @@ class Pipeline:
152
182
 
153
183
  @property
154
184
  def config_path(self):
155
- return self.build_config_path(self.uuid, self.repo_path, use_repo_path=self.use_repo_path)
185
+ if not self._config_path:
186
+ self._config_path = self.build_config_path(
187
+ self.uuid,
188
+ self.repo_path,
189
+ context_data=self.context_data,
190
+ use_repo_path=self.use_repo_path,
191
+ )
192
+ return self._config_path
156
193
 
157
194
  @property
158
195
  def catalog_config_path(self):
159
196
  if project_platform_activated() and not self.use_repo_path:
160
197
  from mage_ai.settings.platform.utils import get_pipeline_config_path
161
198
 
162
- config_path, _repo_path = get_pipeline_config_path(self.uuid)
199
+ config_path, _repo_path = get_pipeline_config_path(
200
+ self.uuid,
201
+ context_data=self.context_data,
202
+ repo_path=self.repo_path,
203
+ )
163
204
  if config_path:
164
205
  return os.path.join(os.path.dirname(config_path), DATA_INTEGRATION_CATALOG_FILE)
165
206
 
@@ -197,6 +238,16 @@ class Pipeline:
197
238
  self.uuid,
198
239
  )
199
240
 
241
+ @property
242
+ def project(self):
243
+ if self._project is not None:
244
+ return self._project
245
+
246
+ from mage_ai.data_preparation.models.project import Project
247
+
248
+ self._project = Project(context_data=self.context_data, repo_config=self.repo_config)
249
+ return self._project
250
+
200
251
  @property
201
252
  def remote_variables_dir(self):
202
253
  remote_variables_dir = self.repo_config.remote_variables_dir
@@ -215,18 +266,20 @@ class Pipeline:
215
266
 
216
267
  @property
217
268
  def all_block_configs(self) -> List[Dict]:
218
- return self.block_configs + \
219
- self.conditional_configs + \
220
- self.callback_configs + \
221
- self.widget_configs
269
+ return (
270
+ self.block_configs
271
+ + self.conditional_configs
272
+ + self.callback_configs
273
+ + self.widget_configs
274
+ )
222
275
 
223
276
  @classmethod
224
277
  def create(
225
278
  self,
226
279
  name: str,
280
+ repo_path: str = None,
227
281
  description: str = None,
228
282
  pipeline_type: PipelineType = PipelineType.PYTHON,
229
- repo_path: str = None,
230
283
  tags: List[str] = None,
231
284
  ):
232
285
  """
@@ -234,6 +287,8 @@ class Pipeline:
234
287
  2. Create a new yaml file to store pipeline config
235
288
  3. Create other files: requirements.txt, __init__.py
236
289
  """
290
+ warn_for_repo_path(repo_path)
291
+
237
292
  uuid = clean_name(name)
238
293
  pipeline_path = os.path.join(repo_path, PIPELINES_FOLDER, uuid)
239
294
  if os.path.exists(pipeline_path):
@@ -242,14 +297,17 @@ class Pipeline:
242
297
  copy_template_directory('pipeline', pipeline_path)
243
298
  # Update metadata.yaml with pipeline config
244
299
  with open(os.path.join(pipeline_path, PIPELINE_CONFIG_FILE), 'w') as fp:
245
- yaml.dump(dict(
246
- created_at=str(datetime.now(tz=pytz.UTC)),
247
- description=description,
248
- name=name,
249
- tags=tags or [],
250
- uuid=uuid,
251
- type=format_enum(pipeline_type or PipelineType.PYTHON),
252
- ), fp)
300
+ yaml.dump(
301
+ dict(
302
+ created_at=str(datetime.now(tz=pytz.UTC)),
303
+ description=description,
304
+ name=name,
305
+ tags=tags or [],
306
+ uuid=uuid,
307
+ type=format_enum(pipeline_type or PipelineType.PYTHON),
308
+ ),
309
+ fp,
310
+ )
253
311
 
254
312
  pipeline = Pipeline(
255
313
  uuid,
@@ -268,10 +326,13 @@ class Pipeline:
268
326
  zip_size = sum(e.file_size for e in zipf.infolist()) # calc zip size in bytes
269
327
  if zip_size / 1000 > PIPELINE_MAX_FILE_SIZE: # prevention against zip-bombs
270
328
  raise PipelineZipTooLargeError(
271
- f'Pipeline zip exceeds size limit {PIPELINE_MAX_FILE_SIZE/1000}Kb')
329
+ f'Pipeline zip exceeds size limit {PIPELINE_MAX_FILE_SIZE / 1000}Kb'
330
+ )
272
331
 
273
332
  # Ignore `__MACOSX` for zips created on macOS systems
274
- zip_contents = [path for path in zipf.namelist() if not path.startswith('__MACOSX')]
333
+ zip_contents = [
334
+ path for path in zipf.namelist() if not path.startswith('__MACOSX')
335
+ ]
275
336
  # Verify if zip contents are part of a root folder
276
337
  prefix = os.path.commonpath(zip_contents)
277
338
 
@@ -301,7 +362,9 @@ class Pipeline:
301
362
  raise FileWriteError(f'Failed to write pipeline file to {destination}.')
302
363
 
303
364
  # return the pipeline configuration file
304
- config_destination_path = pipeline_files[0][1] # First item is the pipeline config path
365
+ config_destination_path = pipeline_files[0][
366
+ 1
367
+ ] # First item is the pipeline config path
305
368
  ret_file = File.from_path(config_destination_path)
306
369
  ret_file.filename = new_pipeline_uuid
307
370
 
@@ -331,8 +394,10 @@ class Pipeline:
331
394
  repo_path=source_pipeline.repo_path,
332
395
  )
333
396
 
334
- if source_pipeline.type == PipelineType.INTEGRATION and \
335
- source_pipeline.data_integration is not None:
397
+ if (
398
+ source_pipeline.type == PipelineType.INTEGRATION
399
+ and source_pipeline.data_integration is not None
400
+ ):
336
401
  with open(duplicate_pipeline.catalog_config_path, 'w') as fp:
337
402
  json.dump(source_pipeline.data_integration, fp)
338
403
 
@@ -357,7 +422,7 @@ class Pipeline:
357
422
 
358
423
  block_cache = await BlockCache.initialize_cache()
359
424
  for block in blocks:
360
- block_cache.add_pipeline(block, duplicate_pipeline)
425
+ block_cache.add_pipeline(block, duplicate_pipeline, duplicate_pipeline.repo_path)
361
426
 
362
427
  return cls.get(
363
428
  duplicate_pipeline_uuid,
@@ -376,30 +441,46 @@ class Pipeline:
376
441
 
377
442
  @classmethod
378
443
  def get(
379
- self,
444
+ cls,
380
445
  uuid,
381
446
  repo_path: str = None,
447
+ repo_config=None,
382
448
  check_if_exists: bool = False,
383
449
  all_projects: bool = False,
450
+ context_data: Dict = None,
384
451
  use_repo_path: bool = False,
385
452
  ):
386
- from mage_ai.data_preparation.models.pipelines.integration_pipeline import (
387
- IntegrationPipeline,
388
- )
453
+ warn_for_repo_path(repo_path)
389
454
 
390
- config_path, repo_path = self._get_config_path(
455
+ config_path, repo_path = cls._get_config_path(
391
456
  uuid,
392
457
  repo_path=repo_path,
393
458
  all_projects=all_projects,
459
+ context_data=context_data,
394
460
  use_repo_path=use_repo_path,
395
461
  )
396
462
 
397
- if check_if_exists and not os.path.exists(config_path):
463
+ if check_if_exists and (not config_path or not os.path.exists(config_path)):
398
464
  return None
399
465
 
400
- pipeline = self(uuid, repo_path=repo_path, use_repo_path=use_repo_path)
466
+ pipeline = cls(
467
+ uuid,
468
+ repo_path=repo_path,
469
+ repo_config=repo_config,
470
+ context_data=context_data,
471
+ use_repo_path=use_repo_path,
472
+ )
401
473
  if PipelineType.INTEGRATION == pipeline.type:
402
- pipeline = IntegrationPipeline(uuid, repo_path=repo_path)
474
+ from mage_ai.data_preparation.models.pipelines.integration_pipeline import (
475
+ IntegrationPipeline,
476
+ )
477
+
478
+ pipeline = IntegrationPipeline(
479
+ uuid,
480
+ repo_path=repo_path,
481
+ repo_config=repo_config,
482
+ context_data=context_data,
483
+ )
403
484
 
404
485
  return pipeline
405
486
 
@@ -424,7 +505,7 @@ class Pipeline:
424
505
  return None
425
506
 
426
507
  with open(metadata_path) as fp:
427
- config = yaml.full_load(fp) or {}
508
+ config = load_yaml(fp) or {}
428
509
  return config
429
510
 
430
511
  @classmethod
@@ -433,12 +514,17 @@ class Pipeline:
433
514
  uuid,
434
515
  repo_path: str = None,
435
516
  all_projects: bool = False,
517
+ context_data: Dict = None,
436
518
  use_repo_path: bool = False,
437
519
  ) -> Tuple[str, str]:
438
520
  if all_projects and not use_repo_path and project_platform_activated():
439
521
  from mage_ai.settings.platform.utils import get_pipeline_config_path
440
522
 
441
- config_path, repo_path = get_pipeline_config_path(uuid)
523
+ config_path, repo_path = get_pipeline_config_path(
524
+ uuid,
525
+ context_data=context_data,
526
+ repo_path=repo_path,
527
+ )
442
528
  else:
443
529
  repo_path = repo_path or get_repo_path()
444
530
  config_path = os.path.join(
@@ -478,7 +564,7 @@ class Pipeline:
478
564
 
479
565
  config = None
480
566
  async with aiofiles.open(config_path, mode='r') as f:
481
- config = yaml.safe_load(await f.read()) or {}
567
+ config = load_yaml(await f.read()) or {}
482
568
  except Exception as e:
483
569
  if raise_exception:
484
570
  raise e
@@ -495,16 +581,19 @@ class Pipeline:
495
581
  uuid,
496
582
  repo_path: str = None,
497
583
  all_projects: bool = False,
584
+ context_data: Dict = None,
498
585
  use_repo_path: bool = False,
499
586
  ):
500
- from mage_ai.data_preparation.models.pipelines.integration_pipeline import (
501
- IntegrationPipeline,
502
- )
587
+ warn_for_repo_path(repo_path)
503
588
 
504
589
  if all_projects and not use_repo_path and project_platform_activated():
505
590
  from mage_ai.settings.platform.utils import get_pipeline_config_path
506
591
 
507
- config_path, repo_path = get_pipeline_config_path(uuid)
592
+ config_path, repo_path = get_pipeline_config_path(
593
+ uuid,
594
+ context_data=context_data,
595
+ repo_path=repo_path,
596
+ )
508
597
  else:
509
598
  repo_path = repo_path or get_repo_path()
510
599
  config_path = os.path.join(
@@ -514,12 +603,16 @@ class Pipeline:
514
603
  PIPELINE_CONFIG_FILE,
515
604
  )
516
605
 
517
- if not os.path.exists(config_path):
606
+ if not config_path or not os.path.exists(config_path):
518
607
  raise Exception(f'Pipeline {uuid} does not exist.')
519
608
  async with aiofiles.open(config_path, mode='r', encoding='utf-8') as f:
520
- config = yaml.safe_load(await f.read()) or {}
609
+ config = load_yaml(await f.read()) or {}
521
610
 
522
611
  if PipelineType.INTEGRATION == config.get('type'):
612
+ from mage_ai.data_preparation.models.pipelines.integration_pipeline import (
613
+ IntegrationPipeline,
614
+ )
615
+
523
616
  catalog = None
524
617
  catalog_config_path = os.path.join(
525
618
  repo_path,
@@ -553,9 +646,15 @@ class Pipeline:
553
646
  **kwargs,
554
647
  ) -> Union[List[str], List[Tuple[str, str]]]:
555
648
  if project_platform_activated():
556
- repo_paths = [d.get(
557
- 'full_path',
558
- ) for d in build_repo_path_for_all_projects(mage_projects_only=True).values()]
649
+ repo_paths = [
650
+ d.get(
651
+ 'full_path',
652
+ )
653
+ for d in build_repo_path_for_all_projects(
654
+ context_data=kwargs.get('context_data'),
655
+ mage_projects_only=True
656
+ ).values()
657
+ ]
559
658
 
560
659
  return Pipeline.get_all_pipelines(
561
660
  *args,
@@ -571,6 +670,7 @@ class Pipeline:
571
670
  repo_paths: List[str] = None,
572
671
  disable_pipelines_folder_creation: bool = False,
573
672
  include_repo_path: bool = False,
673
+ **kwargs,
574
674
  ) -> Union[List[str], List[Tuple[str, str]]]:
575
675
  arr = []
576
676
 
@@ -580,7 +680,7 @@ class Pipeline:
580
680
  if repo_paths:
581
681
  paths.extend(repo_paths)
582
682
 
583
- for path in paths:
683
+ for path in set(paths):
584
684
  pipelines_folder = os.path.join(path, PIPELINES_FOLDER)
585
685
  pipelines_folder_exists = os.path.exists(pipelines_folder)
586
686
  if not pipelines_folder_exists and not disable_pipelines_folder_creation:
@@ -598,7 +698,10 @@ class Pipeline:
598
698
  return arr
599
699
 
600
700
  @classmethod
601
- def get_pipelines_by_block(self, block, repo_path=None, widget=False) -> List['Pipeline']:
701
+ def get_pipelines_by_block(
702
+ self, block, repo_path: str = None, widget=False
703
+ ) -> List['Pipeline']:
704
+ warn_for_repo_path(repo_path)
602
705
  repo_path = repo_path or get_repo_path()
603
706
  pipelines_folder = os.path.join(repo_path, PIPELINES_FOLDER)
604
707
  pipelines = []
@@ -689,6 +792,7 @@ class Pipeline:
689
792
  from mage_ai.data_preparation.executors.streaming_pipeline_executor import (
690
793
  StreamingPipelineExecutor,
691
794
  )
795
+
692
796
  StreamingPipelineExecutor(self).execute(
693
797
  build_block_output_stdout=build_block_output_stdout,
694
798
  global_vars=global_vars,
@@ -720,7 +824,7 @@ class Pipeline:
720
824
  if not os.path.exists(self.config_path):
721
825
  raise Exception(f'Pipeline {self.uuid} does not exist in repo_path {self.repo_path}.')
722
826
  with open(self.config_path, encoding='utf-8') as fp:
723
- config = yaml.full_load(fp) or {}
827
+ config = load_yaml(fp) or {}
724
828
  return config
725
829
 
726
830
  def get_catalog_from_json(self):
@@ -789,7 +893,9 @@ class Pipeline:
789
893
 
790
894
  language = c.get('language')
791
895
 
792
- return BlockFactory.block_class_from_type(block_type, language=language, pipeline=self)(
896
+ return BlockFactory.block_class_from_type(
897
+ block_type, language=language, pipeline=self
898
+ )(
793
899
  c.get('name'),
794
900
  c.get('uuid'),
795
901
  block_type,
@@ -838,17 +944,28 @@ class Pipeline:
838
944
 
839
945
  for extension_uuid, extension_config in config.get('extensions', {}).items():
840
946
  extension_configs = extension_config.get('blocks') or []
841
- extension_blocks = [build_shared_args_kwargs(merge_dict(c, dict(
842
- extension_uuid=extension_uuid,
843
- ))) for c in extension_configs]
947
+ extension_blocks = [
948
+ build_shared_args_kwargs(
949
+ merge_dict(
950
+ c,
951
+ dict(
952
+ extension_uuid=extension_uuid,
953
+ ),
954
+ )
955
+ )
956
+ for c in extension_configs
957
+ ]
844
958
 
845
- self.extensions[extension_uuid] = merge_dict(extension_config, dict(
846
- blocks_by_uuid=self.__initialize_blocks_by_uuid(
847
- extension_configs,
848
- extension_blocks,
849
- all_blocks,
959
+ self.extensions[extension_uuid] = merge_dict(
960
+ extension_config,
961
+ dict(
962
+ blocks_by_uuid=self.__initialize_blocks_by_uuid(
963
+ extension_configs,
964
+ extension_blocks,
965
+ all_blocks,
966
+ ),
850
967
  ),
851
- ))
968
+ )
852
969
 
853
970
  blocks_with_callbacks = {}
854
971
  for callback_block in self.callbacks_by_uuid.values():
@@ -882,11 +999,13 @@ class Pipeline:
882
999
  for b in configs:
883
1000
  block = blocks_by_uuid[b['uuid']]
884
1001
  block.downstream_blocks = [
885
- all_blocks_by_uuid[uuid] for uuid in b.get('downstream_blocks', [])
1002
+ all_blocks_by_uuid[uuid]
1003
+ for uuid in b.get('downstream_blocks', [])
886
1004
  if uuid in all_blocks_by_uuid
887
1005
  ]
888
1006
  block.upstream_blocks = [
889
- all_blocks_by_uuid[uuid] for uuid in b.get('upstream_blocks', [])
1007
+ all_blocks_by_uuid[uuid]
1008
+ for uuid in b.get('upstream_blocks', [])
890
1009
  if uuid in all_blocks_by_uuid
891
1010
  ]
892
1011
 
@@ -940,7 +1059,9 @@ class Pipeline:
940
1059
 
941
1060
  blocks_data = [b.to_dict(**shared_kwargs) for b in self.blocks_by_uuid.values()]
942
1061
  callbacks_data = [b.to_dict(**shared_kwargs) for b in self.callbacks_by_uuid.values()]
943
- conditionals_data = [b.to_dict(**shared_kwargs) for b in self.conditionals_by_uuid.values()]
1062
+ conditionals_data = [
1063
+ b.to_dict(**shared_kwargs) for b in self.conditionals_by_uuid.values()
1064
+ ]
944
1065
  widgets_data = [b.to_dict(**shared_kwargs) for b in self.widgets_by_uuid.values()]
945
1066
 
946
1067
  data = dict(
@@ -961,13 +1082,17 @@ class Pipeline:
961
1082
  include_outputs=include_outputs,
962
1083
  include_outputs_spark=include_outputs_spark,
963
1084
  sample_count=sample_count,
964
- ) for b in extension['blocks_by_uuid'].values()
1085
+ )
1086
+ for b in extension['blocks_by_uuid'].values()
965
1087
  ]
966
1088
  extensions_data[extension_uuid] = merge_dict(
967
- ignore_keys(extension, [
968
- 'blocks',
969
- 'blocks_by_uuid',
970
- ]),
1089
+ ignore_keys(
1090
+ extension,
1091
+ [
1092
+ 'blocks',
1093
+ 'blocks_by_uuid',
1094
+ ],
1095
+ ),
971
1096
  dict(
972
1097
  blocks=blocks,
973
1098
  ),
@@ -991,7 +1116,10 @@ class Pipeline:
991
1116
  include_extensions: bool = False,
992
1117
  include_outputs: bool = False,
993
1118
  include_outputs_spark: bool = False,
994
- sample_count: int = None,
1119
+ sample_count: Optional[int] = None,
1120
+ disable_block_output_previews: bool = False,
1121
+ exclude_blank_variable_uuids: bool = False,
1122
+ max_results: Optional[int] = None,
995
1123
  ):
996
1124
  shared_kwargs = dict(
997
1125
  check_if_file_exists=True,
@@ -1005,26 +1133,36 @@ class Pipeline:
1005
1133
  )
1006
1134
  if include_block_pipelines:
1007
1135
  shared_kwargs['block_cache'] = BlockCache()
1008
- blocks_data = await asyncio.gather(
1009
- *[b.to_dict_async(**merge_dict(shared_kwargs, dict(
1010
- include_block_catalog=include_block_catalog,
1011
- include_block_pipelines=include_block_pipelines,
1012
- include_outputs_spark=include_outputs_spark,
1013
- ))) for b in self.blocks_by_uuid.values()]
1014
- )
1015
- callbacks_data = await asyncio.gather(
1016
- *[b.to_dict_async(**shared_kwargs) for b in self.callbacks_by_uuid.values()]
1017
- )
1018
- conditionals_data = await asyncio.gather(
1019
- *[b.to_dict_async(**shared_kwargs) for b in self.conditionals_by_uuid.values()]
1020
- )
1021
- widgets_data = await asyncio.gather(
1022
- *[b.to_dict_async(
1136
+ blocks_data = await asyncio.gather(*[
1137
+ b.to_dict_async(
1138
+ **merge_dict(
1139
+ shared_kwargs,
1140
+ dict(
1141
+ include_block_catalog=include_block_catalog,
1142
+ include_block_pipelines=include_block_pipelines,
1143
+ include_outputs_spark=include_outputs_spark,
1144
+ disable_output_preview=disable_block_output_previews,
1145
+ exclude_blank_variable_uuids=exclude_blank_variable_uuids,
1146
+ max_results=max_results,
1147
+ ),
1148
+ )
1149
+ )
1150
+ for b in self.blocks_by_uuid.values()
1151
+ ])
1152
+ callbacks_data = await asyncio.gather(*[
1153
+ b.to_dict_async(**shared_kwargs) for b in self.callbacks_by_uuid.values()
1154
+ ])
1155
+ conditionals_data = await asyncio.gather(*[
1156
+ b.to_dict_async(**shared_kwargs) for b in self.conditionals_by_uuid.values()
1157
+ ])
1158
+ widgets_data = await asyncio.gather(*[
1159
+ b.to_dict_async(
1023
1160
  include_content=include_content,
1024
1161
  include_outputs=include_outputs,
1025
1162
  sample_count=sample_count,
1026
- ) for b in self.widgets_by_uuid.values()]
1027
- )
1163
+ )
1164
+ for b in self.widgets_by_uuid.values()
1165
+ ])
1028
1166
  data = dict(
1029
1167
  blocks=blocks_data,
1030
1168
  callbacks=callbacks_data,
@@ -1037,18 +1175,22 @@ class Pipeline:
1037
1175
  for extension_uuid, extension in self.extensions.items():
1038
1176
  blocks = []
1039
1177
  if 'blocks_by_uuid' in extension:
1040
- blocks = await asyncio.gather(
1041
- *[b.to_dict_async(
1178
+ blocks = await asyncio.gather(*[
1179
+ b.to_dict_async(
1042
1180
  include_content=include_content,
1043
1181
  include_outputs=include_outputs,
1044
1182
  sample_count=sample_count,
1045
- ) for b in extension['blocks_by_uuid'].values()]
1046
- )
1183
+ )
1184
+ for b in extension['blocks_by_uuid'].values()
1185
+ ])
1047
1186
  extensions_data[extension_uuid] = merge_dict(
1048
- ignore_keys(extension, [
1049
- 'blocks',
1050
- 'blocks_by_uuid',
1051
- ]),
1187
+ ignore_keys(
1188
+ extension,
1189
+ [
1190
+ 'blocks',
1191
+ 'blocks_by_uuid',
1192
+ ],
1193
+ ),
1052
1194
  dict(
1053
1195
  blocks=blocks,
1054
1196
  ),
@@ -1086,6 +1228,8 @@ class Pipeline:
1086
1228
  self.uuid = new_uuid
1087
1229
  new_pipeline_path = self.dir_path
1088
1230
  os.rename(old_pipeline_path, new_pipeline_path)
1231
+ # Force updating the config path
1232
+ self._config_path = None
1089
1233
  await self.save_async()
1090
1234
  transfer_related_models_for_pipeline(old_uuid, new_uuid)
1091
1235
 
@@ -1099,7 +1243,7 @@ class Pipeline:
1099
1243
  should_update_tag_cache = True
1100
1244
 
1101
1245
  cache = PipelineCache()
1102
- cache.move_model(dict(uuid=new_uuid), dict(uuid=old_uuid))
1246
+ cache.move_model(dict(uuid=new_uuid), dict(uuid=old_uuid), repo_path=self.repo_path)
1103
1247
 
1104
1248
  should_save = False
1105
1249
 
@@ -1107,11 +1251,12 @@ class Pipeline:
1107
1251
  for extension_uuid, extension in data['extensions'].items():
1108
1252
  if extension_uuid not in self.extensions:
1109
1253
  self.extensions[extension_uuid] = {}
1110
- self.extensions[extension_uuid] = merge_dict(
1111
- self.extensions[extension_uuid],
1112
- extension,
1113
- )
1114
- should_save = True
1254
+ if compare_extension(extension, self.extensions[extension_uuid]):
1255
+ self.extensions[extension_uuid] = merge_dict(
1256
+ self.extensions[extension_uuid],
1257
+ extension,
1258
+ )
1259
+ should_save = True
1115
1260
 
1116
1261
  if 'tags' in data:
1117
1262
  new_tags = data.get('tags', [])
@@ -1137,16 +1282,17 @@ class Pipeline:
1137
1282
 
1138
1283
  for key in [
1139
1284
  'cache_block_output_in_memory',
1285
+ 'concurrency_config',
1140
1286
  'data_integration',
1141
1287
  'executor_type',
1142
1288
  'retry_config',
1143
1289
  'run_pipeline_in_one_process',
1144
1290
  ]:
1145
- if key in data:
1291
+ if key in data and data.get(key) != getattr(self, key):
1146
1292
  setattr(self, key, data.get(key))
1147
1293
  should_save = True
1148
1294
 
1149
- if 'settings' in data:
1295
+ if 'settings' in data and data.get('settings') != self.settings.to_dict():
1150
1296
  self.settings = PipelineSettings.load(**(data.get('settings') or {}))
1151
1297
  should_save = True
1152
1298
 
@@ -1224,7 +1370,7 @@ class Pipeline:
1224
1370
  )
1225
1371
 
1226
1372
  if hooks:
1227
- for hook in (hooks or []):
1373
+ for hook in hooks or []:
1228
1374
  output = hook.output
1229
1375
  if not output:
1230
1376
  continue
@@ -1242,25 +1388,31 @@ class Pipeline:
1242
1388
  old_block_content = await block.content_async()
1243
1389
  if block_data['content'] != old_block_content:
1244
1390
  if cache_block_action_object is None:
1245
- cache_block_action_object = \
1391
+ cache_block_action_object = (
1246
1392
  await BlockActionObjectCache.initialize_cache()
1393
+ )
1247
1394
 
1248
1395
  await block.update_content_async(block_data['content'], widget=widget)
1249
1396
 
1250
1397
  cache_block_action_object.update_block(block)
1251
1398
 
1252
- if 'callback_content' in block_data \
1253
- and block.callback_block:
1399
+ if 'callback_content' in block_data and block.callback_block:
1254
1400
  await block.callback_block.update_content_async(
1255
1401
  block_data['callback_content'],
1256
1402
  widget=widget,
1257
1403
  )
1258
- if 'outputs' in block_data:
1259
- if not is_dynamic_block(block) and not is_dynamic_block_child(block):
1260
- await block.save_outputs_async(
1261
- block_data['outputs'],
1262
- override=True,
1263
- )
1404
+ if (
1405
+ 'outputs' in block_data
1406
+ and not widget
1407
+ and not is_dynamic_block(block)
1408
+ and not is_dynamic_block_child(block)
1409
+ ):
1410
+ if BlockType.SCRATCHPAD == block.type:
1411
+ block.delete_variables()
1412
+ await block.save_outputs_async(
1413
+ block_data['outputs'],
1414
+ override=True,
1415
+ )
1264
1416
 
1265
1417
  name = block_data.get('name')
1266
1418
 
@@ -1272,7 +1424,7 @@ class Pipeline:
1272
1424
  block.update(extract(block_data, ['color']))
1273
1425
 
1274
1426
  configuration = block_data.get('configuration')
1275
- if configuration:
1427
+ if configuration and configuration != block.configuration:
1276
1428
  block.configuration = configuration
1277
1429
  should_save_async = should_save_async or True
1278
1430
 
@@ -1282,42 +1434,50 @@ class Pipeline:
1282
1434
  if name and name != block.name:
1283
1435
  keys_to_update.append('name')
1284
1436
 
1285
- if block_data.get('upstream_blocks'):
1437
+ upstream_blocks = block_data.get('upstream_blocks') or []
1438
+ if upstream_blocks != block.upstream_block_uuids:
1286
1439
  keys_to_update.append('upstream_blocks')
1287
1440
  block_data['upstream_blocks'] = [
1288
- block_uuid_mapping.get(b, b)
1289
- for b in block_data['upstream_blocks']
1441
+ block_uuid_mapping.get(b, b) for b in block_data['upstream_blocks']
1290
1442
  ]
1291
-
1292
1443
  if len(keys_to_update) >= 1:
1293
1444
  block.update(extract(block_data, keys_to_update))
1445
+ should_save_async = should_save_async or True
1294
1446
 
1295
- should_save_async = should_save_async or True
1296
1447
  elif name and name != block.name:
1297
1448
  from mage_ai.cache.block_action_object import (
1298
1449
  BlockActionObjectCache,
1299
1450
  )
1300
1451
 
1301
1452
  if cache_block_action_object is None:
1302
- cache_block_action_object = \
1453
+ cache_block_action_object = (
1303
1454
  await BlockActionObjectCache.initialize_cache()
1455
+ )
1304
1456
  cache_block_action_object.update_block(block, remove=True)
1305
1457
 
1458
+ new_block_uuid = clean_name(name)
1459
+ (
1460
+ old_file_path,
1461
+ old_file_path_relative,
1462
+ ) = block.build_file_path_directory()
1463
+ (
1464
+ new_file_path,
1465
+ new_file_path_relative,
1466
+ ) = block.build_file_path_directory(
1467
+ block_uuid=new_block_uuid,
1468
+ )
1469
+
1306
1470
  block_update_payload = extract(block_data, ['name'])
1307
- configuration = copy.deepcopy(block_data).get('configuration', {})
1308
- file_path = (configuration.get('file_source') or {}).get('path')
1309
- if file_path:
1310
- # Check for block name with period to avoid replacing a directory name
1311
- new_file_path = file_path.replace(
1312
- f'{clean_name(block.name)}.', f'{clean_name(name)}.'
1313
- )
1314
- configuration['file_source']['path'] = new_file_path
1315
- block_update_payload['configuration'] = configuration
1471
+ configuration = copy.deepcopy(block_data).get('configuration', {}) or {}
1472
+ configuration['file_path'] = new_file_path_relative
1473
+
1474
+ if not configuration.get('file_source'):
1475
+ configuration['file_source'] = {}
1476
+ configuration['file_source']['path'] = new_file_path_relative
1477
+ block_update_payload['configuration'] = configuration
1478
+
1316
1479
  blocks_to_remove_from_cache.append(block.to_dict())
1317
- block.update(
1318
- block_update_payload,
1319
- detach=block_data.get('detach', False)
1320
- )
1480
+ block.update(block_update_payload, detach=block_data.get('detach', False))
1321
1481
 
1322
1482
  block_uuids_to_add_to_cache.append(block.uuid)
1323
1483
  cache_block_action_object.update_block(block)
@@ -1335,9 +1495,9 @@ class Pipeline:
1335
1495
  # we need to update mage_sources.yml
1336
1496
  if any(
1337
1497
  (
1338
- BlockType.DBT != block.type and
1339
- block.language in [BlockLanguage.SQL, BlockLanguage.PYTHON, BlockLanguage.R] and
1340
- any(
1498
+ BlockType.DBT != block.type
1499
+ and block.language in [BlockLanguage.SQL, BlockLanguage.PYTHON, BlockLanguage.R]
1500
+ and any(
1341
1501
  BlockType.DBT == downstream_block.type
1342
1502
  for downstream_block in block.downstream_blocks
1343
1503
  )
@@ -1370,7 +1530,7 @@ class Pipeline:
1370
1530
  old_uuid,
1371
1531
  self.repo_path,
1372
1532
  )
1373
- cache.update_pipeline(block.to_dict(), self)
1533
+ cache.update_pipeline(block.to_dict(), self, self.repo_path)
1374
1534
 
1375
1535
  if should_update_tag_cache:
1376
1536
  from mage_ai.cache.tag import TagCache
@@ -1452,8 +1612,7 @@ class Pipeline:
1452
1612
 
1453
1613
  files_to_be_written = []
1454
1614
  with open(config_zip_path, 'r') as pipeline_config:
1455
-
1456
- config = yaml.safe_load(pipeline_config)
1615
+ config = load_yaml(pipeline_config)
1457
1616
 
1458
1617
  # check if pipeline exists with same uuid and generate new one if necessary
1459
1618
  if not overwrite:
@@ -1563,8 +1722,10 @@ class Pipeline:
1563
1722
 
1564
1723
  if block_uuid in self.blocks_by_uuid:
1565
1724
  block = self.blocks_by_uuid[block_uuid]
1566
- if upstream_blocks_reordered is not None and \
1567
- upstream_blocks != upstream_blocks_reordered:
1725
+ if (
1726
+ upstream_blocks_reordered is not None
1727
+ and upstream_blocks != upstream_blocks_reordered
1728
+ ):
1568
1729
  block.update(
1569
1730
  dict(upstream_blocks=upstream_blocks_reordered),
1570
1731
  check_upstream_block_order=True,
@@ -1612,7 +1773,8 @@ class Pipeline:
1612
1773
  all_block_uuids = {b.get('uuid') for b in self.all_block_configs}
1613
1774
  if block.uuid in all_block_uuids:
1614
1775
  raise InvalidPipelineError(
1615
- f'Block with uuid {block.uuid} already exists in pipeline {self.uuid}')
1776
+ f'Block with uuid {block.uuid} already exists in pipeline {self.uuid}'
1777
+ )
1616
1778
 
1617
1779
  if widget:
1618
1780
  self.widgets_by_uuid = self.__add_block_to_mapping(
@@ -1665,9 +1827,8 @@ class Pipeline:
1665
1827
 
1666
1828
  self.update_block(
1667
1829
  downstream_block,
1668
- upstream_block_uuids=(
1669
- downstream_block.upstream_block_uuids or []
1670
- ) + [block.uuid],
1830
+ upstream_block_uuids=(downstream_block.upstream_block_uuids or [])
1831
+ + [block.uuid],
1671
1832
  )
1672
1833
 
1673
1834
  self.validate('A cycle was formed while adding a block')
@@ -1727,9 +1888,13 @@ class Pipeline:
1727
1888
  sample_count: int = None,
1728
1889
  dynamic_block_index: int = None,
1729
1890
  dynamic_block_uuid: str = None,
1891
+ input_data_types: Optional[List[InputDataType]] = None,
1892
+ read_batch_settings: Optional[BatchSettings] = None,
1893
+ read_chunks: Optional[List[ChunkKeyTypeUnion]] = None,
1894
+ write_batch_settings: Optional[BatchSettings] = None,
1895
+ write_chunks: Optional[List[ChunkKeyTypeUnion]] = None,
1730
1896
  ):
1731
1897
  block = self.get_block(block_uuid)
1732
-
1733
1898
  data_integration_settings = block.get_data_integration_settings(
1734
1899
  from_notebook=from_notebook,
1735
1900
  global_vars=global_vars,
@@ -1757,6 +1922,11 @@ class Pipeline:
1757
1922
  variable_uuid=variable_name,
1758
1923
  dynamic_block_index=dynamic_block_index,
1759
1924
  dynamic_block_uuid=dynamic_block_uuid,
1925
+ input_data_types=input_data_types,
1926
+ read_batch_settings=read_batch_settings,
1927
+ read_chunks=read_chunks,
1928
+ write_batch_settings=write_batch_settings,
1929
+ write_chunks=write_chunks,
1760
1930
  )
1761
1931
 
1762
1932
  return variable
@@ -1768,17 +1938,22 @@ class Pipeline:
1768
1938
  def get_executable_blocks(self):
1769
1939
  return [b for b in self.blocks_by_uuid.values() if b.executable]
1770
1940
 
1771
- def get_executor_type(self) -> str:
1772
- if self.executor_type:
1773
- return Template(self.executor_type).render(**get_template_vars())
1774
- return self.executor_type
1941
+ def get_executor_type(self) -> Optional[str]:
1942
+ if self._rendered_executor_type is None:
1943
+ if self.executor_type:
1944
+ return Template(self.executor_type).render(**get_template_vars())
1945
+ else:
1946
+ self._rendered_executor_type = self.executor_type
1947
+ return self._rendered_executor_type
1775
1948
 
1776
1949
  def has_block(self, block_uuid: str, block_type: str = None, extension_uuid: str = None):
1777
1950
  if extension_uuid:
1778
- return self.extensions and \
1779
- extension_uuid in self.extensions and \
1780
- 'blocks_by_uuid' in self.extensions[extension_uuid] and \
1781
- block_uuid in self.extensions[extension_uuid]['blocks_by_uuid']
1951
+ return (
1952
+ self.extensions
1953
+ and extension_uuid in self.extensions
1954
+ and 'blocks_by_uuid' in self.extensions[extension_uuid]
1955
+ and block_uuid in self.extensions[extension_uuid]['blocks_by_uuid']
1956
+ )
1782
1957
  elif BlockType.CALLBACK == block_type:
1783
1958
  return block_uuid in self.callbacks_by_uuid
1784
1959
  elif BlockType.CONDITIONAL == block_type:
@@ -1806,9 +1981,9 @@ class Pipeline:
1806
1981
  if upstream_block_uuids is not None:
1807
1982
  curr_upstream_block_uuids = set(block.upstream_block_uuids)
1808
1983
  new_upstream_block_uuids = set(upstream_block_uuids)
1809
- if curr_upstream_block_uuids != new_upstream_block_uuids or \
1810
- (check_upstream_block_order and
1811
- block.upstream_block_uuids != upstream_block_uuids):
1984
+ if curr_upstream_block_uuids != new_upstream_block_uuids or (
1985
+ check_upstream_block_order and block.upstream_block_uuids != upstream_block_uuids
1986
+ ):
1812
1987
  # Only set upstream block’s downstream to the current block if current block
1813
1988
  # is not an extension block and not a callback/conditional block
1814
1989
  if not is_extension and not is_callback and not is_conditional:
@@ -1867,9 +2042,9 @@ class Pipeline:
1867
2042
  # conditional_blocks field.
1868
2043
  block.update_conditional_blocks(conditional_blocks)
1869
2044
  elif downstream_block_uuids is not None:
1870
- block_uuids_to_remove = \
1871
- [uuid for uuid in block.downstream_block_uuids
1872
- if uuid not in downstream_block_uuids]
2045
+ block_uuids_to_remove = [
2046
+ uuid for uuid in block.downstream_block_uuids if uuid not in downstream_block_uuids
2047
+ ]
1873
2048
 
1874
2049
  for block_uuid in block_uuids_to_remove:
1875
2050
  block_inner = self.get_block(block_uuid)
@@ -1877,10 +2052,12 @@ class Pipeline:
1877
2052
  continue
1878
2053
  block_inner.update(
1879
2054
  dict(
1880
- upstream_blocks=list(filter(
1881
- lambda x, uuid=block.uuid: x != uuid,
1882
- block_inner.upstream_block_uuids or [],
1883
- )),
2055
+ upstream_blocks=list(
2056
+ filter(
2057
+ lambda x, uuid=block.uuid: x != uuid,
2058
+ block_inner.upstream_block_uuids or [],
2059
+ )
2060
+ ),
1884
2061
  ),
1885
2062
  check_upstream_block_order=check_upstream_block_order,
1886
2063
  )
@@ -1988,7 +2165,8 @@ class Pipeline:
1988
2165
  def update_global_variable(self, key, value):
1989
2166
  if not is_yaml_serializable(key, value):
1990
2167
  raise SerializationError(
1991
- f'Failed to update variable {key} because the value is not serializable.')
2168
+ f'Failed to update variable {key} because the value is not serializable.'
2169
+ )
1992
2170
  if self.variables is None:
1993
2171
  self.variables = {}
1994
2172
  self.variables[key] = value
@@ -2051,13 +2229,20 @@ class Pipeline:
2051
2229
  ]
2052
2230
  if self.type == PipelineType.INTEGRATION or force:
2053
2231
  for downstream_block in block.downstream_blocks:
2054
- upstream_block_uuids = list(filter(
2055
- lambda uuid: uuid != block.uuid,
2056
- downstream_block.upstream_block_uuids
2057
- ))
2058
- downstream_block.update(dict(
2059
- upstream_blocks=[*upstream_block_uuids, *block.upstream_block_uuids]
2060
- ))
2232
+ upstream_block_uuids = list(
2233
+ filter(
2234
+ lambda uuid: uuid != block.uuid,
2235
+ downstream_block.upstream_block_uuids,
2236
+ )
2237
+ )
2238
+ downstream_block.update(
2239
+ dict(
2240
+ upstream_blocks=[
2241
+ *upstream_block_uuids,
2242
+ *block.upstream_block_uuids,
2243
+ ]
2244
+ )
2245
+ )
2061
2246
  elif len(downstream_block_uuids) > 0:
2062
2247
  raise HasDownstreamDependencies(
2063
2248
  f'Block(s) {downstream_block_uuids} are depending on block {block.uuid}'
@@ -2107,7 +2292,7 @@ class Pipeline:
2107
2292
  blocks_current = sorted([b.uuid for b in self.blocks_by_uuid.values()])
2108
2293
 
2109
2294
  if block_uuid is not None:
2110
- current_pipeline = Pipeline(self.uuid, self.repo_path)
2295
+ current_pipeline = Pipeline(self.uuid, repo_path=self.repo_path)
2111
2296
  block = self.get_block(
2112
2297
  block_uuid,
2113
2298
  block_type=block_type,
@@ -2163,18 +2348,21 @@ class Pipeline:
2163
2348
  def should_save_trigger_in_code_automatically(self) -> bool:
2164
2349
  from mage_ai.data_preparation.models.project import Project
2165
2350
 
2166
- if self.settings and \
2167
- self.settings.triggers and \
2168
- self.settings.triggers.save_in_code_automatically is not None:
2169
-
2351
+ if (
2352
+ self.settings
2353
+ and self.settings.triggers
2354
+ and self.settings.triggers.save_in_code_automatically is not None
2355
+ ):
2170
2356
  return self.settings.triggers.save_in_code_automatically
2171
2357
 
2172
- project = Project(self.repo_config)
2358
+ project = Project(repo_config=self.repo_config)
2173
2359
 
2174
- return project.pipelines and \
2175
- project.pipelines.settings and \
2176
- project.pipelines.settings.triggers and \
2177
- project.pipelines.settings.triggers.save_in_code_automatically
2360
+ return (
2361
+ project.pipelines
2362
+ and project.pipelines.settings
2363
+ and project.pipelines.settings.triggers
2364
+ and project.pipelines.settings.triggers.save_in_code_automatically
2365
+ )
2178
2366
 
2179
2367
  async def save_async(
2180
2368
  self,
@@ -2231,7 +2419,7 @@ class Pipeline:
2231
2419
  success = True
2232
2420
  with open(test_path, mode='r', encoding='utf-8') as fp:
2233
2421
  try:
2234
- yaml.full_load(fp)
2422
+ load_yaml(fp)
2235
2423
  except yaml.scanner.ScannerError:
2236
2424
  success = False
2237
2425
 
@@ -2281,7 +2469,7 @@ class Pipeline:
2281
2469
  index += 1
2282
2470
 
2283
2471
  cycle = [frame.uuid for frame in virtual_stack[index:]]
2284
- return " --> ".join(cycle)
2472
+ return ' --> '.join(cycle)
2285
2473
 
2286
2474
  def __check_cycle(block: Block):
2287
2475
  virtual_stack = [StackFrame(block)]
@@ -2312,7 +2500,8 @@ class Pipeline:
2312
2500
  uuid = config.get('uuid')
2313
2501
  if uuid in check_block_uuids:
2314
2502
  raise InvalidPipelineError(
2315
- f'Pipeline is invalid: duplicate blocks with uuid {uuid}')
2503
+ f'Pipeline is invalid: duplicate blocks with uuid {uuid}'
2504
+ )
2316
2505
  check_block_uuids.add(uuid)
2317
2506
 
2318
2507