Просмотр исходного кода

Merge branch 'develop' into feature/change-lock-type

Jean-Robin 1 год назад
Родитель
Сommit
2b29e8b1bd
100 измененных файлов с 688 добавлено и 656 удалено
  1. 7 4
      .github/workflows/build-and-release-dev.yml
  2. 1 1
      .github/workflows/build-and-release-prod.yml
  3. 1 1
      .github/workflows/publish.yml
  4. 12 0
      frontend/taipy-gui/base/src/app.ts
  5. 8 0
      frontend/taipy-gui/base/src/utils.ts
  6. 132 103
      frontend/taipy-gui/package-lock.json
  7. 9 8
      frontend/taipy-gui/src/components/Taipy/Chart.tsx
  8. 16 1
      frontend/taipy-gui/src/components/Taipy/Selector.spec.tsx
  9. 3 1
      frontend/taipy-gui/src/components/Taipy/Selector.tsx
  10. 112 113
      frontend/taipy/package-lock.json
  11. 6 2
      taipy/config/common/_config_blocker.py
  12. 1 0
      taipy/config/setup.py
  13. 1 1
      taipy/config/version.json
  14. 33 15
      taipy/core/_core.py
  15. 2 0
      taipy/core/_init.py
  16. 2 2
      taipy/core/_version/_cli/_version_cli.py
  17. 2 2
      taipy/core/_version/_version_manager.py
  18. 2 2
      taipy/core/common/_warnings.py
  19. 2 0
      taipy/core/config/core_section.py
  20. 1 1
      taipy/core/data/data_node.py
  21. 0 9
      taipy/core/data/sql_table.py
  22. 1 0
      taipy/core/setup.py
  23. 39 11
      taipy/core/taipy.py
  24. 1 1
      taipy/core/version.json
  25. 2 2
      taipy/gui/_renderers/builder.py
  26. 1 1
      taipy/gui/_renderers/factory.py
  27. 2 2
      taipy/gui/_renderers/utils.py
  28. 5 5
      taipy/gui/_warnings.py
  29. 2 2
      taipy/gui/data/decimator/rdp.py
  30. 1 1
      taipy/gui/data/pandas_data_accessor.py
  31. 1 1
      taipy/gui/extension/__init__.py
  32. 1 1
      taipy/gui/extension/library.py
  33. 20 4
      taipy/gui/gui.py
  34. 1 3
      taipy/gui/page.py
  35. 5 1
      taipy/gui/partial.py
  36. 1 0
      taipy/gui/setup.py
  37. 2 4
      taipy/gui/types.py
  38. 3 1
      taipy/gui/utils/_locals_context.py
  39. 1 3
      taipy/gui/utils/boolean.py
  40. 6 6
      taipy/gui/utils/chart_config_builder.py
  41. 1 1
      taipy/gui/version.json
  42. 7 5
      taipy/gui_core/_context.py
  43. 2 2
      taipy/rest/__init__.py
  44. 1 0
      taipy/rest/setup.py
  45. 1 1
      taipy/rest/version.json
  46. 1 0
      taipy/templates/setup.py
  47. 1 1
      taipy/templates/version.json
  48. 1 1
      taipy/version.json
  49. 2 4
      tests/core/_backup/test_backup.py
  50. 1 0
      tests/core/_orchestrator/test_orchestrator__cancel_jobs.py
  51. 4 4
      tests/core/_orchestrator/test_orchestrator__is_blocked.py
  52. 2 2
      tests/core/_orchestrator/test_orchestrator__lock_dn_output_and_create_job.py
  53. 3 2
      tests/core/_orchestrator/test_orchestrator__orchestrate_job_to_run_or_block.py
  54. 1 9
      tests/core/config/checkers/test_migration_config_checker.py
  55. 4 1
      tests/core/conftest.py
  56. 12 12
      tests/core/data/test_data_manager.py
  57. 2 1
      tests/core/notification/test_core_event_consumer.py
  58. 6 4
      tests/core/notification/test_events_published.py
  59. 9 7
      tests/core/notification/test_notifier.py
  60. 23 26
      tests/core/test_complex_application.py
  61. 59 66
      tests/core/test_core.py
  62. 19 14
      tests/core/test_taipy.py
  63. 1 3
      tests/gui/actions/test_download.py
  64. 1 3
      tests/gui/actions/test_get_state_id.py
  65. 1 3
      tests/gui/actions/test_hold_control.py
  66. 1 3
      tests/gui/actions/test_invoke_callback.py
  67. 1 3
      tests/gui/actions/test_navigate.py
  68. 1 3
      tests/gui/actions/test_notify.py
  69. 1 3
      tests/gui/actions/test_resume_control.py
  70. 7 0
      tests/gui/conftest.py
  71. 1 3
      tests/gui/data/test_pandas_data_accessor.py
  72. 1 3
      tests/gui/gui_specific/test_folder_pages_binding.py
  73. 7 15
      tests/gui/gui_specific/test_gui.py
  74. 1 3
      tests/gui/gui_specific/test_locals_context.py
  75. 2 4
      tests/gui/gui_specific/test_multiple_instances.py
  76. 4 9
      tests/gui/gui_specific/test_navigate.py
  77. 2 5
      tests/gui/gui_specific/test_partial.py
  78. 1 3
      tests/gui/gui_specific/test_render_route.py
  79. 2 5
      tests/gui/gui_specific/test_run_thread.py
  80. 1 3
      tests/gui/gui_specific/test_state.py
  81. 3 7
      tests/gui/gui_specific/test_variable_binding.py
  82. 3 7
      tests/gui/helpers.py
  83. 1 3
      tests/gui/ignore/no_file/test_ignore.py
  84. 2 5
      tests/gui/ignore/with_file/test_with_ignore.py
  85. 1 3
      tests/gui/long_runnig/test_long_running.py
  86. 1 3
      tests/gui/renderers/test_html_parsing.py
  87. 2 5
      tests/gui/server/http/test_extension.py
  88. 6 13
      tests/gui/server/http/test_file_upload.py
  89. 2 5
      tests/gui/server/http/test_image_path.py
  90. 3 7
      tests/gui/server/http/test_status.py
  91. 3 7
      tests/gui/server/http/test_user_content.py
  92. 1 3
      tests/gui/server/ws/test_a.py
  93. 1 3
      tests/gui/server/ws/test_broadcast.py
  94. 1 3
      tests/gui/server/ws/test_df.py
  95. 1 3
      tests/gui/server/ws/test_du.py
  96. 2 5
      tests/gui/server/ws/test_on_change.py
  97. 1 3
      tests/gui/server/ws/test_ru.py
  98. 1 3
      tests/gui/server/ws/test_u.py
  99. 1 3
      tests/gui/server/ws/test_with.py
  100. 6 13
      tests/gui/utils/test_evaluator.py

+ 7 - 4
.github/workflows/build-and-release-dev.yml

@@ -122,6 +122,8 @@ jobs:
         uses: pypa/gh-action-pypi-publish@release/v1
 
   build-and-release-taipy-dev:
+    permissions:
+      id-token: write  # IMPORTANT: this permission is mandatory for trusted publishing
     runs-on: ubuntu-latest
     needs: [ build-and-release-taipy-dev-packages, fetch-versions ]
     timeout-minutes: 20
@@ -193,11 +195,12 @@ jobs:
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 
+      - uses: stefanzweifel/git-auto-commit-action@v4
+        with:
+          file_pattern: '*/version.json'
+          commit_message: Update version to ${{ needs.fetch-versions.outputs.NEW_VERSION }}
+
       - name: Reset changes
         run: |
           git reset --hard HEAD
           git clean -fdx
-
-      - uses: stefanzweifel/git-auto-commit-action@v4
-        with:
-          commit_message: Update version to ${{ needs.fetch-versions.outputs.NEW_VERSION }}

+ 1 - 1
.github/workflows/build-and-release-prod.yml

@@ -1,4 +1,4 @@
-name: Build a dev version for all packages and release them
+name: Build a prod version for all packages and release them
 
 on:
   workflow_dispatch:

+ 1 - 1
.github/workflows/publish.yml

@@ -48,7 +48,7 @@ jobs:
           sparse-checkout: taipy/${{ matrix.package }}
           sparse-checkout-cone-mode: false
 
-      - name: Checks if package is already on on Pypi
+      - name: Checks if package is already on Pypi
         id: check-version
         run: |
           if curl https://pypi.org/simple/taipy-${{ matrix.package }} | grep -o ">taipy-${{ matrix.package }}-${{ github.event.inputs.version }}\.tar\.gz<"; then

+ 12 - 0
frontend/taipy-gui/base/src/app.ts

@@ -8,11 +8,13 @@ import { initSocket } from "./utils";
 
 export type OnInitHandler = (appManager: TaipyApp) => void;
 export type OnChangeHandler = (appManager: TaipyApp, encodedName: string, value: unknown) => void;
+export type OnNotifyHandler = (appManager: TaipyApp, type: string, message: string) => void;
 
 export class TaipyApp {
     socket: Socket;
     _onInit: OnInitHandler | undefined;
     _onChange: OnChangeHandler | undefined;
+    _onNotify: OnNotifyHandler | undefined;
     variableData: DataManager | undefined;
     functionData: DataManager | undefined;
     appId: string;
@@ -60,6 +62,16 @@ export class TaipyApp {
         this._onChange = handler;
     }
 
+    get onNotify() {
+        return this._onNotify;
+    }
+    set onNotify(handler: OnNotifyHandler | undefined) {
+        if (handler !== undefined && handler?.length !== 3) {
+            throw new Error("onNotify() requires three parameters");
+        }
+        this._onNotify = handler;
+    }
+
     // Utility methods
     init() {
         this.clientId = "";

+ 8 - 0
frontend/taipy-gui/base/src/utils.ts

@@ -9,6 +9,11 @@ interface MultipleUpdatePayload {
     payload: { value: unknown };
 }
 
+interface AlertMessage extends WsMessage {
+    atype: string;
+    message: string;
+}
+
 const initWsMessageTypes = ["ID", "AID", "GMC"];
 
 export const initSocket = (socket: Socket, appManager: TaipyApp) => {
@@ -82,6 +87,9 @@ const processWsMessage = (message: WsMessage, appManager: TaipyApp) => {
                 return appManager.init();
             }
             appManager.appId = payload.id as string;
+        } else if (message.type === "AL" && appManager.onNotify) {
+            const payload = message as AlertMessage;
+            appManager.onNotify(appManager, payload.atype, payload.message);
         }
         postWsMessageProcessing(message, appManager);
     }

+ 132 - 103
frontend/taipy-gui/package-lock.json

@@ -840,9 +840,9 @@
       "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ=="
     },
     "node_modules/@emotion/is-prop-valid": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz",
-      "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==",
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz",
+      "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==",
       "dependencies": {
         "@emotion/memoize": "^0.8.1"
       }
@@ -853,9 +853,9 @@
       "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA=="
     },
     "node_modules/@emotion/react": {
-      "version": "11.11.3",
-      "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.3.tgz",
-      "integrity": "sha512-Cnn0kuq4DoONOMcnoVsTOR8E+AdnKFf//6kUWc4LCdnxj31pZWn7rIULd6Y7/Js1PiPHzn7SKCM9vB/jBni8eA==",
+      "version": "11.11.4",
+      "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz",
+      "integrity": "sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==",
       "dependencies": {
         "@babel/runtime": "^7.18.3",
         "@emotion/babel-plugin": "^11.11.0",
@@ -1007,9 +1007,9 @@
       }
     },
     "node_modules/@eslint/js": {
-      "version": "8.56.0",
-      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz",
-      "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==",
+      "version": "8.57.0",
+      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz",
+      "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==",
       "dev": true,
       "engines": {
         "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@@ -1529,9 +1529,9 @@
       }
     },
     "node_modules/@jridgewell/gen-mapping": {
-      "version": "0.3.3",
-      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
-      "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
+      "version": "0.3.4",
+      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.4.tgz",
+      "integrity": "sha512-Oud2QPM5dHviZNn4y/WhhYKSXksv+1xLEIsNrAbGcFzUN3ubqWRFT5gwPchNc5NuzILOU4tPBDTZ4VwhL8Y7cw==",
       "dev": true,
       "dependencies": {
         "@jridgewell/set-array": "^1.0.1",
@@ -1577,9 +1577,9 @@
       "dev": true
     },
     "node_modules/@jridgewell/trace-mapping": {
-      "version": "0.3.22",
-      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz",
-      "integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==",
+      "version": "0.3.23",
+      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.23.tgz",
+      "integrity": "sha512-9/4foRoUKp8s96tSkh8DlAAc5A0Ty8vLXld+l9gjKKY6ckwI8G15f0hskGmuLZu78ZlGa1vtsfOa+lnB4vG6Jg==",
       "dev": true,
       "dependencies": {
         "@jridgewell/resolve-uri": "^3.1.0",
@@ -1682,14 +1682,14 @@
       }
     },
     "node_modules/@mui/base": {
-      "version": "5.0.0-beta.36",
-      "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.36.tgz",
-      "integrity": "sha512-6A8fYiXgjqTO6pgj31Hc8wm1M3rFYCxDRh09dBVk0L0W4cb2lnurRJa3cAyic6hHY+we1S58OdGYRbKmOsDpGQ==",
+      "version": "5.0.0-beta.37",
+      "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.37.tgz",
+      "integrity": "sha512-/o3anbb+DeCng8jNsd3704XtmmLDZju1Fo8R2o7ugrVtPQ/QpcqddwKNzKPZwa0J5T8YNW3ZVuHyQgbTnQLisQ==",
       "dependencies": {
         "@babel/runtime": "^7.23.9",
         "@floating-ui/react-dom": "^2.0.8",
         "@mui/types": "^7.2.13",
-        "@mui/utils": "^5.15.9",
+        "@mui/utils": "^5.15.11",
         "@popperjs/core": "^2.11.8",
         "clsx": "^2.1.0",
         "prop-types": "^15.8.1"
@@ -1722,9 +1722,9 @@
       }
     },
     "node_modules/@mui/icons-material": {
-      "version": "5.15.10",
-      "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.10.tgz",
-      "integrity": "sha512-9cF8oUHZKo9oQ7EQ3pxPELaZuZVmphskU4OI6NiJNDVN7zcuvrEsuWjYo1Zh4fLiC39Nrvm30h/B51rcUjvSGA==",
+      "version": "5.15.11",
+      "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.11.tgz",
+      "integrity": "sha512-R5ZoQqnKpd+5Ew7mBygTFLxgYsQHPhgR3TDXSgIHYIjGzYuyPLmGLSdcPUoMdi6kxiYqHlpPj4NJxlbaFD0UHA==",
       "dependencies": {
         "@babel/runtime": "^7.23.9"
       },
@@ -1747,16 +1747,16 @@
       }
     },
     "node_modules/@mui/material": {
-      "version": "5.15.10",
-      "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.10.tgz",
-      "integrity": "sha512-YJJGHjwDOucecjDEV5l9ISTCo+l9YeWrho623UajzoHRYxuKUmwrGVYOW4PKwGvCx9SU9oklZnbbi2Clc5XZHw==",
+      "version": "5.15.11",
+      "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.11.tgz",
+      "integrity": "sha512-FA3eEuEZaDaxgN3CgfXezMWbCZ4VCeU/sv0F0/PK5n42qIgsPVD6q+j71qS7/62sp6wRFMHtDMpXRlN+tT/7NA==",
       "dependencies": {
         "@babel/runtime": "^7.23.9",
-        "@mui/base": "5.0.0-beta.36",
-        "@mui/core-downloads-tracker": "^5.15.10",
-        "@mui/system": "^5.15.9",
+        "@mui/base": "5.0.0-beta.37",
+        "@mui/core-downloads-tracker": "^5.15.11",
+        "@mui/system": "^5.15.11",
         "@mui/types": "^7.2.13",
-        "@mui/utils": "^5.15.9",
+        "@mui/utils": "^5.15.11",
         "@types/react-transition-group": "^4.4.10",
         "clsx": "^2.1.0",
         "csstype": "^3.1.3",
@@ -1848,15 +1848,15 @@
       }
     },
     "node_modules/@mui/system": {
-      "version": "5.15.9",
-      "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.9.tgz",
-      "integrity": "sha512-SxkaaZ8jsnIJ77bBXttfG//LUf6nTfOcaOuIgItqfHv60ZCQy/Hu7moaob35kBb+guxVJnoSZ+7vQJrA/E7pKg==",
+      "version": "5.15.11",
+      "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.11.tgz",
+      "integrity": "sha512-9j35suLFq+MgJo5ktVSHPbkjDLRMBCV17NMBdEQurh6oWyGnLM4uhU4QGZZQ75o0vuhjJghOCA1jkO3+79wKsA==",
       "dependencies": {
         "@babel/runtime": "^7.23.9",
-        "@mui/private-theming": "^5.15.9",
-        "@mui/styled-engine": "^5.15.9",
+        "@mui/private-theming": "^5.15.11",
+        "@mui/styled-engine": "^5.15.11",
         "@mui/types": "^7.2.13",
-        "@mui/utils": "^5.15.9",
+        "@mui/utils": "^5.15.11",
         "clsx": "^2.1.0",
         "csstype": "^3.1.3",
         "prop-types": "^15.8.1"
@@ -2627,9 +2627,9 @@
       "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng=="
     },
     "node_modules/@types/react": {
-      "version": "18.2.58",
-      "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.58.tgz",
-      "integrity": "sha512-TaGvMNhxvG2Q0K0aYxiKfNDS5m5ZsoIBBbtfUorxdH4NGSXIlYvZxLJI+9Dd3KjeB3780bciLyAb7ylO8pLhPw==",
+      "version": "18.2.60",
+      "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.60.tgz",
+      "integrity": "sha512-dfiPj9+k20jJrLGOu9Nf6eqxm2EyJRrq2NvwOFsfbb7sFExZ9WELPs67UImHj3Ayxg8ruTtKtNnbjaF8olPq0A==",
       "dependencies": {
         "@types/prop-types": "*",
         "@types/scheduler": "*",
@@ -2718,9 +2718,9 @@
       "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A=="
     },
     "node_modules/@types/semver": {
-      "version": "7.5.7",
-      "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.7.tgz",
-      "integrity": "sha512-/wdoPq1QqkSj9/QOeKkFquEuPzQbHTWAMPH/PaUMB+JuR31lXhlWXRZ52IpfDYVlDOUBvX09uBrPwxGT1hjNBg==",
+      "version": "7.5.8",
+      "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
+      "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==",
       "dev": true
     },
     "node_modules/@types/sprintf-js": {
@@ -2767,16 +2767,16 @@
       "dev": true
     },
     "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "7.0.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.0.2.tgz",
-      "integrity": "sha512-/XtVZJtbaphtdrWjr+CJclaCVGPtOdBpFEnvtNf/jRV0IiEemRrL0qABex/nEt8isYcnFacm3nPHYQwL+Wb7qg==",
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.1.0.tgz",
+      "integrity": "sha512-j6vT/kCulhG5wBmGtstKeiVr1rdXE4nk+DT1k6trYkwlrvW9eOF5ZbgKnd/YR6PcM4uTEXa0h6Fcvf6X7Dxl0w==",
       "dev": true,
       "dependencies": {
         "@eslint-community/regexpp": "^4.5.1",
-        "@typescript-eslint/scope-manager": "7.0.2",
-        "@typescript-eslint/type-utils": "7.0.2",
-        "@typescript-eslint/utils": "7.0.2",
-        "@typescript-eslint/visitor-keys": "7.0.2",
+        "@typescript-eslint/scope-manager": "7.1.0",
+        "@typescript-eslint/type-utils": "7.1.0",
+        "@typescript-eslint/utils": "7.1.0",
+        "@typescript-eslint/visitor-keys": "7.1.0",
         "debug": "^4.3.4",
         "graphemer": "^1.4.0",
         "ignore": "^5.2.4",
@@ -2802,15 +2802,15 @@
       }
     },
     "node_modules/@typescript-eslint/parser": {
-      "version": "7.0.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.0.2.tgz",
-      "integrity": "sha512-GdwfDglCxSmU+QTS9vhz2Sop46ebNCXpPPvsByK7hu0rFGRHL+AusKQJ7SoN+LbLh6APFpQwHKmDSwN35Z700Q==",
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.1.0.tgz",
+      "integrity": "sha512-V1EknKUubZ1gWFjiOZhDSNToOjs63/9O0puCgGS8aDOgpZY326fzFu15QAUjwaXzRZjf/qdsdBrckYdv9YxB8w==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/scope-manager": "7.0.2",
-        "@typescript-eslint/types": "7.0.2",
-        "@typescript-eslint/typescript-estree": "7.0.2",
-        "@typescript-eslint/visitor-keys": "7.0.2",
+        "@typescript-eslint/scope-manager": "7.1.0",
+        "@typescript-eslint/types": "7.1.0",
+        "@typescript-eslint/typescript-estree": "7.1.0",
+        "@typescript-eslint/visitor-keys": "7.1.0",
         "debug": "^4.3.4"
       },
       "engines": {
@@ -2830,13 +2830,13 @@
       }
     },
     "node_modules/@typescript-eslint/scope-manager": {
-      "version": "7.0.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.0.2.tgz",
-      "integrity": "sha512-l6sa2jF3h+qgN2qUMjVR3uCNGjWw4ahGfzIYsCtFrQJCjhbrDPdiihYT8FnnqFwsWX+20hK592yX9I2rxKTP4g==",
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.1.0.tgz",
+      "integrity": "sha512-6TmN4OJiohHfoOdGZ3huuLhpiUgOGTpgXNUPJgeZOZR3DnIpdSgtt83RS35OYNNXxM4TScVlpVKC9jyQSETR1A==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.0.2",
-        "@typescript-eslint/visitor-keys": "7.0.2"
+        "@typescript-eslint/types": "7.1.0",
+        "@typescript-eslint/visitor-keys": "7.1.0"
       },
       "engines": {
         "node": "^16.0.0 || >=18.0.0"
@@ -2847,13 +2847,13 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils": {
-      "version": "7.0.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.0.2.tgz",
-      "integrity": "sha512-IKKDcFsKAYlk8Rs4wiFfEwJTQlHcdn8CLwLaxwd6zb8HNiMcQIFX9sWax2k4Cjj7l7mGS5N1zl7RCHOVwHq2VQ==",
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.1.0.tgz",
+      "integrity": "sha512-UZIhv8G+5b5skkcuhgvxYWHjk7FW7/JP5lPASMEUoliAPwIH/rxoUSQPia2cuOj9AmDZmwUl1usKm85t5VUMew==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/typescript-estree": "7.0.2",
-        "@typescript-eslint/utils": "7.0.2",
+        "@typescript-eslint/typescript-estree": "7.1.0",
+        "@typescript-eslint/utils": "7.1.0",
         "debug": "^4.3.4",
         "ts-api-utils": "^1.0.1"
       },
@@ -2874,9 +2874,9 @@
       }
     },
     "node_modules/@typescript-eslint/types": {
-      "version": "7.0.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.0.2.tgz",
-      "integrity": "sha512-ZzcCQHj4JaXFjdOql6adYV4B/oFOFjPOC9XYwCaZFRvqN8Llfvv4gSxrkQkd2u4Ci62i2c6W6gkDwQJDaRc4nA==",
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.1.0.tgz",
+      "integrity": "sha512-qTWjWieJ1tRJkxgZYXx6WUYtWlBc48YRxgY2JN1aGeVpkhmnopq+SUC8UEVGNXIvWH7XyuTjwALfG6bFEgCkQA==",
       "dev": true,
       "engines": {
         "node": "^16.0.0 || >=18.0.0"
@@ -2887,13 +2887,13 @@
       }
     },
     "node_modules/@typescript-eslint/typescript-estree": {
-      "version": "7.0.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.0.2.tgz",
-      "integrity": "sha512-3AMc8khTcELFWcKcPc0xiLviEvvfzATpdPj/DXuOGIdQIIFybf4DMT1vKRbuAEOFMwhWt7NFLXRkbjsvKZQyvw==",
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.1.0.tgz",
+      "integrity": "sha512-k7MyrbD6E463CBbSpcOnwa8oXRdHzH1WiVzOipK3L5KSML92ZKgUBrTlehdi7PEIMT8k0bQixHUGXggPAlKnOQ==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.0.2",
-        "@typescript-eslint/visitor-keys": "7.0.2",
+        "@typescript-eslint/types": "7.1.0",
+        "@typescript-eslint/visitor-keys": "7.1.0",
         "debug": "^4.3.4",
         "globby": "^11.1.0",
         "is-glob": "^4.0.3",
@@ -2915,17 +2915,17 @@
       }
     },
     "node_modules/@typescript-eslint/utils": {
-      "version": "7.0.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.0.2.tgz",
-      "integrity": "sha512-PZPIONBIB/X684bhT1XlrkjNZJIEevwkKDsdwfiu1WeqBxYEEdIgVDgm8/bbKHVu+6YOpeRqcfImTdImx/4Bsw==",
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.1.0.tgz",
+      "integrity": "sha512-WUFba6PZC5OCGEmbweGpnNJytJiLG7ZvDBJJoUcX4qZYf1mGZ97mO2Mps6O2efxJcJdRNpqweCistDbZMwIVHw==",
       "dev": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.4.0",
         "@types/json-schema": "^7.0.12",
         "@types/semver": "^7.5.0",
-        "@typescript-eslint/scope-manager": "7.0.2",
-        "@typescript-eslint/types": "7.0.2",
-        "@typescript-eslint/typescript-estree": "7.0.2",
+        "@typescript-eslint/scope-manager": "7.1.0",
+        "@typescript-eslint/types": "7.1.0",
+        "@typescript-eslint/typescript-estree": "7.1.0",
         "semver": "^7.5.4"
       },
       "engines": {
@@ -2940,12 +2940,12 @@
       }
     },
     "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "7.0.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.0.2.tgz",
-      "integrity": "sha512-8Y+YiBmqPighbm5xA2k4wKTxRzx9EkBu7Rlw+WHqMvRJ3RPz/BMBO9b2ru0LUNmXg120PHUXD5+SWFy2R8DqlQ==",
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.1.0.tgz",
+      "integrity": "sha512-FhUqNWluiGNzlvnDZiXad4mZRhtghdoKW6e98GoEOYSu5cND+E39rG5KwJMUzeENwm1ztYBRqof8wMLP+wNPIA==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.0.2",
+        "@typescript-eslint/types": "7.1.0",
         "eslint-visitor-keys": "^3.4.1"
       },
       "engines": {
@@ -3963,9 +3963,9 @@
       }
     },
     "node_modules/caniuse-lite": {
-      "version": "1.0.30001589",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001589.tgz",
-      "integrity": "sha512-vNQWS6kI+q6sBlHbh71IIeC+sRwK2N3EDySc/updIGhIee2x5z00J4c1242/5/d6EpEMdOnk/m+6tuk4/tcsqg==",
+      "version": "1.0.30001591",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001591.tgz",
+      "integrity": "sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ==",
       "funding": [
         {
           "type": "opencollective",
@@ -5334,9 +5334,9 @@
       "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ=="
     },
     "node_modules/electron-to-chromium": {
-      "version": "1.4.680",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.680.tgz",
-      "integrity": "sha512-4nToZ5jlPO14W82NkF32wyjhYqQByVaDmLy4J2/tYcAbJfgO2TKJC780Az1V13gzq4l73CJ0yuyalpXvxXXD9A=="
+      "version": "1.4.682",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.682.tgz",
+      "integrity": "sha512-oCglfs8yYKs9RQjJFOHonSnhikPK3y+0SvSYc/YpYJV//6rqc0/hbwd0c7vgK4vrl6y2gJAwjkhkSGWK+z4KRA=="
     },
     "node_modules/element-size": {
       "version": "1.1.1",
@@ -5631,13 +5631,14 @@
       }
     },
     "node_modules/es5-ext": {
-      "version": "0.10.62",
-      "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz",
-      "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==",
+      "version": "0.10.63",
+      "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.63.tgz",
+      "integrity": "sha512-hUCZd2Byj/mNKjfP9jXrdVZ62B8KuA/VoK7X8nUh5qT+AxDmcbvZz041oDVZdbIN1qW6XY9VDNwzkvKnZvK2TQ==",
       "hasInstallScript": true,
       "dependencies": {
         "es6-iterator": "^2.0.3",
         "es6-symbol": "^3.1.3",
+        "esniff": "^2.0.1",
         "next-tick": "^1.1.0"
       },
       "engines": {
@@ -5723,16 +5724,16 @@
       }
     },
     "node_modules/eslint": {
-      "version": "8.56.0",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz",
-      "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==",
+      "version": "8.57.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
+      "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
       "dev": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.2.0",
         "@eslint-community/regexpp": "^4.6.1",
         "@eslint/eslintrc": "^2.1.4",
-        "@eslint/js": "8.56.0",
-        "@humanwhocodes/config-array": "^0.11.13",
+        "@eslint/js": "8.57.0",
+        "@humanwhocodes/config-array": "^0.11.14",
         "@humanwhocodes/module-importer": "^1.0.1",
         "@nodelib/fs.walk": "^1.2.8",
         "@ungap/structured-clone": "^1.2.0",
@@ -5963,6 +5964,25 @@
         "node": "*"
       }
     },
+    "node_modules/esniff": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz",
+      "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==",
+      "dependencies": {
+        "d": "^1.0.1",
+        "es5-ext": "^0.10.62",
+        "event-emitter": "^0.3.5",
+        "type": "^2.7.2"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/esniff/node_modules/type": {
+      "version": "2.7.2",
+      "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz",
+      "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw=="
+    },
     "node_modules/espree": {
       "version": "9.6.1",
       "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
@@ -6041,6 +6061,15 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/event-emitter": {
+      "version": "0.3.5",
+      "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
+      "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==",
+      "dependencies": {
+        "d": "1",
+        "es5-ext": "~0.10.14"
+      }
+    },
     "node_modules/eventemitter3": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
@@ -11455,9 +11484,9 @@
       }
     },
     "node_modules/react-jsx-parser/node_modules/@types/react": {
-      "version": "17.0.75",
-      "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.75.tgz",
-      "integrity": "sha512-MSA+NzEzXnQKrqpO63CYqNstFjsESgvJAdAyyJ1n6ZQq/GLgf6nOfIKwk+Twuz0L1N6xPe+qz5xRCJrbhMaLsw==",
+      "version": "17.0.76",
+      "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.76.tgz",
+      "integrity": "sha512-w9Aq+qeszGYoQM0hgFcdsAODGJdogadBDiitPm+zjBFJ0mLymvn2qSXsDaLJUndFRqqXk1FQfa9avHUBk1JhJQ==",
       "optional": true,
       "dependencies": {
         "@types/prop-types": "*",
@@ -13346,9 +13375,9 @@
       }
     },
     "node_modules/typedoc": {
-      "version": "0.25.8",
-      "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.8.tgz",
-      "integrity": "sha512-mh8oLW66nwmeB9uTa0Bdcjfis+48bAjSH3uqdzSuSawfduROQLlXw//WSNZLYDdhmMVB7YcYZicq6e8T0d271A==",
+      "version": "0.25.9",
+      "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.9.tgz",
+      "integrity": "sha512-jVoGmfNw848iW0L313+jqHbsknepwDV6F9nzk1H30oWhKXkw65uaENgR6QtTw9a5KqRWEb6nwNd54KxffBJyWw==",
       "dev": true,
       "dependencies": {
         "lunr": "^2.3.9",

+ 9 - 8
frontend/taipy-gui/src/components/Taipy/Chart.tsx

@@ -16,7 +16,7 @@ import { Data, Layout, PlotDatum, PlotMarker, PlotRelayoutEvent, PlotSelectionEv
 import Skeleton from "@mui/material/Skeleton";
 import Box from "@mui/material/Box";
 import Tooltip from "@mui/material/Tooltip";
-import { useTheme } from "@mui/system";
+import { useTheme } from "@mui/material";
 
 import { getArrayValue, getUpdateVar, TaipyActiveProps, TaipyChangeProps } from "./utils";
 import {
@@ -293,6 +293,7 @@ const Chart = (props: ChartProp) => {
     useDispatchRequestUpdateOnFirstRender(dispatch, id, module, updateVars);
 
     const layout = useMemo(() => {
+        const layout = {...baseLayout};
         let template = undefined;
         try {
             const tpl = props.template && JSON.parse(props.template);
@@ -307,32 +308,32 @@ const Chart = (props: ChartProp) => {
             console.info(`Error while parsing Chart.template\n${(e as Error).message || e}`);
         }
         if (template) {
-            baseLayout.template = template;
+            layout.template = template;
         }
         if (props.figure) {
             return {
                 ...(props.figure[0].layout as Partial<Layout>),
-                ...baseLayout,
-                title: title || baseLayout.title || (props.figure[0].layout as Partial<Layout>).title,
+                ...layout,
+                title: title || layout.title || (props.figure[0].layout as Partial<Layout>).title,
                 clickmode: "event+select",
             } as Layout;
         }
         return {
-            ...baseLayout,
-            title: title || baseLayout.title,
+            ...layout,
+            title: title || layout.title,
             xaxis: {
                 title:
                     config.traces.length && config.traces[0].length && config.traces[0][0]
                         ? getColNameFromIndexed(config.columns[config.traces[0][0]].dfid)
                         : undefined,
-                ...baseLayout.xaxis,
+                ...layout.xaxis,
             },
             yaxis: {
                 title:
                     config.traces.length == 1 && config.traces[0].length > 1 && config.columns[config.traces[0][1]]
                         ? getColNameFromIndexed(config.columns[config.traces[0][1]].dfid)
                         : undefined,
-                ...baseLayout.yaxis,
+                ...layout.yaxis,
             },
             clickmode: "event+select",
         } as Layout;

+ 16 - 1
frontend/taipy-gui/src/components/Taipy/Selector.spec.tsx

@@ -187,7 +187,22 @@ describe("Selector Component", () => {
             const { getByTestId } = render(<Selector lov={lov} defaultValue="id1" dropdown={true} multiple={true} />);
             getByTestId("CancelIcon");
         });
-        it("opens a dropdown on click", async () => {
+        it("is disabled", async () => {
+            const { getByText } = render(<Selector lov={lov} defaultValue="id1" active={false} dropdown={true} />);
+            const elt = getByText("Item 1");
+            expect(elt.parentElement).toHaveClass("Mui-disabled");
+        });
+        it("is enabled by default", async () => {
+            const { getByText } = render(<Selector lov={lov} defaultValue="id1" dropdown={true} />);
+            const elt = getByText("Item 1");
+            expect(elt.parentElement).not.toHaveClass("Mui-disabled");
+        });
+        it("is enabled by active", async () => {
+            const { getByText } = render(<Selector defaultValue="id1" lov={lov} active={true} dropdown={true} />);
+            const elt = getByText("Item 1");
+            expect(elt.parentElement).not.toHaveClass("Mui-disabled");
+        });
+            it("opens a dropdown on click", async () => {
             const { getByText, getByRole, queryAllByRole } = render(<Selector lov={lov} dropdown={true} />);
             const butElt = getByRole("combobox");
             expect(butElt).toBeInTheDocument()

+ 3 - 1
frontend/taipy-gui/src/components/Taipy/Selector.tsx

@@ -323,6 +323,7 @@ const Selector = (props: SelTreeProps) => {
                         value={dropdownValue}
                         onChange={handleChange}
                         input={<OutlinedInput label={props.label} />}
+                        disabled={!active}
                         renderValue={(selected) => (
                             <Box sx={renderBoxSx}>
                                 {lovList
@@ -345,6 +346,7 @@ const Selector = (props: SelTreeProps) => {
                                                     onDelete={handleDelete}
                                                     data-id={item.id}
                                                     onMouseDown={doNotPropagateEvent}
+                                                    disabled={!active}
                                                 />
                                             );
                                         } else if (idx === 0) {
@@ -362,7 +364,7 @@ const Selector = (props: SelTreeProps) => {
                         MenuProps={getMenuProps(height)}
                     >
                         {lovList.map((item) => (
-                            <MenuItem key={item.id} value={item.id} style={getStyles(item.id, selectedValue, theme)}>
+                            <MenuItem key={item.id} value={item.id} style={getStyles(item.id, selectedValue, theme)} disabled={item.id === null}>
                                 {typeof item.item === "string" ? item.item : <LovImage item={item.item as Icon} />}
                             </MenuItem>
                         ))}

+ 112 - 113
frontend/taipy/package-lock.json

@@ -41,7 +41,6 @@
       }
     },
     "../../taipy/gui/webapp": {
-      "name": "taipy-gui",
       "version": "3.1.0"
     },
     "node_modules/@aashutoshrathi/word-wrap": {
@@ -234,9 +233,9 @@
       }
     },
     "node_modules/@babel/runtime": {
-      "version": "7.23.9",
-      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz",
-      "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==",
+      "version": "7.24.0",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.0.tgz",
+      "integrity": "sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==",
       "dependencies": {
         "regenerator-runtime": "^0.14.0"
       },
@@ -245,9 +244,9 @@
       }
     },
     "node_modules/@babel/types": {
-      "version": "7.23.9",
-      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz",
-      "integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==",
+      "version": "7.24.0",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz",
+      "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==",
       "dependencies": {
         "@babel/helper-string-parser": "^7.23.4",
         "@babel/helper-validator-identifier": "^7.22.20",
@@ -302,9 +301,9 @@
       "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ=="
     },
     "node_modules/@emotion/is-prop-valid": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz",
-      "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==",
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz",
+      "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==",
       "dependencies": {
         "@emotion/memoize": "^0.8.1"
       }
@@ -315,9 +314,9 @@
       "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA=="
     },
     "node_modules/@emotion/react": {
-      "version": "11.11.3",
-      "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.3.tgz",
-      "integrity": "sha512-Cnn0kuq4DoONOMcnoVsTOR8E+AdnKFf//6kUWc4LCdnxj31pZWn7rIULd6Y7/Js1PiPHzn7SKCM9vB/jBni8eA==",
+      "version": "11.11.4",
+      "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz",
+      "integrity": "sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==",
       "dependencies": {
         "@babel/runtime": "^7.18.3",
         "@emotion/babel-plugin": "^11.11.0",
@@ -469,9 +468,9 @@
       }
     },
     "node_modules/@eslint/js": {
-      "version": "8.56.0",
-      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz",
-      "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==",
+      "version": "8.57.0",
+      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz",
+      "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==",
       "dev": true,
       "engines": {
         "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@@ -596,9 +595,9 @@
       }
     },
     "node_modules/@jridgewell/gen-mapping": {
-      "version": "0.3.3",
-      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
-      "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
+      "version": "0.3.4",
+      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.4.tgz",
+      "integrity": "sha512-Oud2QPM5dHviZNn4y/WhhYKSXksv+1xLEIsNrAbGcFzUN3ubqWRFT5gwPchNc5NuzILOU4tPBDTZ4VwhL8Y7cw==",
       "dev": true,
       "dependencies": {
         "@jridgewell/set-array": "^1.0.1",
@@ -644,9 +643,9 @@
       "dev": true
     },
     "node_modules/@jridgewell/trace-mapping": {
-      "version": "0.3.22",
-      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz",
-      "integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==",
+      "version": "0.3.23",
+      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.23.tgz",
+      "integrity": "sha512-9/4foRoUKp8s96tSkh8DlAAc5A0Ty8vLXld+l9gjKKY6ckwI8G15f0hskGmuLZu78ZlGa1vtsfOa+lnB4vG6Jg==",
       "dev": true,
       "dependencies": {
         "@jridgewell/resolve-uri": "^3.1.0",
@@ -685,14 +684,14 @@
       }
     },
     "node_modules/@mui/base": {
-      "version": "5.0.0-beta.36",
-      "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.36.tgz",
-      "integrity": "sha512-6A8fYiXgjqTO6pgj31Hc8wm1M3rFYCxDRh09dBVk0L0W4cb2lnurRJa3cAyic6hHY+we1S58OdGYRbKmOsDpGQ==",
+      "version": "5.0.0-beta.37",
+      "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.37.tgz",
+      "integrity": "sha512-/o3anbb+DeCng8jNsd3704XtmmLDZju1Fo8R2o7ugrVtPQ/QpcqddwKNzKPZwa0J5T8YNW3ZVuHyQgbTnQLisQ==",
       "dependencies": {
         "@babel/runtime": "^7.23.9",
         "@floating-ui/react-dom": "^2.0.8",
         "@mui/types": "^7.2.13",
-        "@mui/utils": "^5.15.9",
+        "@mui/utils": "^5.15.11",
         "@popperjs/core": "^2.11.8",
         "clsx": "^2.1.0",
         "prop-types": "^15.8.1"
@@ -725,9 +724,9 @@
       }
     },
     "node_modules/@mui/icons-material": {
-      "version": "5.15.10",
-      "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.10.tgz",
-      "integrity": "sha512-9cF8oUHZKo9oQ7EQ3pxPELaZuZVmphskU4OI6NiJNDVN7zcuvrEsuWjYo1Zh4fLiC39Nrvm30h/B51rcUjvSGA==",
+      "version": "5.15.11",
+      "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.11.tgz",
+      "integrity": "sha512-R5ZoQqnKpd+5Ew7mBygTFLxgYsQHPhgR3TDXSgIHYIjGzYuyPLmGLSdcPUoMdi6kxiYqHlpPj4NJxlbaFD0UHA==",
       "dependencies": {
         "@babel/runtime": "^7.23.9"
       },
@@ -750,16 +749,16 @@
       }
     },
     "node_modules/@mui/material": {
-      "version": "5.15.10",
-      "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.10.tgz",
-      "integrity": "sha512-YJJGHjwDOucecjDEV5l9ISTCo+l9YeWrho623UajzoHRYxuKUmwrGVYOW4PKwGvCx9SU9oklZnbbi2Clc5XZHw==",
+      "version": "5.15.11",
+      "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.11.tgz",
+      "integrity": "sha512-FA3eEuEZaDaxgN3CgfXezMWbCZ4VCeU/sv0F0/PK5n42qIgsPVD6q+j71qS7/62sp6wRFMHtDMpXRlN+tT/7NA==",
       "dependencies": {
         "@babel/runtime": "^7.23.9",
-        "@mui/base": "5.0.0-beta.36",
-        "@mui/core-downloads-tracker": "^5.15.10",
-        "@mui/system": "^5.15.9",
+        "@mui/base": "5.0.0-beta.37",
+        "@mui/core-downloads-tracker": "^5.15.11",
+        "@mui/system": "^5.15.11",
         "@mui/types": "^7.2.13",
-        "@mui/utils": "^5.15.9",
+        "@mui/utils": "^5.15.11",
         "@types/react-transition-group": "^4.4.10",
         "clsx": "^2.1.0",
         "csstype": "^3.1.3",
@@ -851,15 +850,15 @@
       }
     },
     "node_modules/@mui/system": {
-      "version": "5.15.9",
-      "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.9.tgz",
-      "integrity": "sha512-SxkaaZ8jsnIJ77bBXttfG//LUf6nTfOcaOuIgItqfHv60ZCQy/Hu7moaob35kBb+guxVJnoSZ+7vQJrA/E7pKg==",
+      "version": "5.15.11",
+      "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.11.tgz",
+      "integrity": "sha512-9j35suLFq+MgJo5ktVSHPbkjDLRMBCV17NMBdEQurh6oWyGnLM4uhU4QGZZQ75o0vuhjJghOCA1jkO3+79wKsA==",
       "dependencies": {
         "@babel/runtime": "^7.23.9",
-        "@mui/private-theming": "^5.15.9",
-        "@mui/styled-engine": "^5.15.9",
+        "@mui/private-theming": "^5.15.11",
+        "@mui/styled-engine": "^5.15.11",
         "@mui/types": "^7.2.13",
-        "@mui/utils": "^5.15.9",
+        "@mui/utils": "^5.15.11",
         "clsx": "^2.1.0",
         "csstype": "^3.1.3",
         "prop-types": "^15.8.1"
@@ -1148,9 +1147,9 @@
       "dev": true
     },
     "node_modules/@types/eslint": {
-      "version": "8.56.3",
-      "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.3.tgz",
-      "integrity": "sha512-PvSf1wfv2wJpVIFUMSb+i4PvqNYkB9Rkp9ZDO3oaWzq4SKhsQk4mrMBr3ZH06I0hKrVGLBacmgl8JM4WVjb9dg==",
+      "version": "8.56.4",
+      "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.4.tgz",
+      "integrity": "sha512-lG1GLUnL5vuRBGb3MgWUWLdGMH2Hps+pERuyQXCfWozuGKdnhf9Pbg4pkcrVUHjKrU7Rl+GCZ/299ObBXZFAxg==",
       "dev": true,
       "dependencies": {
         "@types/estree": "*",
@@ -1213,9 +1212,9 @@
       "dev": true
     },
     "node_modules/@types/node": {
-      "version": "20.11.20",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.20.tgz",
-      "integrity": "sha512-7/rR21OS+fq8IyHTgtLkDK949uzsa6n8BkziAKtPVpugIkO6D+/ooXMvzXxDnZrmtXVfjb1bKQafYpb8s89LOg==",
+      "version": "20.11.21",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.21.tgz",
+      "integrity": "sha512-/ySDLGscFPNasfqStUuWWPfL78jompfIoVzLJPVVAHBh6rpG68+pI2Gk+fNLeI8/f1yPYL4s46EleVIc20F1Ow==",
       "dev": true,
       "dependencies": {
         "undici-types": "~5.26.4"
@@ -1232,9 +1231,9 @@
       "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng=="
     },
     "node_modules/@types/react": {
-      "version": "18.2.58",
-      "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.58.tgz",
-      "integrity": "sha512-TaGvMNhxvG2Q0K0aYxiKfNDS5m5ZsoIBBbtfUorxdH4NGSXIlYvZxLJI+9Dd3KjeB3780bciLyAb7ylO8pLhPw==",
+      "version": "18.2.60",
+      "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.60.tgz",
+      "integrity": "sha512-dfiPj9+k20jJrLGOu9Nf6eqxm2EyJRrq2NvwOFsfbb7sFExZ9WELPs67UImHj3Ayxg8ruTtKtNnbjaF8olPq0A==",
       "dependencies": {
         "@types/prop-types": "*",
         "@types/scheduler": "*",
@@ -1255,9 +1254,9 @@
       "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A=="
     },
     "node_modules/@types/semver": {
-      "version": "7.5.7",
-      "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.7.tgz",
-      "integrity": "sha512-/wdoPq1QqkSj9/QOeKkFquEuPzQbHTWAMPH/PaUMB+JuR31lXhlWXRZ52IpfDYVlDOUBvX09uBrPwxGT1hjNBg==",
+      "version": "7.5.8",
+      "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
+      "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==",
       "dev": true
     },
     "node_modules/@types/yargs": {
@@ -1276,16 +1275,16 @@
       "dev": true
     },
     "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "7.0.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.0.2.tgz",
-      "integrity": "sha512-/XtVZJtbaphtdrWjr+CJclaCVGPtOdBpFEnvtNf/jRV0IiEemRrL0qABex/nEt8isYcnFacm3nPHYQwL+Wb7qg==",
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.1.0.tgz",
+      "integrity": "sha512-j6vT/kCulhG5wBmGtstKeiVr1rdXE4nk+DT1k6trYkwlrvW9eOF5ZbgKnd/YR6PcM4uTEXa0h6Fcvf6X7Dxl0w==",
       "dev": true,
       "dependencies": {
         "@eslint-community/regexpp": "^4.5.1",
-        "@typescript-eslint/scope-manager": "7.0.2",
-        "@typescript-eslint/type-utils": "7.0.2",
-        "@typescript-eslint/utils": "7.0.2",
-        "@typescript-eslint/visitor-keys": "7.0.2",
+        "@typescript-eslint/scope-manager": "7.1.0",
+        "@typescript-eslint/type-utils": "7.1.0",
+        "@typescript-eslint/utils": "7.1.0",
+        "@typescript-eslint/visitor-keys": "7.1.0",
         "debug": "^4.3.4",
         "graphemer": "^1.4.0",
         "ignore": "^5.2.4",
@@ -1311,15 +1310,15 @@
       }
     },
     "node_modules/@typescript-eslint/parser": {
-      "version": "7.0.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.0.2.tgz",
-      "integrity": "sha512-GdwfDglCxSmU+QTS9vhz2Sop46ebNCXpPPvsByK7hu0rFGRHL+AusKQJ7SoN+LbLh6APFpQwHKmDSwN35Z700Q==",
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.1.0.tgz",
+      "integrity": "sha512-V1EknKUubZ1gWFjiOZhDSNToOjs63/9O0puCgGS8aDOgpZY326fzFu15QAUjwaXzRZjf/qdsdBrckYdv9YxB8w==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/scope-manager": "7.0.2",
-        "@typescript-eslint/types": "7.0.2",
-        "@typescript-eslint/typescript-estree": "7.0.2",
-        "@typescript-eslint/visitor-keys": "7.0.2",
+        "@typescript-eslint/scope-manager": "7.1.0",
+        "@typescript-eslint/types": "7.1.0",
+        "@typescript-eslint/typescript-estree": "7.1.0",
+        "@typescript-eslint/visitor-keys": "7.1.0",
         "debug": "^4.3.4"
       },
       "engines": {
@@ -1339,13 +1338,13 @@
       }
     },
     "node_modules/@typescript-eslint/scope-manager": {
-      "version": "7.0.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.0.2.tgz",
-      "integrity": "sha512-l6sa2jF3h+qgN2qUMjVR3uCNGjWw4ahGfzIYsCtFrQJCjhbrDPdiihYT8FnnqFwsWX+20hK592yX9I2rxKTP4g==",
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.1.0.tgz",
+      "integrity": "sha512-6TmN4OJiohHfoOdGZ3huuLhpiUgOGTpgXNUPJgeZOZR3DnIpdSgtt83RS35OYNNXxM4TScVlpVKC9jyQSETR1A==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.0.2",
-        "@typescript-eslint/visitor-keys": "7.0.2"
+        "@typescript-eslint/types": "7.1.0",
+        "@typescript-eslint/visitor-keys": "7.1.0"
       },
       "engines": {
         "node": "^16.0.0 || >=18.0.0"
@@ -1356,13 +1355,13 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils": {
-      "version": "7.0.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.0.2.tgz",
-      "integrity": "sha512-IKKDcFsKAYlk8Rs4wiFfEwJTQlHcdn8CLwLaxwd6zb8HNiMcQIFX9sWax2k4Cjj7l7mGS5N1zl7RCHOVwHq2VQ==",
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.1.0.tgz",
+      "integrity": "sha512-UZIhv8G+5b5skkcuhgvxYWHjk7FW7/JP5lPASMEUoliAPwIH/rxoUSQPia2cuOj9AmDZmwUl1usKm85t5VUMew==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/typescript-estree": "7.0.2",
-        "@typescript-eslint/utils": "7.0.2",
+        "@typescript-eslint/typescript-estree": "7.1.0",
+        "@typescript-eslint/utils": "7.1.0",
         "debug": "^4.3.4",
         "ts-api-utils": "^1.0.1"
       },
@@ -1383,9 +1382,9 @@
       }
     },
     "node_modules/@typescript-eslint/types": {
-      "version": "7.0.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.0.2.tgz",
-      "integrity": "sha512-ZzcCQHj4JaXFjdOql6adYV4B/oFOFjPOC9XYwCaZFRvqN8Llfvv4gSxrkQkd2u4Ci62i2c6W6gkDwQJDaRc4nA==",
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.1.0.tgz",
+      "integrity": "sha512-qTWjWieJ1tRJkxgZYXx6WUYtWlBc48YRxgY2JN1aGeVpkhmnopq+SUC8UEVGNXIvWH7XyuTjwALfG6bFEgCkQA==",
       "dev": true,
       "engines": {
         "node": "^16.0.0 || >=18.0.0"
@@ -1396,13 +1395,13 @@
       }
     },
     "node_modules/@typescript-eslint/typescript-estree": {
-      "version": "7.0.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.0.2.tgz",
-      "integrity": "sha512-3AMc8khTcELFWcKcPc0xiLviEvvfzATpdPj/DXuOGIdQIIFybf4DMT1vKRbuAEOFMwhWt7NFLXRkbjsvKZQyvw==",
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.1.0.tgz",
+      "integrity": "sha512-k7MyrbD6E463CBbSpcOnwa8oXRdHzH1WiVzOipK3L5KSML92ZKgUBrTlehdi7PEIMT8k0bQixHUGXggPAlKnOQ==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.0.2",
-        "@typescript-eslint/visitor-keys": "7.0.2",
+        "@typescript-eslint/types": "7.1.0",
+        "@typescript-eslint/visitor-keys": "7.1.0",
         "debug": "^4.3.4",
         "globby": "^11.1.0",
         "is-glob": "^4.0.3",
@@ -1424,17 +1423,17 @@
       }
     },
     "node_modules/@typescript-eslint/utils": {
-      "version": "7.0.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.0.2.tgz",
-      "integrity": "sha512-PZPIONBIB/X684bhT1XlrkjNZJIEevwkKDsdwfiu1WeqBxYEEdIgVDgm8/bbKHVu+6YOpeRqcfImTdImx/4Bsw==",
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.1.0.tgz",
+      "integrity": "sha512-WUFba6PZC5OCGEmbweGpnNJytJiLG7ZvDBJJoUcX4qZYf1mGZ97mO2Mps6O2efxJcJdRNpqweCistDbZMwIVHw==",
       "dev": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.4.0",
         "@types/json-schema": "^7.0.12",
         "@types/semver": "^7.5.0",
-        "@typescript-eslint/scope-manager": "7.0.2",
-        "@typescript-eslint/types": "7.0.2",
-        "@typescript-eslint/typescript-estree": "7.0.2",
+        "@typescript-eslint/scope-manager": "7.1.0",
+        "@typescript-eslint/types": "7.1.0",
+        "@typescript-eslint/typescript-estree": "7.1.0",
         "semver": "^7.5.4"
       },
       "engines": {
@@ -1449,12 +1448,12 @@
       }
     },
     "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "7.0.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.0.2.tgz",
-      "integrity": "sha512-8Y+YiBmqPighbm5xA2k4wKTxRzx9EkBu7Rlw+WHqMvRJ3RPz/BMBO9b2ru0LUNmXg120PHUXD5+SWFy2R8DqlQ==",
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.1.0.tgz",
+      "integrity": "sha512-FhUqNWluiGNzlvnDZiXad4mZRhtghdoKW6e98GoEOYSu5cND+E39rG5KwJMUzeENwm1ztYBRqof8wMLP+wNPIA==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.0.2",
+        "@typescript-eslint/types": "7.1.0",
         "eslint-visitor-keys": "^3.4.1"
       },
       "engines": {
@@ -2043,9 +2042,9 @@
       }
     },
     "node_modules/caniuse-lite": {
-      "version": "1.0.30001589",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001589.tgz",
-      "integrity": "sha512-vNQWS6kI+q6sBlHbh71IIeC+sRwK2N3EDySc/updIGhIee2x5z00J4c1242/5/d6EpEMdOnk/m+6tuk4/tcsqg==",
+      "version": "1.0.30001591",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001591.tgz",
+      "integrity": "sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ==",
       "dev": true,
       "funding": [
         {
@@ -2340,15 +2339,15 @@
       }
     },
     "node_modules/electron-to-chromium": {
-      "version": "1.4.680",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.680.tgz",
-      "integrity": "sha512-4nToZ5jlPO14W82NkF32wyjhYqQByVaDmLy4J2/tYcAbJfgO2TKJC780Az1V13gzq4l73CJ0yuyalpXvxXXD9A==",
+      "version": "1.4.685",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.685.tgz",
+      "integrity": "sha512-yDYeobbTEe4TNooEzOQO6xFqg9XnAkVy2Lod1C1B2it8u47JNLYvl9nLDWBamqUakWB8Jc1hhS1uHUNYTNQdfw==",
       "dev": true
     },
     "node_modules/enhanced-resolve": {
-      "version": "5.15.0",
-      "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz",
-      "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==",
+      "version": "5.15.1",
+      "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.1.tgz",
+      "integrity": "sha512-3d3JRbwsCLJsYgvb6NuWEG44jjPSOMuS73L/6+7BZuoKm3W+qXnSoIYVHi8dG7Qcg4inAY4jbzkZ7MnskePeDg==",
       "dev": true,
       "dependencies": {
         "graceful-fs": "^4.2.4",
@@ -2547,16 +2546,16 @@
       }
     },
     "node_modules/eslint": {
-      "version": "8.56.0",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz",
-      "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==",
+      "version": "8.57.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
+      "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
       "dev": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.2.0",
         "@eslint-community/regexpp": "^4.6.1",
         "@eslint/eslintrc": "^2.1.4",
-        "@eslint/js": "8.56.0",
-        "@humanwhocodes/config-array": "^0.11.13",
+        "@eslint/js": "8.57.0",
+        "@humanwhocodes/config-array": "^0.11.14",
         "@humanwhocodes/module-importer": "^1.0.1",
         "@nodelib/fs.walk": "^1.2.8",
         "@ungap/structured-clone": "^1.2.0",
@@ -5114,9 +5113,9 @@
       }
     },
     "node_modules/terser": {
-      "version": "5.28.0",
-      "resolved": "https://registry.npmjs.org/terser/-/terser-5.28.0.tgz",
-      "integrity": "sha512-UK1n2JZN6gqfwmjzcikRp+uF/7jdwPXO7M0k/Hnqxzj3T5POL4YBVrv5Y4MKixTzBq4OmH7FfEYRxcy/Eh7YhA==",
+      "version": "5.28.1",
+      "resolved": "https://registry.npmjs.org/terser/-/terser-5.28.1.tgz",
+      "integrity": "sha512-wM+bZp54v/E9eRRGXb5ZFDvinrJIOaTapx3WUokyVGZu5ucVCK55zEgGd5Dl2fSr3jUo5sDiERErUWLY6QPFyA==",
       "dev": true,
       "dependencies": {
         "@jridgewell/source-map": "^0.3.3",

+ 6 - 2
taipy/config/common/_config_blocker.py

@@ -23,11 +23,15 @@ class _ConfigBlocker:
 
     @classmethod
     def _block(cls):
-        cls.__block_config_update = True
+        if not cls.__block_config_update:
+            cls.__logger.info("Blocking configuration update.")
+            cls.__block_config_update = True
 
     @classmethod
     def _unblock(cls):
-        cls.__block_config_update = False
+        if cls.__block_config_update:
+            cls.__logger.info("Unblocking configuration update.")
+            cls.__block_config_update = False
 
     @classmethod
     def _check(cls):

+ 1 - 0
taipy/config/setup.py

@@ -57,6 +57,7 @@ setup(
     packages=find_namespace_packages(where=".")
     + find_packages(include=["taipy", "taipy.config", "taipy.config.*", "taipy.logger", "taipy.logger.*"]),
     include_package_data=True,
+    data_files=[('version', ['version.json'])],
     test_suite="tests",
     tests_require=test_requirements,
     url="https://github.com/avaiga/taipy-config",

+ 1 - 1
taipy/config/version.json

@@ -1 +1 @@
-{"major": 3, "minor": 1, "patch": 0, "ext": "dev0"}
+{"major": 3, "minor": 1, "patch": 0, "ext": "dev2"}

+ 33 - 15
taipy/core/_core.py

@@ -33,6 +33,9 @@ class Core:
     _is_running = False
     __lock_is_running = Lock()
 
+    _version_is_initialized = False
+    __lock_version_is_initialized = Lock()
+
     __logger = _TaipyLogger._get_logger()
 
     _orchestrator: Optional[_Orchestrator] = None
@@ -48,8 +51,8 @@ class Core:
         """
         Start a Core service.
 
-        This function checks the configuration, manages application's version,
-        and starts a dispatcher and lock the Config.
+        This function checks and locks the configuration, manages application's version,
+        and starts a job dispatcher.
         """
         if self.__class__._is_running:
             raise CoreServiceIsAlreadyRunning
@@ -57,13 +60,7 @@ class Core:
         with self.__class__.__lock_is_running:
             self.__class__._is_running = True
 
-        self.__update_core_section()
-        self.__manage_version()
-        self.__check_and_block_config()
-
-        if self._orchestrator is None:
-            self._orchestrator = _OrchestratorFactory._build_orchestrator()
-
+        self._manage_version_and_block_config()
         self.__start_dispatcher(force_restart)
 
     def stop(self, wait: bool = True, timeout: Optional[float] = None):
@@ -84,25 +81,46 @@ class Core:
         with self.__class__.__lock_is_running:
             self.__class__._is_running = False
 
-    @staticmethod
-    def __update_core_section():
+        with self.__class__.__lock_version_is_initialized:
+            self.__class__._version_is_initialized = False
+
+    @classmethod
+    def _manage_version_and_block_config(cls):
+        """
+        Manage the application's version and block the Config from updates.
+        """
+        if cls._version_is_initialized:
+            return
+
+        with cls.__lock_version_is_initialized:
+            cls._version_is_initialized = True
+
+        cls.__update_core_section()
+        cls.__manage_version()
+        cls.__check_and_block_config()
+
+    @classmethod
+    def __update_core_section(cls):
         _CoreCLI.create_parser()
         Config._applied_config._unique_sections[CoreSection.name]._update(_CoreCLI.parse_arguments())
 
-    @staticmethod
-    def __manage_version():
+    @classmethod
+    def __manage_version(cls):
         _VersionManagerFactory._build_manager()._manage_version()
         Config._applied_config._unique_sections[CoreSection.name]._update(
             {"version_number": _VersionManagerFactory._build_manager()._get_latest_version()}
         )
 
-    @staticmethod
-    def __check_and_block_config():
+    @classmethod
+    def __check_and_block_config(cls):
         Config.check()
         Config.block_update()
         _init_backup_file_with_storage_folder()
 
     def __start_dispatcher(self, force_restart):
+        if self._orchestrator is None:
+            self._orchestrator = _OrchestratorFactory._build_orchestrator()
+
         if dispatcher := _OrchestratorFactory._build_dispatcher(force_restart=force_restart):
             self._dispatcher = dispatcher
 

+ 2 - 0
taipy/core/_init.py

@@ -24,8 +24,10 @@ from .sequence.sequence import Sequence
 from .sequence.sequence_id import SequenceId
 from .submission.submission import Submission
 from .submission.submission_id import SubmissionId
+from .submission.submission_status import SubmissionStatus
 from .taipy import (
     cancel_job,
+    clean_all_entities,
     clean_all_entities_by_version,
     compare_scenarios,
     create_global_data_node,

+ 2 - 2
taipy/core/_version/_cli/_version_cli.py

@@ -21,7 +21,7 @@ from ...exceptions.exceptions import VersionIsNotProductionVersion
 from ...job._job_manager_factory import _JobManagerFactory
 from ...scenario._scenario_manager_factory import _ScenarioManagerFactory
 from ...sequence._sequence_manager_factory import _SequenceManagerFactory
-from ...taipy import clean_all_entities_by_version
+from ...taipy import clean_all_entities
 from ...task._task_manager_factory import _TaskManagerFactory
 from .._version_manager_factory import _VersionManagerFactory
 from ._bcolor import _Bcolors
@@ -102,7 +102,7 @@ class _VersionCLI:
                 raise SystemExit(e) from None
 
         if args.delete:
-            if clean_all_entities_by_version(args.delete):
+            if clean_all_entities(args.delete):
                 cls.__logger.info(f"Successfully delete version {args.delete}.")
             else:
                 sys.exit(1)

+ 2 - 2
taipy/core/_version/_version_manager.py

@@ -182,12 +182,12 @@ class _VersionManager(_Manager[_Version]):
 
     @classmethod
     def _manage_version(cls):
-        from ..taipy import clean_all_entities_by_version
+        from ..taipy import clean_all_entities
 
         if Config.core.mode == "development":
             current_version_number = cls._get_development_version()
             cls.__logger.info(f"Development mode: Clean all entities of version {current_version_number}")
-            clean_all_entities_by_version(current_version_number)
+            clean_all_entities(current_version_number)
             cls._set_development_version(current_version_number)
 
         elif Config.core.mode in ["experiment", "production"]:

+ 2 - 2
taipy/core/common/_warnings.py

@@ -24,14 +24,14 @@ def _warn_deprecated(deprecated: str, suggest: Optional[str] = None, stacklevel:
     warnings.warn(message=message, category=category, stacklevel=stacklevel)
 
 
-def _warn_no_core_service(stacklevel: int = 3):
+def _warn_no_core_service(specific_message, stacklevel: int = 3):
     def inner(f):
         @functools.wraps(f)
         def _check_if_core_service_is_running(*args, **kwargs):
             from .._orchestrator._orchestrator_factory import _OrchestratorFactory
 
             if not _OrchestratorFactory._dispatcher:
-                message = "The Core service is NOT running"
+                message = f"The Core service is NOT running. {specific_message}"
                 warnings.warn(message=message, category=ResourceWarning, stacklevel=stacklevel)
 
             return f(*args, **kwargs)

+ 2 - 0
taipy/core/config/core_section.py

@@ -334,6 +334,7 @@ class CoreSection(UniqueSection):
     def _configure(
         root_folder: Optional[str] = None,
         storage_folder: Optional[str] = None,
+        taipy_storage_folder: Optional[str] = None,
         repository_type: Optional[str] = None,
         repository_properties: Optional[Dict[str, Union[str, int]]] = None,
         read_entity_retry: Optional[int] = None,
@@ -373,6 +374,7 @@ class CoreSection(UniqueSection):
         section = CoreSection(
             root_folder=root_folder,
             storage_folder=storage_folder,
+            taipy_storage_folder=taipy_storage_folder,
             repository_type=repository_type,
             repository_properties=repository_properties,
             read_entity_retry=read_entity_retry,

+ 1 - 1
taipy/core/data/data_node.py

@@ -566,7 +566,7 @@ class DataNode(_Entity, _Labeled):
 
 
 @_make_event.register(DataNode)
-def make_event_for_datanode(
+def _make_event_for_datanode(
     data_node: DataNode,
     operation: EventOperation,
     /,

+ 0 - 9
taipy/core/data/sql_table.py

@@ -122,15 +122,6 @@ class SQLTableDataNode(_AbstractSQLDataNode):
         self.__insert_data(data, engine, connection, delete_table=True)
 
     def __insert_data(self, data, engine, connection, delete_table: bool = False) -> None:
-        """
-        Insert data into a SQL table.
-
-        Parameters:
-            data (List[Dict]): a list of dictionaries, where each dictionary represents a row of the table.
-            table: a SQLAlchemy object that represents a table.
-            connection: a SQLAlchemy connection to write the data.
-            delete_table (bool): indicates if the table should be deleted before inserting the data.
-        """
         table = self._create_table(engine)
         if isinstance(data, pd.DataFrame):
             self.__insert_dataframe(data, table, connection, delete_table)

+ 1 - 0
taipy/core/setup.py

@@ -78,6 +78,7 @@ setup(
     name="taipy-core",
     packages=find_namespace_packages(where=".") + find_packages(include=["taipy", "taipy.core", "taipy.core.*"]),
     include_package_data=True,
+    data_files=[('version', ['version.json'])],
     test_suite="tests",
     tests_require=test_requirements,
     url="https://github.com/avaiga/taipy-core",

+ 39 - 11
taipy/core/taipy.py

@@ -17,6 +17,7 @@ from typing import Any, Callable, Dict, List, Optional, Set, Union, overload
 from taipy.config.common.scope import Scope
 from taipy.logger._taipy_logger import _TaipyLogger
 
+from ._core import Core
 from ._entity._entity import _Entity
 from ._version._version_manager_factory import _VersionManagerFactory
 from .common._check_instance import (
@@ -28,7 +29,7 @@ from .common._check_instance import (
     _is_submission,
     _is_task,
 )
-from .common._warnings import _warn_no_core_service
+from .common._warnings import _warn_deprecated, _warn_no_core_service
 from .config.data_node_config import DataNodeConfig
 from .config.scenario_config import ScenarioConfig
 from .cycle._cycle_manager_factory import _CycleManagerFactory
@@ -219,7 +220,7 @@ def is_readable(
     return False
 
 
-@_warn_no_core_service()
+@_warn_no_core_service("The submitted entity will not be executed until the Core service is running.")
 def submit(
     entity: Union[Scenario, Sequence, Task],
     force: bool = False,
@@ -441,7 +442,7 @@ def is_deletable(entity: Union[Scenario, Job, Submission, ScenarioId, JobId, Sub
 
     Parameters:
         entity (Union[Scenario, Job, Submission, ScenarioId, JobId, SubmissionId]): The scenario,
-        job or submission to check.
+            job or submission to check.
 
     Returns:
         True if the given scenario, job or submission can be deleted. False otherwise.
@@ -801,7 +802,7 @@ def get_latest_submission(entity: Union[Scenario, Sequence, Task]) -> Optional[S
 
     Parameters:
         entity (Union[Scenario^, Sequence^, Task^]): The scenario, sequence or task to
-        retrieve the latest submission from.
+            retrieve the latest submission from.
 
     Returns:
         The latest submission created from _scenario_, _sequence_ and _task_, or None
@@ -835,7 +836,10 @@ def create_scenario(
 ) -> Scenario:
     """Create and return a new scenario based on a scenario configuration.
 
-    If the scenario belongs to a cycle, a cycle (corresponding to the _creation_date_
+    This function checks and locks the configuration, manages application's version,
+    and creates a new scenario from the scenario configuration provided.
+
+    If the scenario belongs to a cycle, the cycle (corresponding to the _creation_date_
     and the configuration frequency attribute) is created if it does not exist yet.
 
     Parameters:
@@ -846,13 +850,22 @@ def create_scenario(
 
     Returns:
         The new scenario.
+
+    Raises:
+        SystemExit: If the configuration check returns some errors.
+
     """
+    Core._manage_version_and_block_config()
+
     return _ScenarioManagerFactory._build_manager()._create(config, creation_date, name)
 
 
 def create_global_data_node(config: DataNodeConfig) -> DataNode:
     """Create and return a new GLOBAL data node from a data node configuration.
 
+    This function checks and locks the configuration, manages application's version,
+    and creates the new data node from the data node configuration provided.
+
     Parameters:
         config (DataNodeConfig^): The data node configuration. It must have a `GLOBAL` scope.
 
@@ -861,32 +874,40 @@ def create_global_data_node(config: DataNodeConfig) -> DataNode:
 
     Raises:
         DataNodeConfigIsNotGlobal^: If the data node configuration does not have GLOBAL scope.
+        SystemExit: If the configuration check returns some errors.
     """
     # Check if the data node config has GLOBAL scope
     if config.scope is not Scope.GLOBAL:
         raise DataNodeConfigIsNotGlobal(config.id)
 
+    Core._manage_version_and_block_config()
+
     if dns := _DataManagerFactory._build_manager()._get_by_config_id(config.id):
         return dns[0]
     return _DataManagerFactory._build_manager()._create_and_set(config, None, None)
 
 
 def clean_all_entities_by_version(version_number=None) -> bool:
-    """Delete all entities of a specific version.
+    """Deprecated. Use `clean_all_entities` function instead."""
+    _warn_deprecated("'clean_all_entities_by_version'", suggest="the 'clean_all_entities' function")
+    return clean_all_entities(version_number)
+
 
-    This function deletes all entities associated with the specified version.
+def clean_all_entities(version_number: str) -> bool:
+    """Deletes all entities associated with the specified version.
 
     Parameters:
-        version_number (optional[str]): The version number of the entities to be deleted.
-            If None, the default behavior may apply.
+        version_number (str): The version number of the entities to be deleted.
+            The version_number should not be a production version.
 
     Returns:
         True if the operation succeeded, False otherwise.
 
     Notes:
         - If the specified version does not exist, the operation will be aborted, and False will be returned.
-        - This function cleans all entities, including jobs, scenarios, sequences, tasks, and data nodes.
-        - The production version of the specified version is also deleted if it exists.
+        - If the specified version is a production version, the operation will be aborted, and False will be returned.
+        - This function cleans all entities, including jobs, submissions, scenarios, cycles, sequences, tasks,
+            and data nodes.
     """
     version_manager = _VersionManagerFactory._build_manager()
     try:
@@ -895,6 +916,12 @@ def clean_all_entities_by_version(version_number=None) -> bool:
         __logger.warning(f"{e.message} Abort cleaning the entities of version '{version_number}'.")
         return False
 
+    if version_number in version_manager._get_production_versions():
+        __logger.warning(
+            f"Abort cleaning the entities of version '{version_number}'. A production version can not be deleted."
+        )
+        return False
+
     _JobManagerFactory._build_manager()._delete_by_version(version_number)
     _SubmissionManagerFactory._build_manager()._delete_by_version(version_number)
     _ScenarioManagerFactory._build_manager()._delete_by_version(version_number)
@@ -903,6 +930,7 @@ def clean_all_entities_by_version(version_number=None) -> bool:
     _DataManagerFactory._build_manager()._delete_by_version(version_number)
 
     version_manager._delete(version_number)
+
     try:
         version_manager._delete_production_version(version_number)
     except VersionIsNotProductionVersion:

+ 1 - 1
taipy/core/version.json

@@ -1 +1 @@
-{"major": 3, "minor": 1, "patch": 0, "ext": "dev0"}
+{"major": 3, "minor": 1, "patch": 0, "ext": "dev2"}

+ 2 - 2
taipy/gui/_renderers/builder.py

@@ -20,8 +20,8 @@ from inspect import isclass
 from urllib.parse import quote
 
 from .._warnings import _warn
-from ..gui_types import PropertyType, _get_taipy_type
 from ..partial import Partial
+from ..types import PropertyType, _get_taipy_type
 from ..utils import (
     _date_to_string,
     _get_broadcast_var_name,
@@ -69,7 +69,7 @@ class _Builder:
         "apply",
         "style",
         "tooltip",
-        "lov"
+        "lov",
     ]
 
     def __init__(

+ 1 - 1
taipy/gui/_renderers/factory.py

@@ -13,7 +13,7 @@ import re
 import typing as t
 from datetime import datetime
 
-from ..gui_types import PropertyType
+from ..types import PropertyType
 from .builder import _Builder
 
 if t.TYPE_CHECKING:

+ 2 - 2
taipy/gui/_renderers/utils.py

@@ -12,7 +12,7 @@
 import typing as t
 
 from .._warnings import _warn
-from ..gui_types import NumberTypes
+from ..types import NumberTypes
 from ..utils import _RE_PD_TYPE, _get_date_col_str_name, _MapDict
 
 
@@ -57,7 +57,7 @@ def _get_columns_dict(  # noqa: C901
     if isinstance(columns, str):
         col_dict = _get_columns_dict_from_list([s.strip() for s in columns.split(";")], col_types_keys, value)
     elif isinstance(columns, (list, tuple)):
-        col_dict = _get_columns_dict_from_list(columns, col_types_keys, value) # type: ignore[arg-type]
+        col_dict = _get_columns_dict_from_list(columns, col_types_keys, value)  # type: ignore[arg-type]
     elif isinstance(columns, _MapDict):
         col_dict = columns._dict.copy()
     elif isinstance(columns, dict):

+ 5 - 5
taipy/gui/_warnings.py

@@ -32,11 +32,11 @@ class TaipyGuiWarning(UserWarning):
 
 def _warn(message: str, e: t.Optional[BaseException] = None):
     warnings.warn(
-        f"{message}:\n{''.join(traceback.format_exception(type(e), e, e.__traceback__))}"
-        if e and TaipyGuiWarning._tp_debug_mode
-        else f"{message}:\n{e}"
-        if e
-        else message,
+        (
+            f"{message}:\n{''.join(traceback.format_exception(type(e), e, e.__traceback__))}"
+            if e and TaipyGuiWarning._tp_debug_mode
+            else f"{message}:\n{e}" if e else message
+        ),
         TaipyGuiWarning,
         stacklevel=2,
     )

+ 2 - 2
taipy/gui/data/decimator/rdp.py

@@ -61,7 +61,7 @@ class RDP(Decimator):
         xdiff = P2[0] - P1[0]
         ydiff = P2[1] - P1[1]
         nom = (ydiff * points[:, 0] - xdiff * points[:, 1] + P2[0] * P1[1] - P2[1] * P1[0]) ** 2
-        denom = ydiff ** 2 + xdiff ** 2
+        denom = ydiff**2 + xdiff**2
         return np.divide(nom, denom)
 
     @staticmethod
@@ -89,7 +89,7 @@ class RDP(Decimator):
             points = data[start + 1 : end]
             dsq = RDP.dsquared_line_points(P1, P2, points)
 
-            mask_eps = dsq > epsilon ** 2
+            mask_eps = dsq > epsilon**2
 
             if mask_eps.any():
                 # max point outside eps

+ 1 - 1
taipy/gui/data/pandas_data_accessor.py

@@ -18,7 +18,7 @@ import pandas as pd
 
 from .._warnings import _warn
 from ..gui import Gui
-from ..gui_types import PropertyType
+from ..types import PropertyType
 from ..utils import _RE_PD_TYPE, _get_date_col_str_name
 from .data_accessor import _DataAccessor
 from .data_format import _DataFormat

+ 1 - 1
taipy/gui/extension/__init__.py

@@ -9,5 +9,5 @@
 # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 # specific language governing permissions and limitations under the License.
 
-from ..gui_types import PropertyType
+from ..types import PropertyType
 from .library import Element, ElementLibrary, ElementProperty

+ 1 - 1
taipy/gui/extension/library.py

@@ -20,7 +20,7 @@ from urllib.parse import urlencode
 
 from .._renderers.builder import _Builder
 from .._warnings import _warn
-from ..gui_types import PropertyType
+from ..types import PropertyType
 from ..utils import _get_broadcast_var_name, _TaipyBase, _to_camel_case
 
 if t.TYPE_CHECKING:

+ 20 - 4
taipy/gui/gui.py

@@ -56,11 +56,11 @@ from .data.data_accessor import _DataAccessor, _DataAccessors
 from .data.data_format import _DataFormat
 from .data.data_scope import _DataScopes
 from .extension.library import Element, ElementLibrary
-from .gui_types import _WsType
 from .page import Page
 from .partial import Partial
 from .server import _Server
 from .state import State
+from .types import _WsType
 from .utils import (
     _delscopeattr,
     _filter_locals,
@@ -623,7 +623,23 @@ class Gui:
                         self.__handle_ws_app_id(message)
                 self.__send_ack(message.get("ack_id"))
         except Exception as e:  # pragma: no cover
-            _warn(f"Decoding Message has failed: {message}", e)
+            if isinstance(e, AttributeError) and (name := message.get("name")):
+                try:
+                    names = self._get_real_var_name(name)
+                    var_name = names[0] if isinstance(names, tuple) else names
+                    var_context = names[1] if isinstance(names, tuple) else None
+                    if var_name.startswith("tpec_"):
+                        var_name = var_name[5:]
+                    if var_name.startswith("TpExPr_"):
+                        var_name = var_name[7:]
+                    _warn(
+                        f"A problem occurred while resolving variable '{var_name}'"
+                        + (f" in module '{var_context}'." if var_context else ".")
+                    )
+                except Exception as e1:
+                    _warn(f"Resolving  name '{name}' failed", e1)
+            else:
+                _warn(f"Decoding Message has failed: {message}", e)
 
     def __front_end_update(
         self,
@@ -1213,7 +1229,7 @@ class Gui:
 
     def __send_ws_update_with_dict(self, modified_values: dict) -> None:
         payload = [
-            {"name": _get_client_var_name(k), "payload": (v if isinstance(v, dict) and "value" in v else {"value": v})}
+            {"name": _get_client_var_name(k), "payload": v if isinstance(v, dict) and "value" in v else {"value": v}}
             for k, v in modified_values.items()
         ]
         if self._is_broadcasting():
@@ -2017,7 +2033,7 @@ class Gui:
 
     def _bind_custom_page_variables(self, page: CustomPage, client_id: t.Optional[str]):
         """Handle the bindings of custom page variables"""
-        with self.get_flask_app().app_context() if has_app_context() else contextlib.nullcontext(): # type: ignore[attr-defined]
+        with self.get_flask_app().app_context() if has_app_context() else contextlib.nullcontext():  # type: ignore[attr-defined]
             self.__set_client_id_in_context(client_id)
             with self._set_locals_context(page._get_module_name()):
                 for k in self._get_locals_bind().keys():

+ 1 - 3
taipy/gui/page.py

@@ -85,9 +85,7 @@ class Page:
         return (
             self._class_locals
             if self._is_class_module()
-            else None
-            if (frame := self._get_frame()) is None
-            else _filter_locals(frame.f_locals)
+            else None if (frame := self._get_frame()) is None else _filter_locals(frame.f_locals)
         )
 
     def _is_class_module(self):

+ 5 - 1
taipy/gui/partial.py

@@ -72,5 +72,9 @@ class Partial(_Page):
         if isinstance(content, Page):
             new_partial._renderer = content
         else:
-            new_partial._renderer = type(self._renderer)(content=content) if self._renderer is not None else None
+            new_partial._renderer = (
+                type(self._renderer)(content=content, frame=self._renderer._get_frame())
+                if self._renderer is not None
+                else None
+            )
         return new_partial

+ 1 - 0
taipy/gui/setup.py

@@ -89,6 +89,7 @@ setup(
     install_requires=get_requirements(),
     license="Apache License 2.0",
     include_package_data=True,
+    data_files=[("version", ["version.json"])],
     keywords="taipy-gui",
     name="taipy-gui",
     packages=find_namespace_packages(where=".") + find_packages(include=["taipy", "taipy.gui", "taipy.gui.*"]),

+ 2 - 4
taipy/gui/gui_types.py → taipy/gui/types.py

@@ -144,8 +144,7 @@ class PropertyType(Enum):
 
 
 @t.overload  # noqa: F811
-def _get_taipy_type(a_type: None) -> None:
-    ...
+def _get_taipy_type(a_type: None) -> None: ...
 
 
 @t.overload
@@ -161,8 +160,7 @@ def _get_taipy_type(a_type: PropertyType) -> t.Type[_TaipyBase]:  # noqa: F811
 @t.overload
 def _get_taipy_type(  # noqa: F811
     a_type: t.Optional[t.Union[t.Type[_TaipyBase], t.Type[Decimator], PropertyType]]
-) -> t.Optional[t.Union[t.Type[_TaipyBase], t.Type[Decimator], PropertyType]]:
-    ...
+) -> t.Optional[t.Union[t.Type[_TaipyBase], t.Type[Decimator], PropertyType]]: ...
 
 
 def _get_taipy_type(  # noqa: F811

+ 3 - 1
taipy/gui/utils/_locals_context.py

@@ -48,14 +48,16 @@ class _LocalsContext:
 
     @contextlib.contextmanager
     def set_locals_context(self, context: t.Optional[str]) -> t.Iterator[None]:
+        has_set_context = False
         try:
             if context in self._locals_map:
                 if hasattr(g, _LocalsContext.__ctx_g_name):
                     self._lc_stack.append(getattr(g, _LocalsContext.__ctx_g_name))
                 setattr(g, _LocalsContext.__ctx_g_name, context)
+                has_set_context = True
             yield
         finally:
-            if hasattr(g, _LocalsContext.__ctx_g_name):
+            if has_set_context and hasattr(g, _LocalsContext.__ctx_g_name):
                 if len(self._lc_stack) > 0:
                     setattr(g, _LocalsContext.__ctx_g_name, self._lc_stack.pop())
                 else:

+ 1 - 3
taipy/gui/utils/boolean.py

@@ -16,9 +16,7 @@ def _is_boolean_true(s: t.Union[bool, str]) -> bool:
     return (
         s
         if isinstance(s, bool)
-        else s.lower() in ["true", "1", "t", "y", "yes", "yeah", "sure"]
-        if isinstance(s, str)
-        else False
+        else s.lower() in ["true", "1", "t", "y", "yes", "yeah", "sure"] if isinstance(s, str) else False
     )
 
 

+ 6 - 6
taipy/gui/utils/chart_config_builder.py

@@ -16,7 +16,7 @@ from enum import Enum
 
 from .._renderers.utils import _get_columns_dict
 from .._warnings import _warn
-from ..gui_types import PropertyType
+from ..types import PropertyType
 from ..utils import _MapDict
 
 if t.TYPE_CHECKING:
@@ -250,11 +250,11 @@ def _build_chart_config(gui: "Gui", attributes: t.Dict[str, t.Any], col_types: t
             "orientations": [tr[_Chart_iprops.orientation.value] for tr in traces],
             "names": [tr[_Chart_iprops._name.value] for tr in traces],
             "lines": [
-                tr[_Chart_iprops.line.value]
-                if isinstance(tr[_Chart_iprops.line.value], (dict, _MapDict))
-                else {"dash": tr[_Chart_iprops.line.value]}
-                if tr[_Chart_iprops.line.value]
-                else None
+                (
+                    tr[_Chart_iprops.line.value]
+                    if isinstance(tr[_Chart_iprops.line.value], (dict, _MapDict))
+                    else {"dash": tr[_Chart_iprops.line.value]} if tr[_Chart_iprops.line.value] else None
+                )
                 for tr in traces
             ],
             "textAnchors": [tr[_Chart_iprops.text_anchor.value] for tr in traces],

+ 1 - 1
taipy/gui/version.json

@@ -1 +1 @@
-{"major": 3, "minor": 1, "patch": 0, "ext": "dev0"}
+{"major": 3, "minor": 1, "patch": 0, "ext": "dev2"}

+ 7 - 5
taipy/gui_core/_context.py

@@ -218,7 +218,8 @@ class _GuiCoreContext(CoreEventConsumerBase):
                         scenario_or_cycle.id,
                         scenario_or_cycle.get_simple_label(),
                         sorted(
-                            self.scenario_by_cycle.get(scenario_or_cycle, []), key=_GuiCoreContext.sort_by_creation_date
+                            self.scenario_by_cycle.get(scenario_or_cycle, []),
+                            key=_GuiCoreContext.get_entity_creation_date_iso,
                         ),
                         _EntityType.CYCLE.value,
                         False,
@@ -250,7 +251,7 @@ class _GuiCoreContext(CoreEventConsumerBase):
                     cycles_scenarios.extend(scenarios)
                 else:
                     cycles_scenarios.append(cycle)
-        return sorted(cycles_scenarios, key=_GuiCoreContext.sort_by_creation_date)
+        return sorted(cycles_scenarios, key=_GuiCoreContext.get_entity_creation_date_iso)
 
     def select_scenario(self, state: State, id: str, payload: t.Dict[str, str]):
         args = payload.get("args")
@@ -670,8 +671,9 @@ class _GuiCoreContext(CoreEventConsumerBase):
                         ent.properties.pop(key, None)
 
     @staticmethod
-    def sort_by_creation_date(entity: t.Union[Scenario, Cycle]):
-        return entity.creation_date
+    def get_entity_creation_date_iso(entity: t.Union[Scenario, Cycle]):
+        # we might be comparing naive and aware datetime ISO
+        return entity.creation_date.isoformat()
 
     def get_scenarios_for_owner(self, owner_id: str):
         cycles_scenarios: t.List[t.Union[Scenario, Cycle]] = []
@@ -691,7 +693,7 @@ class _GuiCoreContext(CoreEventConsumerBase):
                         cycles_scenarios.extend(scenarios_cycle)
                     elif isinstance(entity, Scenario):
                         cycles_scenarios.append(entity)
-        return sorted(cycles_scenarios, key=_GuiCoreContext.sort_by_creation_date)
+        return sorted(cycles_scenarios, key=_GuiCoreContext.get_entity_creation_date_iso)
 
     def get_data_node_history(self, datanode: DataNode, id: str):
         if (

+ 2 - 2
taipy/rest/__init__.py

@@ -12,13 +12,13 @@
 """# Taipy Rest
 
 The Taipy Rest package exposes the Runnable `Rest^` service to provide REST APIs on top of Taipy Core. (more details
-on Taipy Core functionalities in the [user manual](../../../manuals/core/)).
+on Taipy Core functionalities in the [user manual](../../../manuals/core/index.md)).
 
 Once the `Rest^` service runs, users can call REST APIs to create, read, update, submit and remove Taipy entities
 (including cycles, scenarios, sequences, tasks, jobs, and data nodes). It is handy when it comes to integrating a
 Taipy application in a more complex IT ecosystem.
 
-Please refer to [REST API](../../reference_rest/) page to get the exhaustive list of available APIs."""
+Please refer to [REST API](../../reference_rest/index.md) page to get the exhaustive list of available APIs."""
 
 from ._init import *
 from .version import _get_version

+ 1 - 0
taipy/rest/setup.py

@@ -47,6 +47,7 @@ setup(
     author_email="dev@taipy.io",
     packages=find_namespace_packages(where=".") + find_packages(include=["taipy", "taipy.rest"]),
     include_package_data=True,
+    data_files=[('version', ['version.json'])],
     long_description=readme,
     long_description_content_type="text/markdown",
     description="Library to expose taipy-core REST APIs.",

+ 1 - 1
taipy/rest/version.json

@@ -1 +1 @@
-{"major": 3, "minor": 1, "patch": 0, "ext": "dev0"}
+{"major": 3, "minor": 1, "patch": 0, "ext": "dev2"}

+ 1 - 0
taipy/templates/setup.py

@@ -51,6 +51,7 @@ setup(
     name="taipy-templates",
     packages=find_namespace_packages(where=".") + find_packages(include=["taipy"]),
     include_package_data=True,
+    data_files=[('version', ['version.json'])],
     test_suite="tests",
     url="https://github.com/avaiga/taipy-templates",
     version=version_string,

+ 1 - 1
taipy/templates/version.json

@@ -1 +1 @@
-{"major": 3, "minor": 1, "patch": 0, "ext": "dev0"}
+{"major": 3, "minor": 1, "patch": 0, "ext": "dev2"}

+ 1 - 1
taipy/version.json

@@ -1 +1 @@
-{"major": 3, "minor": 1, "patch": 0, "ext": "dev0"}
+{"major": 3, "minor": 1, "patch": 0, "ext": "dev2"}

+ 2 - 4
tests/core/_backup/test_backup.py

@@ -10,7 +10,6 @@
 # specific language governing permissions and limitations under the License.
 
 import os
-from unittest.mock import patch
 
 import pytest
 
@@ -47,9 +46,8 @@ backup_file_path = ".taipy_backups"
 
 
 def test_backup_storage_folder_when_core_run():
-    with patch("sys.argv", ["prog"]):
-        core = Core()
-        core.run()
+    core = Core()
+    core.run()
     backup_files = read_backup_file(backup_file_path)
     assert backup_files == [f"{Config.core.storage_folder}\n"]
     core.stop()

+ 1 - 0
tests/core/_orchestrator/test_orchestrator__cancel_jobs.py

@@ -8,6 +8,7 @@
 # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 # specific language governing permissions and limitations under the License.
+
 from taipy import Job, JobId, Status
 from taipy.config import Config
 from taipy.core import taipy

+ 4 - 4
tests/core/_orchestrator/test_orchestrator__is_blocked.py

@@ -35,7 +35,7 @@ def test_is_not_blocked_task_multiple_input_and_output():
     dn_0 = Config.configure_data_node("in_0", default_data="THIS")
     dn_1 = Config.configure_data_node("in_1", default_data="IS")
     dn_2 = Config.configure_data_node("in_2", default_data="DEFAULT")
-    out = Config.configure_data_node("output")
+    out = Config.configure_data_node("output_dn")
     t = Config.configure_task("the_task", nothing, [dn_0, dn_1, dn_2], [out])
     sc_conf = Config.configure_scenario("scenario", [t])
     scenario = taipy.create_scenario(sc_conf)
@@ -74,7 +74,7 @@ def test_is_blocked_task_single_input_edit_in_progress():
 def test_is_blocked_task_multiple_input_no_data():
     dn_0 = Config.configure_data_node("input_0", default_data="THIS")
     dn_1 = Config.configure_data_node("input_1")
-    out = Config.configure_data_node("output")
+    out = Config.configure_data_node("output_dn")
     t_config = Config.configure_task("the_task", nothing, [dn_0, dn_1], [out])
     sc_conf = Config.configure_scenario("scenario", [t_config])
     scenario = taipy.create_scenario(sc_conf)
@@ -101,7 +101,7 @@ def test_is_not_blocked_job_single_input():
 def test_is_not_blocked_job_multiple_input_and_output():
     in_0 = Config.configure_data_node("in_0", default_data="THIS")
     in_1 = Config.configure_data_node("in_1", default_data="IS")
-    out = Config.configure_data_node("output")
+    out = Config.configure_data_node("output_dn")
     t = Config.configure_task("the_task", nothing, [in_0, in_1], [out])
     sc_conf = Config.configure_scenario("scenario", [t])
     scenario = taipy.create_scenario(sc_conf)
@@ -144,7 +144,7 @@ def test_is_blocked_job_multiple_input_no_data():
     dn_0 = Config.configure_data_node("in_0", default_data="THIS")
     dn_1 = Config.configure_data_node("in_1", default_data="IS")
     dn_2 = Config.configure_data_node("in_2")
-    out = Config.configure_data_node("output")
+    out = Config.configure_data_node("output_dn")
     t = Config.configure_task("the_task", nothing, [dn_0, dn_1, dn_2], [out])
     sc_conf = Config.configure_scenario("scenario", [t])
     scenario = taipy.create_scenario(sc_conf)

+ 2 - 2
tests/core/_orchestrator/test_orchestrator__lock_dn_output_and_create_job.py

@@ -63,14 +63,14 @@ def test_lock_dn_and_create_job_with_callback_and_force():
 
 
 def test_lock_dn_and_create_job_one_output():
-    dn = Config.configure_data_node("output")
+    dn = Config.configure_data_node("output_dn")
     t = Config.configure_task("one_output", nothing, [], [dn])
     sc_conf = Config.configure_scenario("scenario", [t])
     scenario = taipy.create_scenario(sc_conf)
     orchestrator = _OrchestratorFactory._build_orchestrator()
     orchestrator._lock_dn_output_and_create_job(scenario.one_output, "submit_id", "scenario_id")
 
-    assert scenario.output.edit_in_progress
+    assert scenario.output_dn.edit_in_progress
 
 
 def test_lock_dn_and_create_job_multiple_outputs_one_input():

+ 3 - 2
tests/core/_orchestrator/test_orchestrator__orchestrate_job_to_run_or_block.py

@@ -8,6 +8,7 @@
 # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 # specific language governing permissions and limitations under the License.
+
 from taipy import Status
 from taipy.config import Config
 from taipy.core import taipy
@@ -50,9 +51,9 @@ def test_orchestrate_job_to_run_or_block_single_pending_job():
 
 
 def test_orchestrate_job_to_run_or_block_multiple_jobs():
-    input = Config.configure_data_node("input", default_data=1)  # Has default data
+    input = Config.configure_data_node("input_dn", default_data=1)  # Has default data
     intermediate = Config.configure_data_node("intermediate")  # Has default data
-    output = Config.configure_data_node("output")  # Has default data
+    output = Config.configure_data_node("output_dn")  # Has default data
     t1 = Config.configure_task("my_task_1", nothing, [input], [])
     t2 = Config.configure_task("my_task_2", nothing, [], [intermediate])
     t3 = Config.configure_task("my_task_3", nothing, [intermediate], [output])

+ 1 - 9
tests/core/config/checkers/test_migration_config_checker.py

@@ -24,14 +24,6 @@ def mock_func():
 
 
 def test_check_if_entity_property_key_used_is_predefined(caplog):
-    with patch("sys.argv", ["prog", "--production", "1.0"]):
-        core = Core()
-        core.run()
-    assert caplog.text == ""
-    core.stop()
-
-    caplog.clear()
-
     Config.unique_sections[MigrationConfig.name]._properties["_entity_owner"] = None
     with patch("sys.argv", ["prog", "--production", "1.0"]):
         with pytest.raises(SystemExit):
@@ -72,9 +64,9 @@ def test_check_valid_version(caplog):
     Config.unblock_update()
 
     with patch("sys.argv", ["prog", "--production", "2.0"]):
+        # No SystemExit should be raised
         core = Core()
         core.run()
-    assert caplog.text == ""
     core.stop()
 
 

+ 4 - 1
tests/core/conftest.py

@@ -14,6 +14,7 @@ import pickle
 import shutil
 from datetime import datetime
 from queue import Queue
+from unittest.mock import patch
 
 import pandas as pd
 import pytest
@@ -327,7 +328,8 @@ def clean_repository(init_config, init_managers, init_orchestrator, init_notifie
     init_config()
     init_notifier()
 
-    yield
+    with patch("sys.argv", ["prog"]):
+        yield
 
 
 @pytest.fixture
@@ -346,6 +348,7 @@ def init_config(reset_configuration_singleton, inject_core_sections):
 
         Config.configure_core(read_entity_retry=0)
         Core._is_running = False
+        Core._version_is_initialized = False
 
     return _init_config
 

+ 12 - 12
tests/core/data/test_data_manager.py

@@ -549,18 +549,18 @@ class TestDataManager:
             """
             [TAIPY]
 
-            [DATA_NODE.input]
+            [DATA_NODE.input_dn]
             scope = "SCENARIO:SCOPE"
             default_data = "21:int"
 
-            [DATA_NODE.output]
+            [DATA_NODE.output_dn]
             storage_type = "in_memory"
             scope = "SCENARIO:SCOPE"
 
             [TASK.double]
-            inputs = [ "input:SECTION",]
+            inputs = [ "input_dn:SECTION",]
             function = "math.sqrt:function"
-            outputs = [ "output:SECTION",]
+            outputs = [ "output_dn:SECTION",]
             skippable = "False:bool"
 
             [SCENARIO.my_scenario]
@@ -575,26 +575,26 @@ class TestDataManager:
         Config.override(file_config.filename)
         scenario = tp.create_scenario(Config.scenarios["my_scenario"])
 
-        assert isinstance(scenario.input, PickleDataNode)
-        assert isinstance(scenario.output, InMemoryDataNode)
+        assert isinstance(scenario.input_dn, PickleDataNode)
+        assert isinstance(scenario.output_dn, InMemoryDataNode)
 
     def test_create_dn_from_loaded_config_modified_default_config(self):
         file_config = NamedTemporaryFile(
             """
             [TAIPY]
 
-            [DATA_NODE.input]
+            [DATA_NODE.input_dn]
             scope = "SCENARIO:SCOPE"
             default_path="fake/path.csv"
 
-            [DATA_NODE.output]
+            [DATA_NODE.output_dn]
             storage_type = "in_memory"
             scope = "SCENARIO:SCOPE"
 
             [TASK.double]
-            inputs = [ "input:SECTION",]
+            inputs = [ "input_dn:SECTION",]
             function = "math.sqrt:function"
-            outputs = [ "output:SECTION",]
+            outputs = [ "output_dn:SECTION",]
             skippable = "False:bool"
 
             [SCENARIO.my_scenario]
@@ -610,8 +610,8 @@ class TestDataManager:
         Config.override(file_config.filename)
         scenario = tp.create_scenario(Config.scenarios["my_scenario"])
 
-        assert isinstance(scenario.input, CSVDataNode)
-        assert isinstance(scenario.output, InMemoryDataNode)
+        assert isinstance(scenario.input_dn, CSVDataNode)
+        assert isinstance(scenario.output_dn, InMemoryDataNode)
 
     def test_get_tasks_by_config_id(self):
         dn_config_1 = Config.configure_data_node("dn_1", scope=Scope.SCENARIO)

+ 2 - 1
tests/core/notification/test_core_event_consumer.py

@@ -16,6 +16,7 @@ from taipy.core import taipy as tp
 from taipy.core.notification.core_event_consumer import CoreEventConsumerBase
 from taipy.core.notification.event import Event, EventEntityType, EventOperation
 from taipy.core.notification.notifier import Notifier
+from taipy.core.scenario._scenario_manager_factory import _ScenarioManagerFactory
 from tests.core.utils import assert_true_after_time
 
 
@@ -79,7 +80,7 @@ def test_core_event_consumer():
     )
 
     # Create a scenario trigger 5 creation events
-    scenario = tp.create_scenario(scenario_config)
+    scenario = _ScenarioManagerFactory._build_manager()._create(scenario_config)
     assert_true_after_time(lambda: all_evt_csumer_0.event_collected == 5, time=10)
     assert_true_after_time(lambda: len(all_evt_csumer_0.event_entity_type_collected) == 5, time=10)
     assert_true_after_time(lambda: all_evt_csumer_0.event_operation_collected[EventOperation.CREATION] == 5, time=10)

+ 6 - 4
tests/core/notification/test_events_published.py

@@ -17,6 +17,7 @@ from taipy.core.job.status import Status
 from taipy.core.notification.core_event_consumer import CoreEventConsumerBase
 from taipy.core.notification.event import Event, EventEntityType, EventOperation
 from taipy.core.notification.notifier import Notifier
+from taipy.core.scenario._scenario_manager_factory import _ScenarioManagerFactory
 
 
 class Snapshot:
@@ -85,8 +86,9 @@ def test_events_published_for_scenario_creation():
     register_id_0, register_queue_0 = Notifier.register()
     all_evts = RecordingConsumer(register_id_0, register_queue_0)
     all_evts.start()
-    # Create a scenario only trigger 6 creation events (for cycle, data node(x2), task, sequence and scenario)
-    tp.create_scenario(sc_config)
+    # Create a scenario via the manager
+    # should only trigger 6 creation events (for cycle, data node(x2), task, sequence and scenario)
+    _ScenarioManagerFactory._build_manager()._create(sc_config)
     snapshot = all_evts.capture()
 
     assert len(snapshot.collected_events) == 6
@@ -249,7 +251,7 @@ def test_job_events():
     consumer.start()
 
     # Create scenario
-    scenario = tp.create_scenario(sc_config)
+    scenario = _ScenarioManagerFactory._build_manager()._create(sc_config)
     snapshot = consumer.capture()
     assert len(snapshot.collected_events) == 0
 
@@ -329,7 +331,7 @@ def test_data_node_events():
     consumer = RecordingConsumer(register_id, register_queue)
     consumer.start()
 
-    scenario = tp.create_scenario(sc_config)
+    scenario = _ScenarioManagerFactory._build_manager()._create(sc_config)
 
     snapshot = consumer.capture()
     # We expect two creation events since we have two data nodes:

+ 9 - 7
tests/core/notification/test_notifier.py

@@ -13,10 +13,12 @@ from queue import SimpleQueue
 
 from taipy.config import Config, Frequency
 from taipy.core import taipy as tp
+from taipy.core._version._version_manager_factory import _VersionManagerFactory
 from taipy.core.notification import EventEntityType, EventOperation
 from taipy.core.notification._topic import _Topic
 from taipy.core.notification.event import Event
 from taipy.core.notification.notifier import Notifier
+from taipy.core.scenario._scenario_manager_factory import _ScenarioManagerFactory
 
 
 def test_register():
@@ -317,7 +319,7 @@ def test_publish_creation_event():
 
     # Test CREATION Event
 
-    scenario = tp.create_scenario(scenario_config)
+    scenario = _ScenarioManagerFactory._build_manager()._create(scenario_config)
     cycle = scenario.cycle
     task = scenario.tasks[task_config.id]
     dn = scenario.data_nodes[dn_config.id]
@@ -357,7 +359,7 @@ def test_publish_update_event():
     )
     scenario_config.add_sequences({"sequence_config": [task_config]})
 
-    scenario = tp.create_scenario(scenario_config)
+    scenario = _ScenarioManagerFactory._build_manager()._create(scenario_config)
     cycle = scenario.cycle
     task = scenario.tasks[task_config.id]
     dn = scenario.data_nodes[dn_config.id]
@@ -554,7 +556,7 @@ def test_publish_update_event_in_context_manager():
     )
     scenario_config.add_sequences({"sequence_config": [task_config]})
 
-    scenario = tp.create_scenario(scenario_config)
+    scenario = _ScenarioManagerFactory._build_manager()._create(scenario_config)
     cycle = scenario.cycle
     task = scenario.tasks[task_config.id]
     dn = scenario.data_nodes[dn_config.id]
@@ -699,7 +701,7 @@ def test_publish_submission_event():
         "scenario_config", [task_config], frequency=Frequency.DAILY, flag="test"
     )
     scenario_config.add_sequences({"sequence_config": [task_config]})
-    scenario = tp.create_scenario(scenario_config)
+    scenario = _ScenarioManagerFactory._build_manager()._create(scenario_config)
 
     assert registration_queue.qsize() == 5
     while registration_queue.qsize() > 0:
@@ -751,7 +753,7 @@ def test_publish_deletion_event():
         "scenario_config", [task_config], frequency=Frequency.DAILY, flag="test"
     )
     scenario_config.add_sequences({"sequence_config": [task_config]})
-    scenario = tp.create_scenario(scenario_config)
+    scenario = _ScenarioManagerFactory._build_manager()._create(scenario_config)
     cycle = scenario.cycle
     task = scenario.tasks[task_config.id]
     dn = scenario.data_nodes[dn_config.id]
@@ -793,7 +795,7 @@ def test_publish_deletion_event():
         for i, event in enumerate(published_events)
     )
 
-    scenario = tp.create_scenario(scenario_config)
+    scenario = _ScenarioManagerFactory._build_manager()._create(scenario_config)
     cycle = scenario.cycle
     assert registration_queue.qsize() == 5
 
@@ -801,7 +803,7 @@ def test_publish_deletion_event():
     while registration_queue.qsize() != 0:
         registration_queue.get()
 
-    tp.clean_all_entities_by_version()
+    tp.clean_all_entities(_VersionManagerFactory._build_manager()._get_latest_version())
     assert registration_queue.qsize() == 6
 
     published_events = []

+ 23 - 26
tests/core/test_complex_application.py

@@ -12,7 +12,6 @@
 import os
 import pathlib
 from time import sleep
-from unittest.mock import patch
 
 import pandas as pd
 
@@ -76,25 +75,24 @@ def test_skipped_jobs():
     task_config_2 = Config.configure_task("second", mult_by_2, intermediate_config, output_config, skippable=True)
     scenario_config = Config.configure_scenario("scenario", [task_config_1, task_config_2])
 
-    with patch("sys.argv", ["prog"]):
-        core = Core()
-        core.run()
-
-        scenario = tp.create_scenario(scenario_config)
-        scenario.input_dn.write(2)
-        scenario.submit()
-        assert len(tp.get_jobs()) == 2
-        for job in tp.get_jobs():
-            assert job.status == Status.COMPLETED
-        scenario.submit()
-        assert len(tp.get_jobs()) == 4
-        skipped = []
-        for job in tp.get_jobs():
-            if job.status != Status.COMPLETED:
-                assert job.status == Status.SKIPPED
-                skipped.append(job)
-        assert len(skipped) == 2
-        core.stop()
+    core = Core()
+    core.run()
+
+    scenario = tp.create_scenario(scenario_config)
+    scenario.input_dn.write(2)
+    scenario.submit()
+    assert len(tp.get_jobs()) == 2
+    for job in tp.get_jobs():
+        assert job.status == Status.COMPLETED
+    scenario.submit()
+    assert len(tp.get_jobs()) == 4
+    skipped = []
+    for job in tp.get_jobs():
+        if job.status != Status.COMPLETED:
+            assert job.status == Status.SKIPPED
+            skipped.append(job)
+    assert len(skipped) == 2
+    core.stop()
 
 
 def test_complex():
@@ -173,12 +171,11 @@ def test_complex():
         ],
     )
 
-    with patch("sys.argv", ["prog"]):
-        core = Core()
-        core.run()
-        scenario = tp.create_scenario(scenario_config)
-        tp.submit(scenario)
-        core.stop()
+    core = Core()
+    core.run()
+    scenario = tp.create_scenario(scenario_config)
+    tp.submit(scenario)
+    core.stop()
 
     csv_sum_res = pd.read_csv(csv_path_sum)
     excel_sum_res = pd.read_excel(excel_path_sum)

+ 59 - 66
tests/core/test_core.py

@@ -9,7 +9,6 @@
 # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 # specific language governing permissions and limitations under the License.
 
-from unittest.mock import patch
 
 import pytest
 
@@ -26,10 +25,9 @@ from taipy.core.exceptions.exceptions import CoreServiceIsAlreadyRunning
 class TestCore:
     def test_run_core_trigger_config_check(self, caplog):
         Config.configure_data_node(id="d0", storage_type="toto")
-        with patch("sys.argv", ["prog"]):
-            with pytest.raises(SystemExit):
-                core = Core()
-                core.run()
+        with pytest.raises(SystemExit):
+            core = Core()
+            core.run()
         expected_error_message = (
             "`storage_type` field of DataNodeConfig `d0` must be either csv, sql_table,"
             " sql, mongo_collection, pickle, excel, generic, json, parquet, s3_object, or in_memory."
@@ -41,83 +39,78 @@ class TestCore:
     def test_run_core_as_a_service_development_mode(self):
         _OrchestratorFactory._dispatcher = None
 
-        with patch("sys.argv", ["prog"]):
-            core = Core()
-            assert core._orchestrator is None
-            assert core._dispatcher is None
-            assert _OrchestratorFactory._dispatcher is None
-
-            core.run()
-            assert core._orchestrator is not None
-            assert core._orchestrator == _Orchestrator
-            assert _OrchestratorFactory._orchestrator is not None
-            assert _OrchestratorFactory._orchestrator == _Orchestrator
-            assert core._dispatcher is not None
-            assert isinstance(core._dispatcher, _DevelopmentJobDispatcher)
-            assert isinstance(_OrchestratorFactory._dispatcher, _DevelopmentJobDispatcher)
-            core.stop()
+        core = Core()
+        assert core._orchestrator is None
+        assert core._dispatcher is None
+        assert _OrchestratorFactory._dispatcher is None
+
+        core.run()
+        assert core._orchestrator is not None
+        assert core._orchestrator == _Orchestrator
+        assert _OrchestratorFactory._orchestrator is not None
+        assert _OrchestratorFactory._orchestrator == _Orchestrator
+        assert core._dispatcher is not None
+        assert isinstance(core._dispatcher, _DevelopmentJobDispatcher)
+        assert isinstance(_OrchestratorFactory._dispatcher, _DevelopmentJobDispatcher)
+        core.stop()
 
     def test_run_core_as_a_service_standalone_mode(self):
         _OrchestratorFactory._dispatcher = None
 
-        with patch("sys.argv", ["prog"]):
-            core = Core()
-            assert core._orchestrator is None
-
-            assert core._dispatcher is None
-            assert _OrchestratorFactory._dispatcher is None
-
-            Config.configure_job_executions(mode=JobConfig._STANDALONE_MODE, max_nb_of_workers=2)
-            core.run()
-            assert core._orchestrator is not None
-            assert core._orchestrator == _Orchestrator
-            assert _OrchestratorFactory._orchestrator is not None
-            assert _OrchestratorFactory._orchestrator == _Orchestrator
-            assert core._dispatcher is not None
-            assert isinstance(core._dispatcher, _StandaloneJobDispatcher)
-            assert isinstance(_OrchestratorFactory._dispatcher, _StandaloneJobDispatcher)
-            assert core._dispatcher.is_running()
-            assert _OrchestratorFactory._dispatcher.is_running()
-            core.stop()
+        core = Core()
+        assert core._orchestrator is None
+
+        assert core._dispatcher is None
+        assert _OrchestratorFactory._dispatcher is None
+
+        Config.configure_job_executions(mode=JobConfig._STANDALONE_MODE, max_nb_of_workers=2)
+        core.run()
+        assert core._orchestrator is not None
+        assert core._orchestrator == _Orchestrator
+        assert _OrchestratorFactory._orchestrator is not None
+        assert _OrchestratorFactory._orchestrator == _Orchestrator
+        assert core._dispatcher is not None
+        assert isinstance(core._dispatcher, _StandaloneJobDispatcher)
+        assert isinstance(_OrchestratorFactory._dispatcher, _StandaloneJobDispatcher)
+        assert core._dispatcher.is_running()
+        assert _OrchestratorFactory._dispatcher.is_running()
+        core.stop()
 
     def test_core_service_can_only_be_run_once(self):
-        with patch("sys.argv", ["prog"]):
-            core_instance_1 = Core()
-            core_instance_2 = Core()
+        core_instance_1 = Core()
+        core_instance_2 = Core()
 
-            core_instance_1.run()
-
-            with pytest.raises(CoreServiceIsAlreadyRunning):
-                core_instance_1.run()
-            with pytest.raises(CoreServiceIsAlreadyRunning):
-                core_instance_2.run()
-
-            # Stop the Core service and run it again should work
-            core_instance_1.stop()
+        core_instance_1.run()
 
+        with pytest.raises(CoreServiceIsAlreadyRunning):
             core_instance_1.run()
-            core_instance_1.stop()
+        with pytest.raises(CoreServiceIsAlreadyRunning):
             core_instance_2.run()
-            core_instance_2.stop()
+
+        # Stop the Core service and run it again should work
+        core_instance_1.stop()
+
+        core_instance_1.run()
+        core_instance_1.stop()
+        core_instance_2.run()
+        core_instance_2.stop()
 
     def test_block_config_update_when_core_service_is_running_development_mode(self):
         _OrchestratorFactory._dispatcher = None
 
-        with patch("sys.argv", ["prog"]):
-            core = Core()
-            core.run()
-            with pytest.raises(ConfigurationUpdateBlocked):
-                Config.configure_data_node(id="i1")
-            core.stop()
+        core = Core()
+        core.run()
+        with pytest.raises(ConfigurationUpdateBlocked):
+            Config.configure_data_node(id="i1")
+        core.stop()
 
     @pytest.mark.standalone
     def test_block_config_update_when_core_service_is_running_standalone_mode(self):
         _OrchestratorFactory._dispatcher = None
 
-        with patch("sys.argv", ["prog"]):
-            core = Core()
-            Config.configure_job_executions(mode=JobConfig._STANDALONE_MODE, max_nb_of_workers=2)
-            core.run()
-            with pytest.raises(ConfigurationUpdateBlocked):
-                Config.configure_data_node(id="i1")
-            core.stop()
+        core = Core()
+        Config.configure_job_executions(mode=JobConfig._STANDALONE_MODE, max_nb_of_workers=2)
+        core.run()
+        with pytest.raises(ConfigurationUpdateBlocked):
+            Config.configure_data_node(id="i1")
+        core.stop()

+ 19 - 14
tests/core/test_taipy.py

@@ -352,7 +352,7 @@ class TestTaipy:
                 tp.submit(scenario)
 
         assert len(warning) == 1
-        assert warning[0].message.args[0] == "The Core service is NOT running"
+        assert "The Core service is NOT running" in warning[0].message.args[0]
 
     def test_get_tasks(self):
         with mock.patch("taipy.core.task._task_manager._TaskManager._get_all") as mck:
@@ -636,28 +636,33 @@ class TestTaipy:
             mck.assert_called_once_with(cycle_id)
 
     def test_create_global_data_node(self):
-        dn_cfg = DataNodeConfig("id", "pickle", Scope.GLOBAL)
-        with mock.patch("taipy.core.data._data_manager._DataManager._create_and_set") as mck:
-            dn = tp.create_global_data_node(dn_cfg)
-            mck.assert_called_once_with(dn_cfg, None, None)
-
-        dn = tp.create_global_data_node(dn_cfg)
+        dn_cfg_global = DataNodeConfig("id", "pickle", Scope.GLOBAL)
+        dn_cfg_scenario = DataNodeConfig("id", "pickle", Scope.SCENARIO)
+        with mock.patch("taipy.core.data._data_manager._DataManager._create_and_set") as dn_create_mock:
+            with mock.patch("taipy.core._core.Core._manage_version_and_block_config") as mv_mock:
+                dn = tp.create_global_data_node(dn_cfg_global)
+                dn_create_mock.assert_called_once_with(dn_cfg_global, None, None)
+                mv_mock.assert_called_once()
+
+        dn = tp.create_global_data_node(dn_cfg_global)
         assert dn.scope == Scope.GLOBAL
-        assert dn.config_id == dn_cfg.id
+        assert dn.config_id == dn_cfg_global.id
+        assert _VersionManager._get(dn.version) is not None
 
         # Create a global data node from the same configuration should return the same data node
-        dn_2 = tp.create_global_data_node(dn_cfg)
+        dn_2 = tp.create_global_data_node(dn_cfg_global)
         assert dn_2.id == dn.id
 
-        dn_cfg.scope = Scope.SCENARIO
         with pytest.raises(DataNodeConfigIsNotGlobal):
-            tp.create_global_data_node(dn_cfg)
+            tp.create_global_data_node(dn_cfg_scenario)
 
-    def test_create_scenario(self, scenario):
+    def test_create_scenario(self):
         scenario_config = ScenarioConfig("scenario_config")
         with mock.patch("taipy.core.scenario._scenario_manager._ScenarioManager._create") as mck:
-            tp.create_scenario(scenario_config)
-            mck.assert_called_once_with(scenario_config, None, None)
+            with mock.patch("taipy.core._core.Core._manage_version_and_block_config") as mv_mock:
+                tp.create_scenario(scenario_config)
+                mck.assert_called_once_with(scenario_config, None, None)
+                mv_mock.assert_called_once()
         with mock.patch("taipy.core.scenario._scenario_manager._ScenarioManager._create") as mck:
             tp.create_scenario(scenario_config, datetime.datetime(2022, 2, 5))
             mck.assert_called_once_with(scenario_config, datetime.datetime(2022, 2, 5), None)

+ 1 - 3
tests/gui/actions/test_download.py

@@ -10,7 +10,6 @@
 # specific language governing permissions and limitations under the License.
 
 import inspect
-from unittest.mock import patch
 
 from flask import g
 
@@ -28,8 +27,7 @@ def test_download(gui: Gui, helpers):
     gui._set_frame(inspect.currentframe())
 
     gui.add_page("test", Markdown("<|Hello {name}|button|id={btn_id}|>"))
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     flask_client = gui._server.test_client()
     # WS client and emit
     ws_client = gui._server._ws.test_client(gui._server.get_flask())

+ 1 - 3
tests/gui/actions/test_get_state_id.py

@@ -10,7 +10,6 @@
 # specific language governing permissions and limitations under the License.
 
 import inspect
-from unittest.mock import patch
 
 from flask import g
 
@@ -25,8 +24,7 @@ def test_get_state_id(gui: Gui, helpers):
     gui._set_frame(inspect.currentframe())
 
     gui.add_page("test", Markdown("<|Hello {name}|button|id={btn_id}|>"))
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     flask_client = gui._server.test_client()
     cid = helpers.create_scope_and_get_sid(gui)
     # Get the jsx once so that the page will be evaluated -> variable will be registered

+ 1 - 3
tests/gui/actions/test_hold_control.py

@@ -10,7 +10,6 @@
 # specific language governing permissions and limitations under the License.
 
 import inspect
-from unittest.mock import patch
 
 from flask import g
 
@@ -25,8 +24,7 @@ def test_hold_control(gui: Gui, helpers):
     gui._set_frame(inspect.currentframe())
 
     gui.add_page("test", Markdown("<|Hello {name}|button|id={btn_id}|>"))
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     flask_client = gui._server.test_client()
     # WS client and emit
     ws_client = gui._server._ws.test_client(gui._server.get_flask())

+ 1 - 3
tests/gui/actions/test_invoke_callback.py

@@ -10,7 +10,6 @@
 # specific language governing permissions and limitations under the License.
 
 import inspect
-from unittest.mock import patch
 
 from flask import g
 
@@ -30,8 +29,7 @@ def test_invoke_callback(gui: Gui, helpers):
     gui._set_frame(inspect.currentframe())
 
     gui.add_page("test", Markdown("<|Hello {name}|button|id={btn_id}|>\n<|{val}|>"))
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False, single_client=True)
+    gui.run(run_server=False, single_client=True)
     flask_client = gui._server.test_client()
     # client id
     cid = helpers.create_scope_and_get_sid(gui)

+ 1 - 3
tests/gui/actions/test_navigate.py

@@ -10,7 +10,6 @@
 # specific language governing permissions and limitations under the License.
 
 import inspect
-from unittest.mock import patch
 
 from flask import g
 
@@ -25,8 +24,7 @@ def test_navigate(gui: Gui, helpers):
     gui._set_frame(inspect.currentframe())
 
     gui.add_page("test", Markdown("<|Hello {name}|button|id={btn_id}|>"))
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     flask_client = gui._server.test_client()
     # WS client and emit
     ws_client = gui._server._ws.test_client(gui._server.get_flask())

+ 1 - 3
tests/gui/actions/test_notify.py

@@ -10,7 +10,6 @@
 # specific language governing permissions and limitations under the License.
 
 import inspect
-from unittest.mock import patch
 
 from flask import g
 
@@ -25,8 +24,7 @@ def test_notify(gui: Gui, helpers):
     gui._set_frame(inspect.currentframe())
 
     gui.add_page("test", Markdown("<|Hello {name}|button|id={btn_id}|>"))
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     flask_client = gui._server.test_client()
     # WS client and emit
     ws_client = gui._server._ws.test_client(gui._server.get_flask())

+ 1 - 3
tests/gui/actions/test_resume_control.py

@@ -10,7 +10,6 @@
 # specific language governing permissions and limitations under the License.
 
 import inspect
-from unittest.mock import patch
 
 from flask import g
 
@@ -25,8 +24,7 @@ def test_resume_control(gui: Gui, helpers):
     gui._set_frame(inspect.currentframe())
 
     gui.add_page("test", Markdown("<|Hello {name}|button|id={btn_id}|>"))
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     flask_client = gui._server.test_client()
     # WS client and emit
     ws_client = gui._server._ws.test_client(gui._server.get_flask())

+ 7 - 0
tests/gui/conftest.py

@@ -11,6 +11,7 @@
 
 import os
 from pathlib import Path
+from unittest.mock import patch
 
 import pandas as pd  # type: ignore
 import pytest
@@ -60,3 +61,9 @@ def test_client():
         with flask_app.app_context():
             g.client_id = "test client id"
             yield testing_client  # this is where the testing happens!
+
+
+@pytest.fixture(scope="function", autouse=True)
+def patch_cli_args():
+    with patch("sys.argv", ["prog"]):
+        yield

+ 1 - 3
tests/gui/data/test_pandas_data_accessor.py

@@ -12,7 +12,6 @@
 import inspect
 from datetime import datetime
 from importlib import util
-from unittest.mock import patch
 
 import pandas  # type: ignore
 from flask import g
@@ -212,8 +211,7 @@ def test_decimator(gui: Gui, helpers, small_dataframe):
     gui._set_frame(inspect.currentframe())
 
     gui.add_page("test", "<|Hello {a_decimator}|button|id={btn_id}|>")
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     flask_client = gui._server.test_client()
 
     cid = helpers.create_scope_and_get_sid(gui)

+ 1 - 3
tests/gui/gui_specific/test_folder_pages_binding.py

@@ -12,7 +12,6 @@
 import inspect
 import os
 from pathlib import Path
-from unittest.mock import patch
 
 from taipy.gui import Gui
 
@@ -21,7 +20,6 @@ def test_folder_pages_binding(gui: Gui):
     folder_path = f"{Path(Path(__file__).parent.resolve())}{os.path.sep}sample_assets"
     gui._set_frame(inspect.currentframe())
     gui.add_pages(folder_path)
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     assert len(gui._config.routes) == 3  # 2 files -> 2 routes + 1 default route
     assert len(gui._config.pages) == 3  # 2 files -> 2 pages + 1 default page

+ 7 - 15
tests/gui/gui_specific/test_gui.py

@@ -10,7 +10,6 @@
 # specific language governing permissions and limitations under the License.
 
 import json
-from unittest.mock import patch
 
 import pandas as pd
 import pytest
@@ -25,24 +24,21 @@ def test__get_real_var_name(gui: Gui):
     assert res[0] == ""
     assert res[1] == ""
 
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     with gui.get_flask_app().app_context():
         with pytest.raises(NameError):
             res = gui._get_real_var_name(f"{_TaipyContent.get_hash()}_var")
 
 
 def test__get_user_instance(gui: Gui):
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     with gui.get_flask_app().app_context():
         with pytest.warns(UserWarning):
             gui._get_user_instance("", type(None))
 
 
 def test__call_broadcast_callback(gui: Gui):
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     with gui.get_flask_app().app_context():
         res = gui._call_broadcast_callback(lambda s, t: t, ["Hello World"], "mine")
         assert res == "Hello World"
@@ -54,8 +50,7 @@ def test__call_broadcast_callback(gui: Gui):
 
 
 def test__refresh_expr(gui: Gui):
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     with gui.get_flask_app().app_context():
         res = gui._refresh_expr("var", None)
         assert res is None
@@ -63,8 +58,7 @@ def test__refresh_expr(gui: Gui):
 
 def test__tbl_cols(gui: Gui):
     data = pd.DataFrame({"col1": [0, 1, 2], "col2": [True, True, False]})
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     with gui.get_flask_app().app_context():
         res = gui._tbl_cols(True, None, json.dumps({}), json.dumps({"data": "data"}), data=data)
         assert isinstance(res, str)
@@ -79,8 +73,7 @@ def test__tbl_cols(gui: Gui):
 
 def test__chart_conf(gui: Gui):
     data = pd.DataFrame({"col1": [0, 1, 2], "col2": [True, True, False]})
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     with gui.get_flask_app().app_context():
         res = gui._chart_conf(True, None, json.dumps({}), json.dumps({"data": "data"}), data=data)
         assert isinstance(res, str)
@@ -98,8 +91,7 @@ def test__chart_conf(gui: Gui):
 
 
 def test__get_valid_adapter_result(gui: Gui):
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     with gui.get_flask_app().app_context():
         res = gui._get_valid_adapter_result(("id", "label"))
         assert isinstance(res, tuple)

+ 1 - 3
tests/gui/gui_specific/test_locals_context.py

@@ -9,7 +9,6 @@
 # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 # specific language governing permissions and limitations under the License.
 
-from unittest.mock import patch
 
 import pytest
 
@@ -19,8 +18,7 @@ from taipy.gui.utils._locals_context import _LocalsContext
 
 def test_locals_context(gui: Gui):
     lc = _LocalsContext()
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     with gui.get_flask_app().app_context():
         with pytest.raises(KeyError):
             lc.get_default()

+ 2 - 4
tests/gui/gui_specific/test_multiple_instances.py

@@ -10,7 +10,6 @@
 # specific language governing permissions and limitations under the License.
 
 import json
-from unittest.mock import patch
 
 from taipy.gui.gui import Gui
 
@@ -18,9 +17,8 @@ from taipy.gui.gui import Gui
 def test_multiple_instance():
     gui1 = Gui("<|gui1|>")
     gui2 = Gui("<|gui2|>")
-    with patch("sys.argv", ["prog"]):
-        gui1.run(run_server=False)
-        gui2.run(run_server=False)
+    gui1.run(run_server=False)
+    gui2.run(run_server=False)
     client1 = gui1._server.test_client()
     client2 = gui2._server.test_client()
     assert_multiple_instance(client1, 'value="gui1"')

+ 4 - 9
tests/gui/gui_specific/test_navigate.py

@@ -11,7 +11,6 @@
 
 import inspect
 import warnings
-from unittest.mock import patch
 
 from taipy.gui import Gui, Markdown, State, navigate
 
@@ -23,8 +22,7 @@ def test_navigate(gui: Gui, helpers):
     with warnings.catch_warnings(record=True):
         gui._set_frame(inspect.currentframe())
         gui.add_page("test", Markdown("#This is a page"))
-        with patch("sys.argv", ["prog"]):
-            gui.run(run_server=False)
+        gui.run(run_server=False)
         client = gui._server.test_client()
         # WS client and emit
         ws_client = gui._server._ws.test_client(gui._server.get_flask())
@@ -43,8 +41,7 @@ def test_navigate_to_no_route(gui: Gui, helpers):
     with warnings.catch_warnings(record=True):
         gui._set_frame(inspect.currentframe())
         gui.add_page("test", Markdown("#This is a page"))
-        with patch("sys.argv", ["prog"]):
-            gui.run(run_server=False)
+        gui.run(run_server=False)
         client = gui._server.test_client()
         # WS client and emit
         ws_client = gui._server._ws.test_client(gui._server.get_flask())
@@ -63,8 +60,7 @@ def test_on_navigate_to_inexistant(gui: Gui, helpers):
     with warnings.catch_warnings(record=True) as records:
         gui._set_frame(inspect.currentframe())
         gui.add_page("test", Markdown("#This is a page"))
-        with patch("sys.argv", ["prog"]):
-            gui.run(run_server=False)
+        gui.run(run_server=False)
         client = gui._server.test_client()
         # Get the jsx once so that the page will be evaluated -> variable will be registered
         sid = helpers.create_scope_and_get_sid(gui)
@@ -83,8 +79,7 @@ def test_on_navigate_to_existant(gui: Gui, helpers):
         gui._set_frame(inspect.currentframe())
         gui.add_page("test1", Markdown("#This is a page test1"))
         gui.add_page("test2", Markdown("#This is a page test2"))
-        with patch("sys.argv", ["prog"]):
-            gui.run(run_server=False)
+        gui.run(run_server=False)
         client = gui._server.test_client()
         # Get the jsx once so that the page will be evaluated -> variable will be registered
         sid = helpers.create_scope_and_get_sid(gui)

+ 2 - 5
tests/gui/gui_specific/test_partial.py

@@ -12,7 +12,6 @@
 import json
 import warnings
 from types import SimpleNamespace
-from unittest.mock import patch
 
 from taipy.gui import Gui, Markdown
 
@@ -20,8 +19,7 @@ from taipy.gui import Gui, Markdown
 def test_partial(gui: Gui):
     with warnings.catch_warnings(record=True):
         gui.add_partial(Markdown("#This is a partial"))
-        with patch("sys.argv", ["prog"]):
-            gui.run(run_server=False)
+        gui.run(run_server=False)
         client = gui._server.test_client()
         response = client.get(f"/taipy-jsx/{gui._config.partial_routes[0]}")
         response_data = json.loads(response.get_data().decode("utf-8", "ignore"))
@@ -32,8 +30,7 @@ def test_partial(gui: Gui):
 def test_partial_update(gui: Gui):
     with warnings.catch_warnings(record=True):
         partial = gui.add_partial(Markdown("#This is a partial"))
-        with patch("sys.argv", ["prog"]):
-            gui.run(run_server=False, single_client=True)
+        gui.run(run_server=False, single_client=True)
         client = gui._server.test_client()
         response = client.get(f"/taipy-jsx/{gui._config.partial_routes[0]}")
         response_data = json.loads(response.get_data().decode("utf-8", "ignore"))

+ 1 - 3
tests/gui/gui_specific/test_render_route.py

@@ -12,7 +12,6 @@
 import inspect
 import json
 import warnings
-from unittest.mock import patch
 
 from taipy.gui import Gui
 
@@ -21,8 +20,7 @@ def test_render_route(gui: Gui):
     gui._set_frame(inspect.currentframe())
     gui.add_page("page1", "# first page")
     gui.add_page("page2", "# second page")
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     with warnings.catch_warnings(record=True):
         client = gui._server.test_client()
         response = client.get("/taipy-init")

+ 2 - 5
tests/gui/gui_specific/test_run_thread.py

@@ -11,7 +11,6 @@
 
 import inspect
 import time
-from unittest.mock import patch
 from urllib.request import urlopen
 
 from taipy.gui import Gui
@@ -21,16 +20,14 @@ from taipy.gui import Gui
 def test_run_thread(gui: Gui, helpers):
     gui._set_frame(inspect.currentframe())
     gui.add_page("page1", "# first page")
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_in_thread=True, run_browser=False)
+    gui.run(run_in_thread=True, run_browser=False)
     while not helpers.port_check():
         time.sleep(0.1)
     assert ">first page</h1>" in urlopen("http://127.0.0.1:5000/taipy-jsx/page1").read().decode("utf-8")
     gui.stop()
     while helpers.port_check():
         time.sleep(0.1)
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_in_thread=True, run_browser=False)
+    gui.run(run_in_thread=True, run_browser=False)
     while not helpers.port_check():
         time.sleep(0.1)
     assert ">first page</h1>" in urlopen("http://127.0.0.1:5000/taipy-jsx/page1").read().decode("utf-8")

+ 1 - 3
tests/gui/gui_specific/test_state.py

@@ -10,7 +10,6 @@
 # specific language governing permissions and limitations under the License.
 
 import inspect
-from unittest.mock import patch
 
 import pytest
 
@@ -23,8 +22,7 @@ def test_state(gui: Gui):
     a = 10  # noqa: F841
     gui._set_frame(inspect.currentframe())
     gui.add_page("page1", md_page1)
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False, single_client=True)
+    gui.run(run_server=False, single_client=True)
     state = gui._Gui__state  # type: ignore[attr-defined]
     with gui.get_flask_app().app_context():
         assert state.a == 10

+ 3 - 7
tests/gui/gui_specific/test_variable_binding.py

@@ -9,7 +9,6 @@
 # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 # specific language governing permissions and limitations under the License.
 
-from unittest.mock import patch
 
 from taipy.gui import Gui, Markdown
 
@@ -27,8 +26,7 @@ def test_variable_binding(helpers):
     z = "button label"
     gui = Gui()
     gui.add_page("test", Markdown("<|{x}|> | <|{y}|> | <|{z}|button|on_action=another_function|>"))
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False, single_client=True)
+    gui.run(run_server=False, single_client=True)
     client = gui._server.test_client()
     jsx = client.get("/taipy-jsx/test").json["jsx"]
     for expected in ["<Button", 'defaultLabel="button label"', "label={tpec_TpExPr_z_TPMDL_0}"]:
@@ -46,8 +44,7 @@ def test_properties_binding(helpers):
     modifier = "nice "  # noqa: F841
     button_properties = {"label": "A {modifier}button"}  # noqa: F841
     gui.add_page("test", Markdown("<|button|properties=button_properties|>"))
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     client = gui._server.test_client()
     jsx = client.get("/taipy-jsx/test").json["jsx"]
     for expected in ["<Button", 'defaultLabel="A nice button"']:
@@ -61,8 +58,7 @@ def test_dict_binding(helpers):
     """
     d = {"k": "test"}  # noqa: F841
     gui = Gui("<|{d.k}|>")
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     client = gui._server.test_client()
     jsx = client.get("/taipy-jsx/TaiPy_root_page").json["jsx"]
     for expected in ["<Field", 'defaultValue="test"']:

+ 3 - 7
tests/gui/helpers.py

@@ -15,7 +15,6 @@ import socket
 import time
 import typing as t
 import warnings
-from unittest.mock import patch
 
 from taipy.gui import Gui, Html, Markdown
 from taipy.gui._renderers.builder import _Builder
@@ -48,8 +47,7 @@ class Helpers:
 
     @staticmethod
     def _test_control(gui: Gui, expected_values: t.Union[str, t.List]):
-        with patch("sys.argv", ["prog"]):
-            gui.run(run_server=False, single_client=True, stylekit=False)
+        gui.run(run_server=False, single_client=True, stylekit=False)
         client = gui._server.test_client()
         response = client.get("/taipy-jsx/test")
         assert response.status_code == 200, f"response.status_code {response.status_code} != 200"
@@ -138,16 +136,14 @@ class Helpers:
         kwargs["run_browser"] = False
         kwargs["stylekit"] = kwargs.get("stylekit", False)
         with warnings.catch_warnings(record=True):
-            with patch("sys.argv", ["prog"]):
-                gui.run(**kwargs)
+            gui.run(**kwargs)
         while not Helpers.port_check():
             time.sleep(0.1)
 
     @staticmethod
     def run_e2e_multi_client(gui: Gui):
         with warnings.catch_warnings(record=True):
-            with patch("sys.argv", ["prog"]):
-                gui.run(run_server=False, run_browser=False, single_client=False, stylekit=False)
+            gui.run(run_server=False, run_browser=False, single_client=False, stylekit=False)
             gui._server.run(
                 host=gui._get_config("host", "127.0.0.1"),
                 port=gui._get_config("port", 5000),

+ 1 - 3
tests/gui/ignore/no_file/test_ignore.py

@@ -11,7 +11,6 @@
 
 import inspect
 import warnings
-from unittest.mock import patch
 
 from taipy.gui import Gui
 
@@ -19,8 +18,7 @@ from taipy.gui import Gui
 def test_no_ignore_file(gui: Gui):
     with warnings.catch_warnings(record=True):
         gui._set_frame(inspect.currentframe())
-        with patch("sys.argv", ["prog"]):
-            gui.run(run_server=False)
+        gui.run(run_server=False)
         client = gui._server.test_client()
         response = client.get("/resource.txt")
         assert (

+ 2 - 5
tests/gui/ignore/with_file/test_with_ignore.py

@@ -11,7 +11,6 @@
 
 import inspect
 import warnings
-from unittest.mock import patch
 
 from taipy.gui import Gui
 
@@ -19,8 +18,7 @@ from taipy.gui import Gui
 def test_ignore_file_found(gui: Gui):
     with warnings.catch_warnings(record=True):
         gui._set_frame(inspect.currentframe())
-        with patch("sys.argv", ["prog"]):
-            gui.run(run_server=False)
+        gui.run(run_server=False)
         client = gui._server.test_client()
         response = client.get("/resource.txt")
         assert (
@@ -31,8 +29,7 @@ def test_ignore_file_found(gui: Gui):
 def test_ignore_file_not_found(gui: Gui):
     with warnings.catch_warnings(record=True):
         gui._set_frame(inspect.currentframe())
-        with patch("sys.argv", ["prog"]):
-            gui.run(run_server=False)
+        gui.run(run_server=False)
         client = gui._server.test_client()
         response = client.get("/resource2.txt")
         assert (

+ 1 - 3
tests/gui/long_runnig/test_long_running.py

@@ -11,7 +11,6 @@
 
 import inspect
 from time import sleep
-from unittest.mock import patch
 
 from taipy.gui import Gui, State, invoke_long_callback
 
@@ -33,8 +32,7 @@ def test_long_callback(gui: Gui):
         state.status = -1
 
     gui._set_frame(inspect.currentframe())
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False, single_client=True)
+    gui.run(run_server=False, single_client=True)
     state = gui._Gui__state  # type: ignore[attr-defined]
 
     with gui.get_flask_app().app_context():

+ 1 - 3
tests/gui/renderers/test_html_parsing.py

@@ -10,7 +10,6 @@
 # specific language governing permissions and limitations under the License.
 
 import inspect
-from unittest.mock import patch
 
 from taipy.gui import Gui, Html
 
@@ -20,8 +19,7 @@ def test_simple_html(gui: Gui, helpers):
     html_string = "<html><head></head><body><h1>test</h1></body></html>"
     gui._set_frame(inspect.currentframe())
     gui.add_page("test", Html(html_string))
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     client = gui._server.test_client()
     jsx = client.get("/taipy-jsx/test").json["jsx"]
     assert jsx == "<h1>test</h1>"

+ 2 - 5
tests/gui/server/http/test_extension.py

@@ -9,7 +9,6 @@
 # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 # specific language governing permissions and limitations under the License.
 
-from unittest.mock import patch
 
 import pytest
 
@@ -26,8 +25,7 @@ class MyLibrary(ElementLibrary):
 
 
 def test_extension_no_config(gui: Gui, helpers):
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False, single_client=True)
+    gui.run(run_server=False, single_client=True)
     flask_client = gui._server.test_client()
     with pytest.warns(UserWarning):
         ret = flask_client.get("/taipy-extension/toto/titi")
@@ -36,8 +34,7 @@ def test_extension_no_config(gui: Gui, helpers):
 
 def test_extension_config_wrong_path(gui: Gui, helpers):
     Gui.add_library(MyLibrary())
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False, single_client=True)
+    gui.run(run_server=False, single_client=True)
     flask_client = gui._server.test_client()
     with pytest.warns(UserWarning):
         ret = flask_client.get("/taipy-extension/taipy_extension_example/titi")

+ 6 - 13
tests/gui/server/http/test_file_upload.py

@@ -13,7 +13,6 @@ import inspect
 import io
 import pathlib
 import tempfile
-from unittest.mock import patch
 
 import pytest
 
@@ -23,8 +22,7 @@ from taipy.gui.utils import _get_non_existent_file_path
 
 
 def test_file_upload_no_varname(gui: Gui, helpers):
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     flask_client = gui._server.test_client()
     # Get the jsx once so that the page will be evaluated -> variable will be registered
     sid = helpers.create_scope_and_get_sid(gui)
@@ -34,8 +32,7 @@ def test_file_upload_no_varname(gui: Gui, helpers):
 
 
 def test_file_upload_no_blob(gui: Gui, helpers):
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     flask_client = gui._server.test_client()
     # Get the jsx once so that the page will be evaluated -> variable will be registered
     sid = helpers.create_scope_and_get_sid(gui)
@@ -45,8 +42,7 @@ def test_file_upload_no_blob(gui: Gui, helpers):
 
 
 def test_file_upload_no_filename(gui: Gui, helpers):
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     flask_client = gui._server.test_client()
     file = (io.BytesIO(b"abcdef"), "")
     # Get the jsx once so that the page will be evaluated -> variable will be registered
@@ -57,8 +53,7 @@ def test_file_upload_no_filename(gui: Gui, helpers):
 
 
 def test_file_upload_simple(gui: Gui, helpers):
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     flask_client = gui._server.test_client()
     # Get the jsx once so that the page will be evaluated -> variable will be registered
     sid = helpers.create_scope_and_get_sid(gui)
@@ -77,8 +72,7 @@ def test_file_upload_simple(gui: Gui, helpers):
 
 
 def test_file_upload_multi_part(gui: Gui, helpers):
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     flask_client = gui._server.test_client()
     # Get the jsx once so that the page will be evaluated -> variable will be registered
     sid = helpers.create_scope_and_get_sid(gui)
@@ -110,8 +104,7 @@ def test_file_upload_multi_part(gui: Gui, helpers):
 def test_file_upload_multiple(gui: Gui, helpers):
     var_name = "varname"
     gui._set_frame(inspect.currentframe())
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False, single_client=True)
+    gui.run(run_server=False, single_client=True)
     flask_client = gui._server.test_client()
     with gui.get_flask_app().app_context():
         gui._bind_var_val(var_name, None)

+ 2 - 5
tests/gui/server/http/test_image_path.py

@@ -10,14 +10,12 @@
 # specific language governing permissions and limitations under the License.
 
 import pathlib
-from unittest.mock import patch
 
 from taipy.gui import Gui
 
 
 def test_image_path_not_found(gui: Gui, helpers):
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     flask_client = gui._server.test_client()
     # Get the jsx once so that the page will be evaluated -> variable will be registered
     sid = helpers.create_scope_and_get_sid(gui)
@@ -29,8 +27,7 @@ def test_image_path_found(gui: Gui, helpers):
     url = gui._get_content(
         "img", str((pathlib.Path(__file__).parent.parent.parent / "resources" / "fred.png").resolve()), True
     )
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     flask_client = gui._server.test_client()
     # Get the jsx once so that the page will be evaluated -> variable will be registered
     sid = helpers.create_scope_and_get_sid(gui)

+ 3 - 7
tests/gui/server/http/test_status.py

@@ -11,14 +11,12 @@
 
 import inspect
 from os import path
-from unittest.mock import patch
 
 from taipy.gui import Gui
 
 
 def test_get_status(gui: Gui):
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     flask_client = gui._server.test_client()
     ret = flask_client.get("/taipy.status.json")
     assert ret.status_code == 200, f"status_code => {ret.status_code} != 200"
@@ -32,8 +30,7 @@ def test_get_status(gui: Gui):
 
 
 def test_get_extended_status(gui: Gui):
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False, extended_status=True)
+    gui.run(run_server=False, extended_status=True)
     flask_client = gui._server.test_client()
     ret = flask_client.get("/taipy.status.json")
     assert ret.status_code == 200, f"status_code => {ret.status_code} != 200"
@@ -58,8 +55,7 @@ def test_get_status_with_user_status(gui: Gui):
 
     gui._set_frame(inspect.currentframe())
 
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     flask_client = gui._server.test_client()
     ret = flask_client.get("/taipy.status.json")
     assert ret.status_code == 200, f"status_code => {ret.status_code} != 200"

+ 3 - 7
tests/gui/server/http/test_user_content.py

@@ -10,7 +10,6 @@
 # specific language governing permissions and limitations under the License.
 
 import inspect
-from unittest.mock import patch
 
 import pytest
 
@@ -18,8 +17,7 @@ from taipy.gui import Gui
 
 
 def test_user_content_without_callback(gui: Gui, helpers):
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False, single_client=True)
+    gui.run(run_server=False, single_client=True)
     flask_client = gui._server.test_client()
     with pytest.warns(UserWarning):
         ret = flask_client.get(gui._get_user_content_url("path"))
@@ -32,8 +30,7 @@ def test_user_content_with_wrong_callback(gui: Gui, helpers):
 
     on_user_content = on_user_content_cb  # noqa: F841
     gui._set_frame(inspect.currentframe())
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False, single_client=True)
+    gui.run(run_server=False, single_client=True)
     flask_client = gui._server.test_client()
     with pytest.warns(UserWarning):
         ret = flask_client.get(gui._get_user_content_url("path", {"a": "b"}))
@@ -46,8 +43,7 @@ def test_user_content_with_callback(gui: Gui, helpers):
 
     on_user_content = on_user_content_cb  # noqa: F841
     gui._set_frame(inspect.currentframe())
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False, single_client=True)
+    gui.run(run_server=False, single_client=True)
     flask_client = gui._server.test_client()
     ret = flask_client.get(gui._get_user_content_url("path"))
     assert ret.status_code == 200

+ 1 - 3
tests/gui/server/ws/test_a.py

@@ -10,7 +10,6 @@
 # specific language governing permissions and limitations under the License.
 
 import inspect
-from unittest.mock import patch
 
 from taipy.gui import Gui, Markdown
 
@@ -28,8 +27,7 @@ def test_a_button_pressed(gui: Gui, helpers):
     gui.add_page(
         "test", Markdown("<|Do something!|button|on_action=do_something|id=my_button|> | <|{x}|> | <|{text}|>")
     )
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     flask_client = gui._server.test_client()
     # WS client and emit
     ws_client = gui._server._ws.test_client(gui._server.get_flask())

+ 1 - 3
tests/gui/server/ws/test_broadcast.py

@@ -10,7 +10,6 @@
 # specific language governing permissions and limitations under the License.
 
 import inspect
-from unittest.mock import patch
 
 from taipy.gui import Gui, Markdown
 
@@ -27,8 +26,7 @@ def test_broadcast(gui: Gui, helpers):
         "test",
         Markdown("<|{selected_val}|selector|multiple|>"),
     )
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     flask_client = gui._server.test_client()
     # WS client and emit
     ws_client = gui._server._ws.test_client(gui._server.get_flask())

+ 1 - 3
tests/gui/server/ws/test_df.py

@@ -12,7 +12,6 @@
 import inspect
 import logging
 import pathlib
-from unittest.mock import patch
 
 from taipy.gui import Gui, download
 
@@ -28,8 +27,7 @@ def test_download_file(gui: Gui, helpers):
     # set gui frame
     gui._set_frame(inspect.currentframe())
 
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     # WS client and emit
     ws_client = gui._server._ws.test_client(gui._server.get_flask())
     # Get the jsx once so that the page will be evaluated -> variable will be registered

+ 1 - 3
tests/gui/server/ws/test_du.py

@@ -10,7 +10,6 @@
 # specific language governing permissions and limitations under the License.
 
 import inspect
-from unittest.mock import patch
 
 from taipy.gui import Gui, Markdown
 
@@ -30,8 +29,7 @@ def test_du_table_data_fetched(gui: Gui, helpers, csvdata):
             "<|{csvdata}|table|page_size=10|page_size_options=10;30;100|columns=Day;Entity;Code;Daily hospital occupancy|date_format=eee dd MMM yyyy|>"  # noqa: E501
         ),
     )
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     flask_client = gui._server.test_client()
     # WS client and emit
     ws_client = gui._server._ws.test_client(gui._server.get_flask())

+ 2 - 5
tests/gui/server/ws/test_on_change.py

@@ -10,7 +10,6 @@
 # specific language governing permissions and limitations under the License.
 
 import inspect
-from unittest.mock import patch
 
 from taipy.gui import Gui, Markdown
 
@@ -27,8 +26,7 @@ def test_default_on_change(gui: Gui, helpers):
     gui._set_frame(inspect.currentframe())
 
     gui.add_page("test", Markdown("<|{x}|input|>"))
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     flask_client = gui._server.test_client()
     # WS client and emit
     ws_client = gui._server._ws.test_client(gui._server.get_flask())
@@ -56,8 +54,7 @@ def test_specific_on_change(gui: Gui, helpers):
     gui._set_frame(inspect.currentframe())
 
     gui.add_page("test", Markdown("<|{x}|input|on_change=on_input_change|>"))
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     flask_client = gui._server.test_client()
     # WS client and emit
     ws_client = gui._server._ws.test_client(gui._server.get_flask())

+ 1 - 3
tests/gui/server/ws/test_ru.py

@@ -10,7 +10,6 @@
 # specific language governing permissions and limitations under the License.
 
 import inspect
-from unittest.mock import patch
 
 from taipy.gui import Gui, Markdown
 
@@ -27,8 +26,7 @@ def test_ru_selector(gui: Gui, helpers, csvdata):
         "test",
         Markdown("<|{selected_val}|selector|multiple|>"),
     )
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     flask_client = gui._server.test_client()
     # WS client and emit
     ws_client = gui._server._ws.test_client(gui._server.get_flask())

+ 1 - 3
tests/gui/server/ws/test_u.py

@@ -10,7 +10,6 @@
 # specific language governing permissions and limitations under the License.
 
 import inspect
-from unittest.mock import patch
 
 from taipy.gui import Gui, Markdown
 
@@ -24,8 +23,7 @@ def ws_u_assert_template(gui: Gui, helpers, value_before_update, value_after_upd
 
     # Bind a page so that the variable will be evaluated as expression
     gui.add_page("test", Markdown("<|{var}|>"))
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     flask_client = gui._server.test_client()
     # WS client and emit
     ws_client = gui._server._ws.test_client(gui._server.get_flask())

+ 1 - 3
tests/gui/server/ws/test_with.py

@@ -10,7 +10,6 @@
 # specific language governing permissions and limitations under the License.
 
 import inspect
-from unittest.mock import patch
 
 from taipy.gui import Gui, Markdown
 from taipy.gui.data.data_scope import _DataScopes
@@ -24,8 +23,7 @@ def test_sending_messages_in_group(gui: Gui, helpers):
     gui._set_frame(inspect.currentframe())
 
     gui.add_page("test", Markdown("<|Hello {name}|button|id={btn_id}|>"))
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False, single_client=True)
+    gui.run(run_server=False, single_client=True)
     flask_client = gui._server.test_client()
     # WS client and emit
     ws_client = gui._server._ws.test_client(gui._server.get_flask())

+ 6 - 13
tests/gui/utils/test_evaluator.py

@@ -11,7 +11,6 @@
 
 import inspect
 import warnings
-from unittest.mock import patch
 
 from flask import g
 
@@ -20,8 +19,7 @@ from taipy.gui.utils.types import _TaipyNumber
 
 
 def test_unbind_variable_in_expression(gui: Gui, helpers):
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False, single_client=True)
+    gui.run(run_server=False, single_client=True)
     with warnings.catch_warnings(record=True) as records:
         with gui.get_flask_app().app_context():
             gui._evaluate_expr("{x}")
@@ -36,8 +34,7 @@ def test_unbind_variable_in_expression(gui: Gui, helpers):
 def test_evaluate_same_expression_multiple_times(gui: Gui):
     x = 10  # noqa: F841
     gui._set_frame(inspect.currentframe())
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False, single_client=True)
+    gui.run(run_server=False, single_client=True)
     with gui.get_flask_app().app_context():
         s1 = gui._evaluate_expr("x + 10 = {x + 10}")
         s2 = gui._evaluate_expr("x + 10 = {x + 10}")
@@ -47,8 +44,7 @@ def test_evaluate_same_expression_multiple_times(gui: Gui):
 def test_evaluate_expressions_same_variable(gui: Gui):
     x = 10  # noqa: F841
     gui._set_frame(inspect.currentframe())
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False, single_client=True)
+    gui.run(run_server=False, single_client=True)
     with gui.get_flask_app().app_context():
         s1 = gui._evaluate_expr("x + 10 = {x + 10}")
         s2 = gui._evaluate_expr("x = {x}")
@@ -58,8 +54,7 @@ def test_evaluate_expressions_same_variable(gui: Gui):
 def test_evaluate_holder(gui: Gui):
     x = 10  # noqa: F841
     gui._set_frame(inspect.currentframe())
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False, single_client=True)
+    gui.run(run_server=False, single_client=True)
     with warnings.catch_warnings(record=True):
         with gui.get_flask_app().app_context():
             gui._evaluate_expr("{x + 10}")
@@ -74,8 +69,7 @@ def test_evaluate_holder(gui: Gui):
 
 
 def test_evaluate_not_expression_type(gui: Gui):
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     with gui.get_flask_app().app_context():
         assert "x + 10" == gui._evaluate_expr("x + 10")
 
@@ -84,8 +78,7 @@ def test_evaluate_expression_2_clients(gui: Gui):
     x = 10  # noqa: F841
     y = 20  # noqa: F841
     gui._set_frame(inspect.currentframe())
-    with patch("sys.argv", ["prog"]):
-        gui.run(run_server=False)
+    gui.run(run_server=False)
     with gui.get_flask_app().app_context():
         gui._bindings()._get_or_create_scope("A")
         gui._bindings()._get_or_create_scope("B")

Некоторые файлы не были показаны из-за большого количества измененных файлов