Browse Source

Scenario viewer update (#1803)

* Scenario viewer update
resolves #1669
 resolves a few mypy/ruff "errors" in python as well

* sometimes _is_deletable breaks because Job is None

* comments and Mui 7 preparation

* mypy

* ruff

* doc embryo

* fix test

* filters

* JR' comment + Mui update

* fab's comment

* job_id cannot be empty/None in the exception block

---------

Co-authored-by: Fred Lefévère-Laoide <Fred.Lefevere-Laoide@Taipy.io>
Fred Lefévère-Laoide 8 months ago
parent
commit
6c2df92707

+ 52 - 96
frontend/taipy-gui/package-lock.json

@@ -87,26 +87,6 @@
         "webpack-cli": "^5.0.0"
         "webpack-cli": "^5.0.0"
       }
       }
     },
     },
-    "node_modules/@75lb/deep-merge": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/@75lb/deep-merge/-/deep-merge-1.1.2.tgz",
-      "integrity": "sha512-08K9ou5VNbheZFxM5tDWoqjA3ImC50DiuuJ2tj1yEPRfkp8lLLg6XAaJ4On+a0yAXor/8ay5gHnAIshRM44Kpw==",
-      "dependencies": {
-        "lodash": "^4.17.21",
-        "typical": "^7.1.1"
-      },
-      "engines": {
-        "node": ">=12.17"
-      }
-    },
-    "node_modules/@75lb/deep-merge/node_modules/typical": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/typical/-/typical-7.2.0.tgz",
-      "integrity": "sha512-W1+HdVRUl8fS3MZ9ogD51GOb46xMmhAZzR0WPw5jcgIZQJVvkddYzAl4YTU6g5w33Y1iRQLdIi2/1jhi2RNL0g==",
-      "engines": {
-        "node": ">=12.17"
-      }
-    },
     "node_modules/@adobe/css-tools": {
     "node_modules/@adobe/css-tools": {
       "version": "4.4.0",
       "version": "4.4.0",
       "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.0.tgz",
       "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.0.tgz",
@@ -1912,18 +1892,18 @@
       "dev": true
       "dev": true
     },
     },
     "node_modules/@mui/core-downloads-tracker": {
     "node_modules/@mui/core-downloads-tracker": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.1.0.tgz",
-      "integrity": "sha512-covEnIn/2er5YdtuukDRA52kmARhKrHjOvPsyTFMQApZdrTBI4h8jbEy2mxZqwMwcAFS9coonQXnEZKL1rUNdQ==",
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.1.1.tgz",
+      "integrity": "sha512-VdQC1tPIIcZAnf62L2M1eQif0x2vlKg3YK4kGYbtijSH4niEgI21GnstykW1vQIs+Bc6L+Hua2GATYVjilJ22A==",
       "funding": {
       "funding": {
         "type": "opencollective",
         "type": "opencollective",
         "url": "https://opencollective.com/mui-org"
         "url": "https://opencollective.com/mui-org"
       }
       }
     },
     },
     "node_modules/@mui/icons-material": {
     "node_modules/@mui/icons-material": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.1.0.tgz",
-      "integrity": "sha512-HxfB0jxwiMTYMN8gAnYn3avbF1aDrqBEuGIj6JDQ3YkLl650E1Wy8AIhwwyP47wdrv0at9aAR0iOO6VLb74A9w==",
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.1.1.tgz",
+      "integrity": "sha512-sy/YKwcLPW8VcacNP2uWMYR9xyWuwO9NN9FXuGEU90bRshBXj8pdKk+joe3TCW7oviVS3zXLHlc94wQ0jNsQRQ==",
       "dependencies": {
       "dependencies": {
         "@babel/runtime": "^7.25.6"
         "@babel/runtime": "^7.25.6"
       },
       },
@@ -1935,7 +1915,7 @@
         "url": "https://opencollective.com/mui-org"
         "url": "https://opencollective.com/mui-org"
       },
       },
       "peerDependencies": {
       "peerDependencies": {
-        "@mui/material": "^6.1.0",
+        "@mui/material": "^6.1.1",
         "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
         "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
         "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
         "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
       },
       },
@@ -1946,15 +1926,15 @@
       }
       }
     },
     },
     "node_modules/@mui/material": {
     "node_modules/@mui/material": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.1.0.tgz",
-      "integrity": "sha512-4MJ46vmy1xbm8x+ZdRcWm8jEMMowdS8pYlhKQzg/qoKhOcLhImZvf2Jn6z9Dj6gl+lY+C/0MxaHF/avAAGys3Q==",
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.1.1.tgz",
+      "integrity": "sha512-b+eULldTqtqTCbN++2BtBWCir/1LwEYw+2mIlOt2GiEUh1EBBw4/wIukGKKNt3xrCZqRA80yLLkV6tF61Lq3cA==",
       "dependencies": {
       "dependencies": {
         "@babel/runtime": "^7.25.6",
         "@babel/runtime": "^7.25.6",
-        "@mui/core-downloads-tracker": "^6.1.0",
-        "@mui/system": "^6.1.0",
-        "@mui/types": "^7.2.16",
-        "@mui/utils": "^6.1.0",
+        "@mui/core-downloads-tracker": "^6.1.1",
+        "@mui/system": "^6.1.1",
+        "@mui/types": "^7.2.17",
+        "@mui/utils": "^6.1.1",
         "@popperjs/core": "^2.11.8",
         "@popperjs/core": "^2.11.8",
         "@types/react-transition-group": "^4.4.11",
         "@types/react-transition-group": "^4.4.11",
         "clsx": "^2.1.1",
         "clsx": "^2.1.1",
@@ -1973,7 +1953,7 @@
       "peerDependencies": {
       "peerDependencies": {
         "@emotion/react": "^11.5.0",
         "@emotion/react": "^11.5.0",
         "@emotion/styled": "^11.3.0",
         "@emotion/styled": "^11.3.0",
-        "@mui/material-pigment-css": "^6.1.0",
+        "@mui/material-pigment-css": "^6.1.1",
         "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
         "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
         "react": "^17.0.0 || ^18.0.0 || ^19.0.0",
         "react": "^17.0.0 || ^18.0.0 || ^19.0.0",
         "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
         "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
@@ -1994,12 +1974,12 @@
       }
       }
     },
     },
     "node_modules/@mui/private-theming": {
     "node_modules/@mui/private-theming": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.1.0.tgz",
-      "integrity": "sha512-+L5qccs4gwsR0r1dgjqhN24QEQRkqIbfOdxILyMbMkuI50x6wNyt9XrV+J3WtjtZTMGJCrUa5VmZBE6OEPGPWA==",
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.1.1.tgz",
+      "integrity": "sha512-JlrjIdhyZUtewtdAuUsvi3ZnO0YS49IW4Mfz19ZWTlQ0sDGga6LNPVwHClWr2/zJK2we2BQx9/i8M32rgKuzrg==",
       "dependencies": {
       "dependencies": {
         "@babel/runtime": "^7.25.6",
         "@babel/runtime": "^7.25.6",
-        "@mui/utils": "^6.1.0",
+        "@mui/utils": "^6.1.1",
         "prop-types": "^15.8.1"
         "prop-types": "^15.8.1"
       },
       },
       "engines": {
       "engines": {
@@ -2020,9 +2000,9 @@
       }
       }
     },
     },
     "node_modules/@mui/styled-engine": {
     "node_modules/@mui/styled-engine": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.1.0.tgz",
-      "integrity": "sha512-MZ+vtaCkjamrT41+b0Er9OMenjAtP/32+L6fARL9/+BZKuV2QbR3q3TmavT2x0NhDu35IM03s4yKqj32Ziqnyg==",
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.1.1.tgz",
+      "integrity": "sha512-HJyIoMpFb11fnHuRtUILOXgq6vj4LhIlE8maG4SwP/W+E5sa7HFexhnB3vOMT7bKys4UKNxhobC8jwWxYilGsA==",
       "dependencies": {
       "dependencies": {
         "@babel/runtime": "^7.25.6",
         "@babel/runtime": "^7.25.6",
         "@emotion/cache": "^11.13.1",
         "@emotion/cache": "^11.13.1",
@@ -2052,15 +2032,15 @@
       }
       }
     },
     },
     "node_modules/@mui/system": {
     "node_modules/@mui/system": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.1.0.tgz",
-      "integrity": "sha512-NumkGDqT6EdXfcoFLYQ+M4XlTW5hH3+aK48xAbRqKPXJfxl36CBt4DLduw/Voa5dcayGus9T6jm1AwU2hoJ5hQ==",
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.1.1.tgz",
+      "integrity": "sha512-PaYsCz2tUOcpu3T0okDEsSuP/yCDIj9JZ4Tox1JovRSKIjltHpXPsXZSGr3RiWdtM1MTQMFMCZzu0+CKbyy+Kw==",
       "dependencies": {
       "dependencies": {
         "@babel/runtime": "^7.25.6",
         "@babel/runtime": "^7.25.6",
-        "@mui/private-theming": "^6.1.0",
-        "@mui/styled-engine": "^6.1.0",
-        "@mui/types": "^7.2.16",
-        "@mui/utils": "^6.1.0",
+        "@mui/private-theming": "^6.1.1",
+        "@mui/styled-engine": "^6.1.1",
+        "@mui/types": "^7.2.17",
+        "@mui/utils": "^6.1.1",
         "clsx": "^2.1.1",
         "clsx": "^2.1.1",
         "csstype": "^3.1.3",
         "csstype": "^3.1.3",
         "prop-types": "^15.8.1"
         "prop-types": "^15.8.1"
@@ -2091,9 +2071,9 @@
       }
       }
     },
     },
     "node_modules/@mui/types": {
     "node_modules/@mui/types": {
-      "version": "7.2.16",
-      "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.16.tgz",
-      "integrity": "sha512-qI8TV3M7ShITEEc8Ih15A2vLzZGLhD+/UPNwck/hcls2gwg7dyRjNGXcQYHKLB5Q7PuTRfrTkAoPa2VV1s67Ag==",
+      "version": "7.2.17",
+      "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.17.tgz",
+      "integrity": "sha512-oyumoJgB6jDV8JFzRqjBo2daUuHpzDjoO/e3IrRhhHo/FxJlaVhET6mcNrKHUq2E+R+q3ql0qAtvQ4rfWHhAeQ==",
       "peerDependencies": {
       "peerDependencies": {
         "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0"
         "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0"
       },
       },
@@ -2104,12 +2084,12 @@
       }
       }
     },
     },
     "node_modules/@mui/utils": {
     "node_modules/@mui/utils": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.1.0.tgz",
-      "integrity": "sha512-oT8ZzMISRUhTVpdbYzY0CgrCBb3t/YEdcaM13tUnuTjZ15pdA6g5lx15ZJUdgYXV6PbJdw7tDQgMEr4uXK5TXQ==",
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.1.1.tgz",
+      "integrity": "sha512-HlRrgdJSPbYDXPpoVMWZV8AE7WcFtAk13rWNWAEVWKSanzBBkymjz3km+Th/Srowsh4pf1fTSP1B0L116wQBYw==",
       "dependencies": {
       "dependencies": {
         "@babel/runtime": "^7.25.6",
         "@babel/runtime": "^7.25.6",
-        "@mui/types": "^7.2.16",
+        "@mui/types": "^7.2.17",
         "@types/prop-types": "^15.7.12",
         "@types/prop-types": "^15.7.12",
         "clsx": "^2.1.1",
         "clsx": "^2.1.1",
         "prop-types": "^15.8.1",
         "prop-types": "^15.8.1",
@@ -2954,9 +2934,9 @@
       }
       }
     },
     },
     "node_modules/@types/estree": {
     "node_modules/@types/estree": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
-      "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw=="
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
+      "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="
     },
     },
     "node_modules/@types/estree-jsx": {
     "node_modules/@types/estree-jsx": {
       "version": "1.0.5",
       "version": "1.0.5",
@@ -4561,9 +4541,9 @@
       }
       }
     },
     },
     "node_modules/caniuse-lite": {
     "node_modules/caniuse-lite": {
-      "version": "1.0.30001660",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001660.tgz",
-      "integrity": "sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg==",
+      "version": "1.0.30001662",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001662.tgz",
+      "integrity": "sha512-sgMUVwLmGseH8ZIrm1d51UbrhqMCH3jvS7gF/M6byuHOnKyLOBL7W8yz5V02OHwgLGA36o/AFhWzzh4uc5aqTA==",
       "funding": [
       "funding": [
         {
         {
           "type": "opencollective",
           "type": "opencollective",
@@ -5080,13 +5060,13 @@
       }
       }
     },
     },
     "node_modules/command-line-usage": {
     "node_modules/command-line-usage": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-7.0.1.tgz",
-      "integrity": "sha512-NCyznE//MuTjwi3y84QVUGEOT+P5oto1e1Pk/jFPVdPPfsG03qpTIl3yw6etR+v73d0lXsoojRpvbru2sqePxQ==",
+      "version": "7.0.3",
+      "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-7.0.3.tgz",
+      "integrity": "sha512-PqMLy5+YGwhMh1wS04mVG44oqDsgyLRSKJBdOo1bnYhMKBW65gZF1dRp2OZRhiTjgUHljy99qkO7bsctLaw35Q==",
       "dependencies": {
       "dependencies": {
         "array-back": "^6.2.2",
         "array-back": "^6.2.2",
         "chalk-template": "^0.4.0",
         "chalk-template": "^0.4.0",
-        "table-layout": "^3.0.0",
+        "table-layout": "^4.1.0",
         "typical": "^7.1.1"
         "typical": "^7.1.1"
       },
       },
       "engines": {
       "engines": {
@@ -6135,9 +6115,9 @@
       }
       }
     },
     },
     "node_modules/electron-to-chromium": {
     "node_modules/electron-to-chromium": {
-      "version": "1.5.24",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.24.tgz",
-      "integrity": "sha512-0x0wLCmpdKFCi9ulhvYZebgcPmHTkFVUfU2wzDykadkslKwT4oAmDTHEKLnlrDsMGZe4B+ksn8quZfZjYsBetA=="
+      "version": "1.5.25",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.25.tgz",
+      "integrity": "sha512-kMb204zvK3PsSlgvvwzI3wBIcAw15tRkYk+NQdsjdDtcQWTp2RABbMQ9rUBy8KNEOM+/E6ep+XC3AykiWZld4g=="
     },
     },
     "node_modules/element-size": {
     "node_modules/element-size": {
       "version": "1.1.1",
       "version": "1.1.1",
@@ -14757,14 +14737,6 @@
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
       "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
       "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
     },
     },
-    "node_modules/stream-read-all": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/stream-read-all/-/stream-read-all-3.0.1.tgz",
-      "integrity": "sha512-EWZT9XOceBPlVJRrYcykW8jyRSZYbkb/0ZK36uLEmoWVO5gxBOnntNTseNzfREsqxqdfEGQrD8SXQ3QWbBmq8A==",
-      "engines": {
-        "node": ">=10"
-      }
-    },
     "node_modules/stream-shift": {
     "node_modules/stream-shift": {
       "version": "1.0.3",
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz",
       "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz",
@@ -15125,21 +15097,13 @@
       "dev": true
       "dev": true
     },
     },
     "node_modules/table-layout": {
     "node_modules/table-layout": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-3.0.2.tgz",
-      "integrity": "sha512-rpyNZYRw+/C+dYkcQ3Pr+rLxW4CfHpXjPDnG7lYhdRoUcZTUt+KEsX+94RGp/aVp/MQU35JCITv2T/beY4m+hw==",
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-4.1.1.tgz",
+      "integrity": "sha512-iK5/YhZxq5GO5z8wb0bY1317uDF3Zjpha0QFFLA8/trAoiLbQD0HUbMesEaxyzUgDxi2QlcbM8IvqOlEjgoXBA==",
       "dependencies": {
       "dependencies": {
-        "@75lb/deep-merge": "^1.1.1",
         "array-back": "^6.2.2",
         "array-back": "^6.2.2",
-        "command-line-args": "^5.2.1",
-        "command-line-usage": "^7.0.0",
-        "stream-read-all": "^3.0.1",
-        "typical": "^7.1.1",
         "wordwrapjs": "^5.1.0"
         "wordwrapjs": "^5.1.0"
       },
       },
-      "bin": {
-        "table-layout": "bin/cli.js"
-      },
       "engines": {
       "engines": {
         "node": ">=12.17"
         "node": ">=12.17"
       }
       }
@@ -15152,14 +15116,6 @@
         "node": ">=12.17"
         "node": ">=12.17"
       }
       }
     },
     },
-    "node_modules/table-layout/node_modules/typical": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/typical/-/typical-7.2.0.tgz",
-      "integrity": "sha512-W1+HdVRUl8fS3MZ9ogD51GOb46xMmhAZzR0WPw5jcgIZQJVvkddYzAl4YTU6g5w33Y1iRQLdIi2/1jhi2RNL0g==",
-      "engines": {
-        "node": ">=12.17"
-      }
-    },
     "node_modules/tapable": {
     "node_modules/tapable": {
       "version": "2.2.1",
       "version": "2.2.1",
       "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
       "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",

+ 2 - 2
frontend/taipy-gui/src/components/Taipy/Chat.tsx

@@ -406,7 +406,7 @@ const Chat = (props: ChatProps) => {
                         label={`message (${senderId})`}
                         label={`message (${senderId})`}
                         disabled={!active}
                         disabled={!active}
                         onKeyDown={handleAction}
                         onKeyDown={handleAction}
-                        InputProps={{
+                        slotProps={{input: {
                             endAdornment: (
                             endAdornment: (
                                 <InputAdornment position="end">
                                 <InputAdornment position="end">
                                     <IconButton
                                     <IconButton
@@ -419,7 +419,7 @@ const Chat = (props: ChatProps) => {
                                     </IconButton>
                                     </IconButton>
                                 </InputAdornment>
                                 </InputAdornment>
                             ),
                             ),
-                        }}
+                        }}}
                         sx={inputSx}
                         sx={inputSx}
                     />
                     />
                 ) : null}
                 ) : null}

+ 48 - 52
frontend/taipy-gui/src/components/Taipy/Input.tsx

@@ -260,45 +260,58 @@ const Input = (props: TaipyInputProps) => {
         (event: React.MouseEvent<HTMLButtonElement>) => event.preventDefault(),
         (event: React.MouseEvent<HTMLButtonElement>) => event.preventDefault(),
         []
         []
     );
     );
-    const muiInputProps = useMemo(
+    const inputProps = useMemo(
         () =>
         () =>
-            type == "password"
+            type == "number"
                 ? {
                 ? {
-                      endAdornment: (
-                          <IconButton
-                              aria-label="toggle password visibility"
-                              onClick={handleClickShowPassword}
-                              onMouseDown={handleMouseDownPassword}
-                              edge="end"
-                          >
-                              {showPassword ? <VisibilityOff /> : <Visibility />}
-                          </IconButton>
-                      ),
+                      htmlInput: {
+                          step: step ? step : 1,
+                          min: min,
+                          max: max,
+                      },
+                      input: {
+                          endAdornment: (
+                              <div style={verticalDivStyle}>
+                                  <IconButton
+                                      aria-label="Increment value"
+                                      size="small"
+                                      onMouseDown={handleUpStepperMouseDown}
+                                  >
+                                      <ArrowDropUpIcon fontSize="inherit" />
+                                  </IconButton>
+                                  <IconButton
+                                      aria-label="Decrement value"
+                                      size="small"
+                                      onMouseDown={handleDownStepperMouseDown}
+                                  >
+                                      <ArrowDropDownIcon fontSize="inherit" />
+                                  </IconButton>
+                              </div>
+                          ),
+                      },
                   }
                   }
-                : type == "number"
-                  ? {
-                        endAdornment: (
-                            <div style={verticalDivStyle}>
-                                <IconButton
-                                    aria-label="Increment value"
-                                    size="small"
-                                    onMouseDown={handleUpStepperMouseDown}
-                                >
-                                    <ArrowDropUpIcon fontSize="inherit" />
-                                </IconButton>
-                                <IconButton
-                                    aria-label="Decrement value"
-                                    size="small"
-                                    onMouseDown={handleDownStepperMouseDown}
-                                >
-                                    <ArrowDropDownIcon fontSize="inherit" />
-                                </IconButton>
-                            </div>
-                        ),
-                    }
-                  : undefined,
+                : type == "password"
+                ? {
+                      htmlInput: { autoComplete: "current-password" },
+                      input: {
+                          endAdornment: (
+                              <IconButton
+                                  aria-label="toggle password visibility"
+                                  onClick={handleClickShowPassword}
+                                  onMouseDown={handleMouseDownPassword}
+                                  edge="end"
+                              >
+                                  {showPassword ? <VisibilityOff /> : <Visibility />}
+                              </IconButton>
+                          ),
+                      },
+                  }
+                : undefined,
         [
         [
             type,
             type,
+            step,
+            min,
+            max,
             showPassword,
             showPassword,
             handleClickShowPassword,
             handleClickShowPassword,
             handleMouseDownPassword,
             handleMouseDownPassword,
@@ -307,20 +320,6 @@ const Input = (props: TaipyInputProps) => {
         ]
         ]
     );
     );
 
 
-    const inputProps = useMemo(
-        () =>
-            type == "number"
-                ? {
-                      step: step ? step : 1,
-                      min: min,
-                      max: max,
-                  }
-                : type == "password"
-                  ? { autoComplete: "current-password" }
-                  : undefined,
-        [type, step, min, max]
-    );
-
     useEffect(() => {
     useEffect(() => {
         if (props.value !== undefined) {
         if (props.value !== undefined) {
             setValue(props.value);
             setValue(props.value);
@@ -337,10 +336,7 @@ const Input = (props: TaipyInputProps) => {
                 className={className}
                 className={className}
                 type={showPassword && type == "password" ? "text" : type}
                 type={showPassword && type == "password" ? "text" : type}
                 id={id}
                 id={id}
-                slotProps={{
-                    htmlInput: inputProps,
-                    input: muiInputProps,
-                }}
+                slotProps={inputProps}
                 label={props.label}
                 label={props.label}
                 onChange={handleInput}
                 onChange={handleInput}
                 disabled={!active}
                 disabled={!active}

+ 5 - 6
frontend/taipy-gui/src/components/Taipy/Login.tsx

@@ -48,7 +48,7 @@ const closeSx: SxProps<Theme> = {
     alignSelf: "start",
     alignSelf: "start",
 };
 };
 const titleSx = { m: 0, p: 2, display: "flex", paddingRight: "0.1em" };
 const titleSx = { m: 0, p: 2, display: "flex", paddingRight: "0.1em" };
-const userProps = { autoComplete: "username" };
+const userProps = { htmlInput: { autoComplete: "username" }};
 const pwdProps = { autoComplete: "current-password" };
 const pwdProps = { autoComplete: "current-password" };
 
 
 const Login = (props: LoginProps) => {
 const Login = (props: LoginProps) => {
@@ -97,7 +97,7 @@ const Login = (props: LoginProps) => {
         []
         []
     );
     );
     const passwordProps = useMemo(
     const passwordProps = useMemo(
-        () => ({
+        () => ({input: {
             endAdornment: (
             endAdornment: (
                 <InputAdornment position="end">
                 <InputAdornment position="end">
                     <IconButton
                     <IconButton
@@ -110,7 +110,7 @@ const Login = (props: LoginProps) => {
                     </IconButton>
                     </IconButton>
                 </InputAdornment>
                 </InputAdornment>
             ),
             ),
-        }),
+        }, htmlInput: pwdProps}),
         [showPassword, handleClickShowPassword, handleMouseDownPassword]
         [showPassword, handleClickShowPassword, handleMouseDownPassword]
     );
     );
 
 
@@ -145,7 +145,7 @@ const Login = (props: LoginProps) => {
                     onChange={changeInput}
                     onChange={changeInput}
                     data-input="user"
                     data-input="user"
                     onKeyDown={handleEnter}
                     onKeyDown={handleEnter}
-                    inputProps={userProps}
+                    slotProps={userProps}
                 ></TextField>
                 ></TextField>
                 <TextField
                 <TextField
                     variant="outlined"
                     variant="outlined"
@@ -159,8 +159,7 @@ const Login = (props: LoginProps) => {
                     onChange={changeInput}
                     onChange={changeInput}
                     data-input="password"
                     data-input="password"
                     onKeyDown={handleEnter}
                     onKeyDown={handleEnter}
-                    inputProps={pwdProps}
-                    InputProps={passwordProps}
+                    slotProps={passwordProps}
                 />
                 />
                 <DialogContentText>{message || defaultMessage}</DialogContentText>
                 <DialogContentText>{message || defaultMessage}</DialogContentText>
             </DialogContent>
             </DialogContent>

+ 45 - 45
frontend/taipy/package-lock.json

@@ -660,18 +660,18 @@
       "dev": true
       "dev": true
     },
     },
     "node_modules/@mui/core-downloads-tracker": {
     "node_modules/@mui/core-downloads-tracker": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.1.0.tgz",
-      "integrity": "sha512-covEnIn/2er5YdtuukDRA52kmARhKrHjOvPsyTFMQApZdrTBI4h8jbEy2mxZqwMwcAFS9coonQXnEZKL1rUNdQ==",
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.1.1.tgz",
+      "integrity": "sha512-VdQC1tPIIcZAnf62L2M1eQif0x2vlKg3YK4kGYbtijSH4niEgI21GnstykW1vQIs+Bc6L+Hua2GATYVjilJ22A==",
       "funding": {
       "funding": {
         "type": "opencollective",
         "type": "opencollective",
         "url": "https://opencollective.com/mui-org"
         "url": "https://opencollective.com/mui-org"
       }
       }
     },
     },
     "node_modules/@mui/icons-material": {
     "node_modules/@mui/icons-material": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.1.0.tgz",
-      "integrity": "sha512-HxfB0jxwiMTYMN8gAnYn3avbF1aDrqBEuGIj6JDQ3YkLl650E1Wy8AIhwwyP47wdrv0at9aAR0iOO6VLb74A9w==",
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.1.1.tgz",
+      "integrity": "sha512-sy/YKwcLPW8VcacNP2uWMYR9xyWuwO9NN9FXuGEU90bRshBXj8pdKk+joe3TCW7oviVS3zXLHlc94wQ0jNsQRQ==",
       "dependencies": {
       "dependencies": {
         "@babel/runtime": "^7.25.6"
         "@babel/runtime": "^7.25.6"
       },
       },
@@ -683,7 +683,7 @@
         "url": "https://opencollective.com/mui-org"
         "url": "https://opencollective.com/mui-org"
       },
       },
       "peerDependencies": {
       "peerDependencies": {
-        "@mui/material": "^6.1.0",
+        "@mui/material": "^6.1.1",
         "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
         "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
         "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
         "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
       },
       },
@@ -694,15 +694,15 @@
       }
       }
     },
     },
     "node_modules/@mui/material": {
     "node_modules/@mui/material": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.1.0.tgz",
-      "integrity": "sha512-4MJ46vmy1xbm8x+ZdRcWm8jEMMowdS8pYlhKQzg/qoKhOcLhImZvf2Jn6z9Dj6gl+lY+C/0MxaHF/avAAGys3Q==",
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.1.1.tgz",
+      "integrity": "sha512-b+eULldTqtqTCbN++2BtBWCir/1LwEYw+2mIlOt2GiEUh1EBBw4/wIukGKKNt3xrCZqRA80yLLkV6tF61Lq3cA==",
       "dependencies": {
       "dependencies": {
         "@babel/runtime": "^7.25.6",
         "@babel/runtime": "^7.25.6",
-        "@mui/core-downloads-tracker": "^6.1.0",
-        "@mui/system": "^6.1.0",
-        "@mui/types": "^7.2.16",
-        "@mui/utils": "^6.1.0",
+        "@mui/core-downloads-tracker": "^6.1.1",
+        "@mui/system": "^6.1.1",
+        "@mui/types": "^7.2.17",
+        "@mui/utils": "^6.1.1",
         "@popperjs/core": "^2.11.8",
         "@popperjs/core": "^2.11.8",
         "@types/react-transition-group": "^4.4.11",
         "@types/react-transition-group": "^4.4.11",
         "clsx": "^2.1.1",
         "clsx": "^2.1.1",
@@ -721,7 +721,7 @@
       "peerDependencies": {
       "peerDependencies": {
         "@emotion/react": "^11.5.0",
         "@emotion/react": "^11.5.0",
         "@emotion/styled": "^11.3.0",
         "@emotion/styled": "^11.3.0",
-        "@mui/material-pigment-css": "^6.1.0",
+        "@mui/material-pigment-css": "^6.1.1",
         "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
         "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
         "react": "^17.0.0 || ^18.0.0 || ^19.0.0",
         "react": "^17.0.0 || ^18.0.0 || ^19.0.0",
         "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
         "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
@@ -742,12 +742,12 @@
       }
       }
     },
     },
     "node_modules/@mui/private-theming": {
     "node_modules/@mui/private-theming": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.1.0.tgz",
-      "integrity": "sha512-+L5qccs4gwsR0r1dgjqhN24QEQRkqIbfOdxILyMbMkuI50x6wNyt9XrV+J3WtjtZTMGJCrUa5VmZBE6OEPGPWA==",
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.1.1.tgz",
+      "integrity": "sha512-JlrjIdhyZUtewtdAuUsvi3ZnO0YS49IW4Mfz19ZWTlQ0sDGga6LNPVwHClWr2/zJK2we2BQx9/i8M32rgKuzrg==",
       "dependencies": {
       "dependencies": {
         "@babel/runtime": "^7.25.6",
         "@babel/runtime": "^7.25.6",
-        "@mui/utils": "^6.1.0",
+        "@mui/utils": "^6.1.1",
         "prop-types": "^15.8.1"
         "prop-types": "^15.8.1"
       },
       },
       "engines": {
       "engines": {
@@ -768,9 +768,9 @@
       }
       }
     },
     },
     "node_modules/@mui/styled-engine": {
     "node_modules/@mui/styled-engine": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.1.0.tgz",
-      "integrity": "sha512-MZ+vtaCkjamrT41+b0Er9OMenjAtP/32+L6fARL9/+BZKuV2QbR3q3TmavT2x0NhDu35IM03s4yKqj32Ziqnyg==",
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.1.1.tgz",
+      "integrity": "sha512-HJyIoMpFb11fnHuRtUILOXgq6vj4LhIlE8maG4SwP/W+E5sa7HFexhnB3vOMT7bKys4UKNxhobC8jwWxYilGsA==",
       "dependencies": {
       "dependencies": {
         "@babel/runtime": "^7.25.6",
         "@babel/runtime": "^7.25.6",
         "@emotion/cache": "^11.13.1",
         "@emotion/cache": "^11.13.1",
@@ -800,15 +800,15 @@
       }
       }
     },
     },
     "node_modules/@mui/system": {
     "node_modules/@mui/system": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.1.0.tgz",
-      "integrity": "sha512-NumkGDqT6EdXfcoFLYQ+M4XlTW5hH3+aK48xAbRqKPXJfxl36CBt4DLduw/Voa5dcayGus9T6jm1AwU2hoJ5hQ==",
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.1.1.tgz",
+      "integrity": "sha512-PaYsCz2tUOcpu3T0okDEsSuP/yCDIj9JZ4Tox1JovRSKIjltHpXPsXZSGr3RiWdtM1MTQMFMCZzu0+CKbyy+Kw==",
       "dependencies": {
       "dependencies": {
         "@babel/runtime": "^7.25.6",
         "@babel/runtime": "^7.25.6",
-        "@mui/private-theming": "^6.1.0",
-        "@mui/styled-engine": "^6.1.0",
-        "@mui/types": "^7.2.16",
-        "@mui/utils": "^6.1.0",
+        "@mui/private-theming": "^6.1.1",
+        "@mui/styled-engine": "^6.1.1",
+        "@mui/types": "^7.2.17",
+        "@mui/utils": "^6.1.1",
         "clsx": "^2.1.1",
         "clsx": "^2.1.1",
         "csstype": "^3.1.3",
         "csstype": "^3.1.3",
         "prop-types": "^15.8.1"
         "prop-types": "^15.8.1"
@@ -839,9 +839,9 @@
       }
       }
     },
     },
     "node_modules/@mui/types": {
     "node_modules/@mui/types": {
-      "version": "7.2.16",
-      "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.16.tgz",
-      "integrity": "sha512-qI8TV3M7ShITEEc8Ih15A2vLzZGLhD+/UPNwck/hcls2gwg7dyRjNGXcQYHKLB5Q7PuTRfrTkAoPa2VV1s67Ag==",
+      "version": "7.2.17",
+      "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.17.tgz",
+      "integrity": "sha512-oyumoJgB6jDV8JFzRqjBo2daUuHpzDjoO/e3IrRhhHo/FxJlaVhET6mcNrKHUq2E+R+q3ql0qAtvQ4rfWHhAeQ==",
       "peerDependencies": {
       "peerDependencies": {
         "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0"
         "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0"
       },
       },
@@ -852,12 +852,12 @@
       }
       }
     },
     },
     "node_modules/@mui/utils": {
     "node_modules/@mui/utils": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.1.0.tgz",
-      "integrity": "sha512-oT8ZzMISRUhTVpdbYzY0CgrCBb3t/YEdcaM13tUnuTjZ15pdA6g5lx15ZJUdgYXV6PbJdw7tDQgMEr4uXK5TXQ==",
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.1.1.tgz",
+      "integrity": "sha512-HlRrgdJSPbYDXPpoVMWZV8AE7WcFtAk13rWNWAEVWKSanzBBkymjz3km+Th/Srowsh4pf1fTSP1B0L116wQBYw==",
       "dependencies": {
       "dependencies": {
         "@babel/runtime": "^7.25.6",
         "@babel/runtime": "^7.25.6",
-        "@mui/types": "^7.2.16",
+        "@mui/types": "^7.2.17",
         "@types/prop-types": "^15.7.12",
         "@types/prop-types": "^15.7.12",
         "clsx": "^2.1.1",
         "clsx": "^2.1.1",
         "prop-types": "^15.8.1",
         "prop-types": "^15.8.1",
@@ -1223,9 +1223,9 @@
       }
       }
     },
     },
     "node_modules/@types/estree": {
     "node_modules/@types/estree": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
-      "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
+      "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
       "dev": true
       "dev": true
     },
     },
     "node_modules/@types/hoist-non-react-statics": {
     "node_modules/@types/hoist-non-react-statics": {
@@ -2080,9 +2080,9 @@
       }
       }
     },
     },
     "node_modules/caniuse-lite": {
     "node_modules/caniuse-lite": {
-      "version": "1.0.30001660",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001660.tgz",
-      "integrity": "sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg==",
+      "version": "1.0.30001662",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001662.tgz",
+      "integrity": "sha512-sgMUVwLmGseH8ZIrm1d51UbrhqMCH3jvS7gF/M6byuHOnKyLOBL7W8yz5V02OHwgLGA36o/AFhWzzh4uc5aqTA==",
       "dev": true,
       "dev": true,
       "funding": [
       "funding": [
         {
         {
@@ -2409,9 +2409,9 @@
       }
       }
     },
     },
     "node_modules/electron-to-chromium": {
     "node_modules/electron-to-chromium": {
-      "version": "1.5.24",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.24.tgz",
-      "integrity": "sha512-0x0wLCmpdKFCi9ulhvYZebgcPmHTkFVUfU2wzDykadkslKwT4oAmDTHEKLnlrDsMGZe4B+ksn8quZfZjYsBetA==",
+      "version": "1.5.25",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.25.tgz",
+      "integrity": "sha512-kMb204zvK3PsSlgvvwzI3wBIcAw15tRkYk+NQdsjdDtcQWTp2RABbMQ9rUBy8KNEOM+/E6ep+XC3AykiWZld4g==",
       "dev": true
       "dev": true
     },
     },
     "node_modules/enhanced-resolve": {
     "node_modules/enhanced-resolve": {

+ 4 - 4
frontend/taipy/src/DataNodeViewer.tsx

@@ -786,7 +786,7 @@ const DataNodeViewer = (props: DataNodeViewerProps) => {
                                                 sx={FieldNoMaxWidth}
                                                 sx={FieldNoMaxWidth}
                                                 value={label || ""}
                                                 value={label || ""}
                                                 onChange={onLabelChange}
                                                 onChange={onLabelChange}
-                                                InputProps={{
+                                                slotProps={{input:{
                                                     endAdornment: (
                                                     endAdornment: (
                                                         <InputAdornment position="end">
                                                         <InputAdornment position="end">
                                                             <Tooltip title="Apply">
                                                             <Tooltip title="Apply">
@@ -809,7 +809,7 @@ const DataNodeViewer = (props: DataNodeViewerProps) => {
                                                             </Tooltip>
                                                             </Tooltip>
                                                         </InputAdornment>
                                                         </InputAdornment>
                                                     ),
                                                     ),
-                                                }}
+                                                }}}
                                                 disabled={!valid}
                                                 disabled={!valid}
                                             />
                                             />
                                         ) : (
                                         ) : (
@@ -1067,7 +1067,7 @@ const DataNodeViewer = (props: DataNodeViewerProps) => {
                                                                 ? "number"
                                                                 ? "number"
                                                                 : undefined
                                                                 : undefined
                                                         }
                                                         }
-                                                        InputProps={{
+                                                        slotProps={{input: {
                                                             endAdornment: (
                                                             endAdornment: (
                                                                 <InputAdornment position="end">
                                                                 <InputAdornment position="end">
                                                                     <Tooltip title="Apply">
                                                                     <Tooltip title="Apply">
@@ -1090,7 +1090,7 @@ const DataNodeViewer = (props: DataNodeViewerProps) => {
                                                                     </Tooltip>
                                                                     </Tooltip>
                                                                 </InputAdornment>
                                                                 </InputAdornment>
                                                             ),
                                                             ),
-                                                        }}
+                                                        }}}
                                                         disabled={!valid}
                                                         disabled={!valid}
                                                     />
                                                     />
                                                 )}
                                                 )}

+ 4 - 4
frontend/taipy/src/PropertiesEditor.tsx

@@ -206,7 +206,7 @@ const PropertiesEditor = (props: PropertiesEditorProps) => {
                                                   data-name="key"
                                                   data-name="key"
                                                   data-id={property.id}
                                                   data-id={property.id}
                                                   onChange={updatePropertyField}
                                                   onChange={updatePropertyField}
-                                                  inputProps={{ onKeyDown }}
+                                                  slotProps={{ input: { onKeyDown } }}
                                               />
                                               />
                                           </Grid>
                                           </Grid>
                                           <Grid size={5}>
                                           <Grid size={5}>
@@ -219,7 +219,7 @@ const PropertiesEditor = (props: PropertiesEditorProps) => {
                                                   data-name="value"
                                                   data-name="value"
                                                   data-id={property.id}
                                                   data-id={property.id}
                                                   onChange={updatePropertyField}
                                                   onChange={updatePropertyField}
-                                                  inputProps={{ onKeyDown, "data-enter": true }}
+                                                  slotProps={{ htmlInput: { onKeyDown, "data-enter": true } }}
                                               />
                                               />
                                           </Grid>
                                           </Grid>
                                           <Grid
                                           <Grid
@@ -309,7 +309,7 @@ const PropertiesEditor = (props: PropertiesEditorProps) => {
                                     variant="outlined"
                                     variant="outlined"
                                     sx={FieldNoMaxWidth}
                                     sx={FieldNoMaxWidth}
                                     disabled={!isDefined}
                                     disabled={!isDefined}
-                                    inputProps={{ onKeyDown }}
+                                    slotProps={{ htmlInput: { onKeyDown } }}
                                 />
                                 />
                             </Grid>
                             </Grid>
                             <Grid size={5}>
                             <Grid size={5}>
@@ -321,7 +321,7 @@ const PropertiesEditor = (props: PropertiesEditorProps) => {
                                     variant="outlined"
                                     variant="outlined"
                                     sx={FieldNoMaxWidth}
                                     sx={FieldNoMaxWidth}
                                     disabled={!isDefined}
                                     disabled={!isDefined}
-                                    inputProps={{ onKeyDown, "data-enter": true }}
+                                    slotProps={{ htmlInput: { onKeyDown, "data-enter": true }}}
                                 />
                                 />
                             </Grid>
                             </Grid>
                             <Grid size={2} container alignContent="center" alignItems="center" justifyContent="center">
                             <Grid size={2} container alignContent="center" alignItems="center" justifyContent="center">

+ 6 - 6
frontend/taipy/src/ScenarioViewer.tsx

@@ -629,7 +629,7 @@ const ScenarioViewer = (props: ScenarioViewerProps) => {
                         expandIcon={expandable ? <ArrowForwardIosSharp sx={AccordionIconSx} /> : null}
                         expandIcon={expandable ? <ArrowForwardIosSharp sx={AccordionIconSx} /> : null}
                         sx={AccordionSummarySx}
                         sx={AccordionSummarySx}
                     >
                     >
-                        <Stack direction="row" justifyContent="space-between" width="100%" alignItems="center">
+                        <Stack direction="row" justifyContent="space-between" width="100%" alignItems="baseline">
                             <Stack direction="row" spacing={1}>
                             <Stack direction="row" spacing={1}>
                                 <Typography>{scLabel}</Typography>
                                 <Typography>{scLabel}</Typography>
                                 {scPrimary ? (
                                 {scPrimary ? (
@@ -712,7 +712,7 @@ const ScenarioViewer = (props: ScenarioViewerProps) => {
                                             sx={FieldNoMaxWidth}
                                             sx={FieldNoMaxWidth}
                                             value={label || ""}
                                             value={label || ""}
                                             onChange={onLabelChange}
                                             onChange={onLabelChange}
-                                            InputProps={{
+                                            slotProps={{input: {
                                                 onKeyDown: onLabelKeyDown,
                                                 onKeyDown: onLabelKeyDown,
                                                 endAdornment: (
                                                 endAdornment: (
                                                     <InputAdornment position="end">
                                                     <InputAdornment position="end">
@@ -736,7 +736,7 @@ const ScenarioViewer = (props: ScenarioViewerProps) => {
                                                         </Tooltip>
                                                         </Tooltip>
                                                     </InputAdornment>
                                                     </InputAdornment>
                                                 ),
                                                 ),
-                                            }}
+                                            }}}
                                             disabled={!valid}
                                             disabled={!valid}
                                         />
                                         />
                                     ) : (
                                     ) : (
@@ -752,7 +752,7 @@ const ScenarioViewer = (props: ScenarioViewerProps) => {
                                 </Grid>
                                 </Grid>
                                 {showTags ? (
                                 {showTags ? (
                                     <Grid
                                     <Grid
-                                    size={12}
+                                        size={12}
                                         container
                                         container
                                         justifyContent="space-between"
                                         justifyContent="space-between"
                                         data-focus="tags"
                                         data-focus="tags"
@@ -787,7 +787,7 @@ const ScenarioViewer = (props: ScenarioViewerProps) => {
                                                         label="Tags"
                                                         label="Tags"
                                                         sx={tagsAutocompleteSx}
                                                         sx={tagsAutocompleteSx}
                                                         fullWidth
                                                         fullWidth
-                                                        InputProps={{
+                                                        slotProps={{input: {
                                                             ...params.InputProps,
                                                             ...params.InputProps,
                                                             onKeyDown: onTagsKeyDown,
                                                             onKeyDown: onTagsKeyDown,
                                                             endAdornment: (
                                                             endAdornment: (
@@ -812,7 +812,7 @@ const ScenarioViewer = (props: ScenarioViewerProps) => {
                                                                     </Tooltip>
                                                                     </Tooltip>
                                                                 </>
                                                                 </>
                                                             ),
                                                             ),
-                                                        }}
+                                                        }}}
                                                     />
                                                     />
                                                 )}
                                                 )}
                                                 disabled={!valid}
                                                 disabled={!valid}

+ 1 - 1
taipy/core/job/_job_manager.py

@@ -94,7 +94,7 @@ class _JobManager(_Manager[Job], _VersionMixin):
         if isinstance(job, str):
         if isinstance(job, str):
             job = cls._get(job)
             job = cls._get(job)
 
 
-        if not job.is_finished():
+        if job and not job.is_finished():
             reason_collector._add_reason(job.id, JobIsNotFinished(job.id))
             reason_collector._add_reason(job.id, JobIsNotFinished(job.id))
 
 
         return reason_collector
         return reason_collector

+ 5 - 4
taipy/gui/_gui_section.py

@@ -19,7 +19,7 @@ from ._default_config import default_config
 
 
 
 
 class _GuiSection(UniqueSection):
 class _GuiSection(UniqueSection):
-    name = "gui"
+    name = "gui" # type: ignore[reportAssignmentType]
 
 
     def __init__(self, property_list: t.Optional[t.List] = None, **properties):
     def __init__(self, property_list: t.Optional[t.List] = None, **properties):
         self._property_list = property_list
         self._property_list = property_list
@@ -37,13 +37,14 @@ class _GuiSection(UniqueSection):
         return as_dict
         return as_dict
 
 
     @classmethod
     @classmethod
-    def _from_dict(cls, as_dict: t.Dict[str, t.Any], *_):
-        return _GuiSection(property_list=list(default_config), **as_dict)
+    def _from_dict(cls, config_as_dict: t.Dict[str, t.Any], id, config):
+        return _GuiSection(property_list=list(default_config), **config_as_dict)
 
 
     def _update(self, config_as_dict: t.Dict[str, t.Any], default_section=None):
     def _update(self, config_as_dict: t.Dict[str, t.Any], default_section=None):
+        as_dict = None
         if self._property_list:
         if self._property_list:
             as_dict = {k: v for k, v in config_as_dict.items() if k in self._property_list}
             as_dict = {k: v for k, v in config_as_dict.items() if k in self._property_list}
-        self._properties.update(as_dict)
+        self._properties.update(as_dict or config_as_dict)
 
 
     @staticmethod
     @staticmethod
     def _configure(**properties) -> "_GuiSection":
     def _configure(**properties) -> "_GuiSection":

+ 1 - 1
taipy/gui/_page.py

@@ -25,7 +25,7 @@ class _Page(object):
     def __init__(self) -> None:
     def __init__(self) -> None:
         self._rendered_jsx: t.Optional[str] = None
         self._rendered_jsx: t.Optional[str] = None
         self._renderer: t.Optional[Page] = None
         self._renderer: t.Optional[Page] = None
-        self._style: t.Optional[str] = None
+        self._style: t.Optional[t.Union[str, t.Dict[str, t.Any]]] = None
         self._route: t.Optional[str] = None
         self._route: t.Optional[str] = None
         self._head: t.Optional[list] = None
         self._head: t.Optional[list] = None
 
 

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

@@ -685,7 +685,7 @@ class _Factory:
         builder = _Factory.__CONTROL_BUILDERS.get(name)
         builder = _Factory.__CONTROL_BUILDERS.get(name)
         built = None
         built = None
         _Factory.__COUNTER += 1
         _Factory.__COUNTER += 1
-        with gui._get_autorization():
+        with gui._get_authorization():
             if builder is None:
             if builder is None:
                 lib, element_name, element = _Factory.__get_library_element(name)
                 lib, element_name, element = _Factory.__get_library_element(name)
                 if lib:
                 if lib:

+ 23 - 23
taipy/gui/config.py

@@ -162,14 +162,16 @@ class _Config(object):
         self.get_time_zone()
         self.get_time_zone()
 
 
     def _get_config(self, name: ConfigParameter, default_value: t.Any) -> t.Any:  # pragma: no cover
     def _get_config(self, name: ConfigParameter, default_value: t.Any) -> t.Any:  # pragma: no cover
-        if name in self.config and self.config[name] is not None:
-            if default_value is not None and not isinstance(self.config[name], type(default_value)):
+        if name in self.config and self.config.get(name) is not None:
+            if default_value is not None and not isinstance(self.config.get(name), type(default_value)):
                 try:
                 try:
-                    return type(default_value)(self.config[name])
+                    return type(default_value)(self.config.get(name))
                 except Exception as e:
                 except Exception as e:
-                    _warn(f'app_config "{name}" value "{self.config[name]}" is not of type {type(default_value)}', e)
+                    _warn(
+                        f'app_config "{name}" value "{self.config.get(name)}" is not of type {type(default_value)}', e
+                    )
                     return default_value
                     return default_value
-            return self.config[name]
+            return self.config.get(name)
         return default_value
         return default_value
 
 
     def get_time_zone(self) -> t.Optional[str]:
     def get_time_zone(self) -> t.Optional[str]:
@@ -234,12 +236,12 @@ class _Config(object):
                     config[key] = _default_stylekit if value else {}
                     config[key] = _default_stylekit if value else {}
                     continue
                     continue
                 try:
                 try:
-                    if isinstance(value, dict) and isinstance(config[key], dict):
-                        config[key].update(value)
+                    if isinstance(value, dict) and isinstance(config.get(key), dict):
+                        t.cast(dict, config.get(key)).update(value)
                     elif key == "port" and str(value).strip() == "auto":
                     elif key == "port" and str(value).strip() == "auto":
                         config["port"] = "auto"
                         config["port"] = "auto"
                     else:
                     else:
-                        config[key] = value if config[key] is None else type(config[key])(value)
+                        config[key] = value if config.get(key) is None else type(config.get(key))(value)  # type: ignore[reportCallIssue]
                 except Exception as e:
                 except Exception as e:
                     _warn(
                     _warn(
                         f"Invalid keyword arguments value in Gui.run {key} - {value}. Unable to parse value to the correct type",  # noqa: E501
                         f"Invalid keyword arguments value in Gui.run {key} - {value}. Unable to parse value to the correct type",  # noqa: E501
@@ -282,29 +284,29 @@ class _Config(object):
         app_config = self.config
         app_config = self.config
         logger = _TaipyLogger._get_logger()
         logger = _TaipyLogger._get_logger()
         # Special config for notebook runtime
         # Special config for notebook runtime
-        if _is_in_notebook() or app_config["run_in_thread"] and not app_config["single_client"]:
+        if _is_in_notebook() or app_config.get("run_in_thread") and not app_config.get("single_client"):
             app_config["single_client"] = True
             app_config["single_client"] = True
             self.__log_outside_reloader(logger, "Running in 'single_client' mode in notebook environment")
             self.__log_outside_reloader(logger, "Running in 'single_client' mode in notebook environment")
 
 
-        if app_config["run_server"] and app_config["ngrok_token"] and app_config["use_reloader"]:
+        if app_config.get("run_server") and app_config.get("ngrok_token") and app_config.get("use_reloader"):
             app_config["use_reloader"] = False
             app_config["use_reloader"] = False
             self.__log_outside_reloader(
             self.__log_outside_reloader(
                 logger, "'use_reloader' parameter will not be used when 'ngrok_token' parameter is available"
                 logger, "'use_reloader' parameter will not be used when 'ngrok_token' parameter is available"
             )
             )
 
 
-        if app_config["use_reloader"] and _is_in_notebook():
+        if app_config.get("use_reloader") and _is_in_notebook():
             app_config["use_reloader"] = False
             app_config["use_reloader"] = False
             self.__log_outside_reloader(logger, "'use_reloader' parameter is not available in notebook environment")
             self.__log_outside_reloader(logger, "'use_reloader' parameter is not available in notebook environment")
 
 
-        if app_config["use_reloader"] and not app_config["debug"]:
+        if app_config.get("use_reloader") and not app_config.get("debug"):
             app_config["debug"] = True
             app_config["debug"] = True
             self.__log_outside_reloader(logger, "Application is running in 'debug' mode")
             self.__log_outside_reloader(logger, "Application is running in 'debug' mode")
 
 
-        if app_config["debug"] and not app_config["allow_unsafe_werkzeug"]:
+        if app_config.get("debug") and not app_config.get("allow_unsafe_werkzeug"):
             app_config["allow_unsafe_werkzeug"] = True
             app_config["allow_unsafe_werkzeug"] = True
             self.__log_outside_reloader(logger, "'allow_unsafe_werkzeug' has been set to True")
             self.__log_outside_reloader(logger, "'allow_unsafe_werkzeug' has been set to True")
 
 
-        if app_config["debug"] and app_config["async_mode"] != "threading":
+        if app_config.get("debug") and app_config.get("async_mode") != "threading":
             app_config["async_mode"] = "threading"
             app_config["async_mode"] = "threading"
             self.__log_outside_reloader(
             self.__log_outside_reloader(
                 logger,
                 logger,
@@ -318,17 +320,15 @@ class _Config(object):
     def _resolve_stylekit(self):
     def _resolve_stylekit(self):
         app_config = self.config
         app_config = self.config
         # support legacy margin variable
         # support legacy margin variable
-        stylekit_config = app_config["stylekit"]
+        stylekit_config = app_config.get("stylekit")
 
 
-        if isinstance(app_config["stylekit"], dict) and "root_margin" in app_config["stylekit"]:
+        if isinstance(stylekit_config, dict) and "root_margin" in stylekit_config:
             from ._default_config import _default_stylekit, default_config
             from ._default_config import _default_stylekit, default_config
 
 
-            stylekit_config = app_config["stylekit"]
-            if (
-                stylekit_config["root_margin"] == _default_stylekit["root_margin"]
-                and app_config["margin"] != default_config["margin"]
-            ):
-                app_config["stylekit"]["root_margin"] = str(app_config["margin"])
+            if stylekit_config.get("root_margin") == _default_stylekit.get("root_margin") and app_config.get(
+                "margin"
+            ) != default_config.get("margin"):
+                stylekit_config["root_margin"] = str(app_config.get("margin"))
             app_config["margin"] = None
             app_config["margin"] = None
 
 
     def _resolve_url_prefix(self):
     def _resolve_url_prefix(self):
@@ -343,4 +343,4 @@ class _Config(object):
 
 
     def _resolve_notebook_proxy(self):
     def _resolve_notebook_proxy(self):
         app_config = self.config
         app_config = self.config
-        app_config["notebook_proxy"] = app_config["notebook_proxy"] if _is_in_notebook() else False
+        app_config["notebook_proxy"] = app_config.get("notebook_proxy", False) if _is_in_notebook() else False

+ 42 - 40
taipy/gui/gui.py

@@ -470,7 +470,7 @@ class Gui:
 
 
             if callable(provider_fn):
             if callable(provider_fn):
                 try:
                 try:
-                    return provider_fn(content)
+                    return provider_fn(t.cast(t.Any, content))
                 except Exception as e:
                 except Exception as e:
                     _warn(f"Error in content provider for type {str(type(content))}", e)
                     _warn(f"Error in content provider for type {str(type(content))}", e)
         return (
         return (
@@ -639,7 +639,7 @@ class Gui:
             self.__set_client_id_in_context(expected_client_id)
             self.__set_client_id_in_context(expected_client_id)
             g.ws_client_id = expected_client_id
             g.ws_client_id = expected_client_id
             with self._set_locals_context(message.get("module_context") or None):
             with self._set_locals_context(message.get("module_context") or None):
-                with self._get_autorization():
+                with self._get_authorization():
                     payload = message.get("payload", {})
                     payload = message.get("payload", {})
                     if msg_type == _WsType.UPDATE.value:
                     if msg_type == _WsType.UPDATE.value:
                         self.__front_end_update(
                         self.__front_end_update(
@@ -966,7 +966,7 @@ class Gui:
     def __upload_files(self):
     def __upload_files(self):
         self.__set_client_id_in_context()
         self.__set_client_id_in_context()
         on_upload_action = request.form.get("on_action", None)
         on_upload_action = request.form.get("on_action", None)
-        var_name = request.form.get("var_name", None)
+        var_name = t.cast(str, request.form.get("var_name", None))
         if not var_name and not on_upload_action:
         if not var_name and not on_upload_action:
             _warn("upload files: No var name")
             _warn("upload files: No var name")
             return ("upload files: No var name", 400)
             return ("upload files: No var name", 400)
@@ -1057,7 +1057,7 @@ class Gui:
             else:
             else:
                 if isinstance(newvalue, (_TaipyContent, _TaipyContentImage)):
                 if isinstance(newvalue, (_TaipyContent, _TaipyContentImage)):
                     ret_value = self.__get_content_accessor().get_info(
                     ret_value = self.__get_content_accessor().get_info(
-                        front_var, newvalue.get(), isinstance(newvalue, _TaipyContentImage)
+                        t.cast(str, front_var), newvalue.get(), isinstance(newvalue, _TaipyContentImage)
                     )
                     )
                     if isinstance(ret_value, tuple):
                     if isinstance(ret_value, tuple):
                         newvalue = f"/{Gui.__CONTENT_ROOT}/{ret_value[0]}"
                         newvalue = f"/{Gui.__CONTENT_ROOT}/{ret_value[0]}"
@@ -1160,8 +1160,8 @@ class Gui:
                 page_path = Gui.__root_page_name
                 page_path = Gui.__root_page_name
             # Get Module Context
             # Get Module Context
             if mc := self._get_page_context(page_path):
             if mc := self._get_page_context(page_path):
-                page_renderer = self._get_page(page_path)._renderer
-                self._bind_custom_page_variables(page_renderer, self._get_client_id())
+                page_renderer = t.cast(_Page, self._get_page(page_path))._renderer
+                self._bind_custom_page_variables(t.cast(t.Any, page_renderer), self._get_client_id())
                 # get metadata if there is one
                 # get metadata if there is one
                 metadata: t.Dict[str, t.Any] = {}
                 metadata: t.Dict[str, t.Any] = {}
                 if hasattr(page_renderer, "_metadata"):
                 if hasattr(page_renderer, "_metadata"):
@@ -1245,12 +1245,12 @@ class Gui:
 
 
     def __handle_ws_get_routes(self):
     def __handle_ws_get_routes(self):
         routes = (
         routes = (
-            [[self._config.root_page._route, self._config.root_page._renderer.page_type]]
+            [[self._config.root_page._route, t.cast(t.Any, self._config.root_page._renderer).page_type]]
             if self._config.root_page
             if self._config.root_page
             else []
             else []
         )
         )
         routes += [
         routes += [
-            [page._route, page._renderer.page_type]
+            [page._route, t.cast(t.Any, page._renderer).page_type]
             for page in self._config.pages
             for page in self._config.pages
             if page._route != Gui.__root_page_name
             if page._route != Gui.__root_page_name
         ]
         ]
@@ -1269,7 +1269,7 @@ class Gui:
                 self._server._ws.emit(
                 self._server._ws.emit(
                     "message",
                     "message",
                     payload,
                     payload,
-                    to=self.__get_ws_receiver(send_back_only),
+                    to=t.cast(str, self.__get_ws_receiver(send_back_only)),
                 )
                 )
                 time.sleep(0.001)
                 time.sleep(0.001)
             except Exception as e:  # pragma: no cover
             except Exception as e:  # pragma: no cover
@@ -1280,7 +1280,7 @@ class Gui:
     def __broadcast_ws(self, payload: dict, client_id: t.Optional[str] = None):
     def __broadcast_ws(self, payload: dict, client_id: t.Optional[str] = None):
         try:
         try:
             to = list(self.__get_sids(client_id)) if client_id else []
             to = list(self.__get_sids(client_id)) if client_id else []
-            self._server._ws.emit("message", payload, to=to if to else None, include_self=True)
+            self._server._ws.emit("message", payload, to=t.cast(str, to) if to else None, include_self=True)
             time.sleep(0.001)
             time.sleep(0.001)
         except Exception as e:  # pragma: no cover
         except Exception as e:  # pragma: no cover
             _warn(f"Exception raised in WebSocket communication in '{self.__frame.f_code.co_name}'", e)
             _warn(f"Exception raised in WebSocket communication in '{self.__frame.f_code.co_name}'", e)
@@ -1291,7 +1291,7 @@ class Gui:
                 self._server._ws.emit(
                 self._server._ws.emit(
                     "message",
                     "message",
                     {"type": _WsType.ACKNOWLEDGEMENT.value, "id": ack_id},
                     {"type": _WsType.ACKNOWLEDGEMENT.value, "id": ack_id},
-                    to=self.__get_ws_receiver(True),
+                    to=t.cast(str, self.__get_ws_receiver(True)),
                 )
                 )
                 time.sleep(0.001)
                 time.sleep(0.001)
             except Exception as e:  # pragma: no cover
             except Exception as e:  # pragma: no cover
@@ -1493,7 +1493,7 @@ class Gui:
 
 
     def __call_function_with_args(self, **kwargs):
     def __call_function_with_args(self, **kwargs):
         action_function = kwargs.get("action_function")
         action_function = kwargs.get("action_function")
-        id = kwargs.get("id")
+        id = t.cast(str, kwargs.get("id"))
         payload = kwargs.get("payload")
         payload = kwargs.get("payload")
 
 
         if callable(action_function):
         if callable(action_function):
@@ -1501,7 +1501,7 @@ class Gui:
                 argcount = action_function.__code__.co_argcount
                 argcount = action_function.__code__.co_argcount
                 if argcount > 0 and inspect.ismethod(action_function):
                 if argcount > 0 and inspect.ismethod(action_function):
                     argcount -= 1
                     argcount -= 1
-                args = [None for _ in range(argcount)]
+                args = t.cast(list, [None for _ in range(argcount)])
                 if argcount > 0:
                 if argcount > 0:
                     args[0] = self.__get_state()
                     args[0] = self.__get_state()
                 if argcount > 1:
                 if argcount > 1:
@@ -1746,7 +1746,7 @@ class Gui:
                         attributes.get("date_format"),
                         attributes.get("date_format"),
                         attributes.get("number_format"),
                         attributes.get("number_format"),
                     )
                     )
-                    _enhance_columns(attributes, hashes, col_dict, "table(cols)")
+                    _enhance_columns(attributes, hashes, t.cast(dict, col_dict), "table(cols)")
 
 
                     return json.dumps(col_dict, cls=_TaipyJsonEncoder)
                     return json.dumps(col_dict, cls=_TaipyJsonEncoder)
             except Exception as e:  # pragma: no cover
             except Exception as e:  # pragma: no cover
@@ -1846,7 +1846,7 @@ class Gui:
 
 
     def _get_locals_context(self) -> str:
     def _get_locals_context(self) -> str:
         current_context = self.__locals_context.get_context()
         current_context = self.__locals_context.get_context()
-        return current_context if current_context is not None else self.__default_module_name
+        return current_context if current_context is not None else t.cast(str, self.__default_module_name)
 
 
     def _set_locals_context(self, context: t.Optional[str]) -> t.ContextManager[None]:
     def _set_locals_context(self, context: t.Optional[str]) -> t.ContextManager[None]:
         return self.__locals_context.set_locals_context(context)
         return self.__locals_context.set_locals_context(context)
@@ -2409,7 +2409,7 @@ class Gui:
             The Flask instance used.
             The Flask instance used.
         """
         """
         if hasattr(self, "_server"):
         if hasattr(self, "_server"):
-            return self._server.get_flask()
+            return t.cast(Flask, self._server.get_flask())
         raise RuntimeError("get_flask_app() cannot be invoked before run() has been called.")
         raise RuntimeError("get_flask_app() cannot be invoked before run() has been called.")
 
 
     def _set_frame(self, frame: t.Optional[FrameType]):
     def _set_frame(self, frame: t.Optional[FrameType]):
@@ -2486,21 +2486,21 @@ class Gui:
                 self,
                 self,
                 path_mapping=self._path_mapping,
                 path_mapping=self._path_mapping,
                 flask=self._flask,
                 flask=self._flask,
-                async_mode=app_config["async_mode"],
-                allow_upgrades=not app_config["notebook_proxy"],
+                async_mode=app_config.get("async_mode"),
+                allow_upgrades=not app_config.get("notebook_proxy"),
                 server_config=app_config.get("server_config"),
                 server_config=app_config.get("server_config"),
             )
             )
 
 
         # Stop and reinitialize the server if it is still running as a thread
         # Stop and reinitialize the server if it is still running as a thread
-        if (_is_in_notebook() or app_config["run_in_thread"]) and hasattr(self._server, "_thread"):
+        if (_is_in_notebook() or app_config.get("run_in_thread")) and hasattr(self._server, "_thread"):
             self.stop()
             self.stop()
             self._flask_blueprint = []
             self._flask_blueprint = []
             self._server = _Server(
             self._server = _Server(
                 self,
                 self,
                 path_mapping=self._path_mapping,
                 path_mapping=self._path_mapping,
                 flask=self._flask,
                 flask=self._flask,
-                async_mode=app_config["async_mode"],
-                allow_upgrades=not app_config["notebook_proxy"],
+                async_mode=app_config.get("async_mode"),
+                allow_upgrades=not app_config.get("notebook_proxy"),
                 server_config=app_config.get("server_config"),
                 server_config=app_config.get("server_config"),
             )
             )
             self._bindings()._new_scopes()
             self._bindings()._new_scopes()
@@ -2509,16 +2509,16 @@ class Gui:
         app_config = self._config.config
         app_config = self._config.config
         if hasattr(self, "_ngrok"):
         if hasattr(self, "_ngrok"):
             # Keep the ngrok instance if token has not changed
             # Keep the ngrok instance if token has not changed
-            if app_config["ngrok_token"] == self._ngrok[1]:
+            if app_config.get("ngrok_token") == self._ngrok[1]:
                 _TaipyLogger._get_logger().info(f" * NGROK Public Url: {self._ngrok[0].public_url}")
                 _TaipyLogger._get_logger().info(f" * NGROK Public Url: {self._ngrok[0].public_url}")
                 return
                 return
             # Close the old tunnel so new tunnel can open for new token
             # Close the old tunnel so new tunnel can open for new token
-            ngrok.disconnect(self._ngrok[0].public_url)
-        if app_config["run_server"] and (token := app_config["ngrok_token"]):  # pragma: no cover
+            ngrok.disconnect(self._ngrok[0].public_url)  # type: ignore[reportPossiblyUnboundVariable]
+        if app_config.get("run_server") and (token := app_config.get("ngrok_token")):  # pragma: no cover
             if not util.find_spec("pyngrok"):
             if not util.find_spec("pyngrok"):
                 raise RuntimeError("Cannot use ngrok as pyngrok package is not installed.")
                 raise RuntimeError("Cannot use ngrok as pyngrok package is not installed.")
-            ngrok.set_auth_token(token)
-            self._ngrok = (ngrok.connect(app_config["port"], "http"), token)
+            ngrok.set_auth_token(token)  # type: ignore[reportPossiblyUnboundVariable]
+            self._ngrok = (ngrok.connect(app_config.get("port"), "http"), token)  # type: ignore[reportPossiblyUnboundVariable]
             _TaipyLogger._get_logger().info(f" * NGROK Public Url: {self._ngrok[0].public_url}")
             _TaipyLogger._get_logger().info(f" * NGROK Public Url: {self._ngrok[0].public_url}")
 
 
     def __bind_default_function(self):
     def __bind_default_function(self):
@@ -2611,7 +2611,7 @@ class Gui:
 
 
         # Register Flask Blueprint if available
         # Register Flask Blueprint if available
         for bp in self._flask_blueprint:
         for bp in self._flask_blueprint:
-            self._server.get_flask().register_blueprint(bp)
+            t.cast(Flask, self._server.get_flask()).register_blueprint(bp)
 
 
     def _get_accessor(self):
     def _get_accessor(self):
         if self.__accessors is None:
         if self.__accessors is None:
@@ -2711,7 +2711,7 @@ class Gui:
 
 
         locals_bind = _filter_locals(self.__frame.f_locals)
         locals_bind = _filter_locals(self.__frame.f_locals)
 
 
-        self.__locals_context.set_default(locals_bind, self.__default_module_name)
+        self.__locals_context.set_default(locals_bind, t.cast(str, self.__default_module_name))
 
 
         self.__var_dir.set_default(self.__frame)
         self.__var_dir.set_default(self.__frame)
 
 
@@ -2761,25 +2761,27 @@ class Gui:
         self.__register_blueprint()
         self.__register_blueprint()
 
 
         # Register data accessor communication data format (JSON, Apache Arrow)
         # Register data accessor communication data format (JSON, Apache Arrow)
-        self._get_accessor().set_data_format(_DataFormat.APACHE_ARROW if app_config["use_arrow"] else _DataFormat.JSON)
+        self._get_accessor().set_data_format(
+            _DataFormat.APACHE_ARROW if app_config.get("use_arrow") else _DataFormat.JSON
+        )
 
 
         # Use multi user or not
         # Use multi user or not
-        self._bindings()._set_single_client(bool(app_config["single_client"]))
+        self._bindings()._set_single_client(bool(app_config.get("single_client")))
 
 
         # Start Flask Server
         # Start Flask Server
         if not run_server:
         if not run_server:
             return self.get_flask_app()
             return self.get_flask_app()
 
 
         return self._server.run(
         return self._server.run(
-            host=app_config["host"],
-            port=app_config["port"],
-            debug=app_config["debug"],
-            use_reloader=app_config["use_reloader"],
-            flask_log=app_config["flask_log"],
-            run_in_thread=app_config["run_in_thread"],
-            allow_unsafe_werkzeug=app_config["allow_unsafe_werkzeug"],
-            notebook_proxy=app_config["notebook_proxy"],
-            port_auto_ranges=app_config["port_auto_ranges"],
+            host=app_config.get("host"),
+            port=app_config.get("port"),
+            debug=app_config.get("debug"),
+            use_reloader=app_config.get("use_reloader"),
+            flask_log=app_config.get("flask_log"),
+            run_in_thread=app_config.get("run_in_thread"),
+            allow_unsafe_werkzeug=app_config.get("allow_unsafe_werkzeug"),
+            notebook_proxy=app_config.get("notebook_proxy"),
+            port_auto_ranges=app_config.get("port_auto_ranges"),
         )
         )
 
 
     def reload(self):  # pragma: no cover
     def reload(self):  # pragma: no cover
@@ -2807,7 +2809,7 @@ class Gui:
             self._server.stop_thread()
             self._server.stop_thread()
             _TaipyLogger._get_logger().info("Gui server has been stopped.")
             _TaipyLogger._get_logger().info("Gui server has been stopped.")
 
 
-    def _get_autorization(self, client_id: t.Optional[str] = None, system: t.Optional[bool] = False):
+    def _get_authorization(self, client_id: t.Optional[str] = None, system: t.Optional[bool] = False):
         return contextlib.nullcontext()
         return contextlib.nullcontext()
 
 
     def set_favicon(self, favicon_path: t.Union[str, Path], state: t.Optional[State] = None):
     def set_favicon(self, favicon_path: t.Union[str, Path], state: t.Optional[State] = None):

+ 2 - 2
taipy/gui/partial.py

@@ -53,7 +53,7 @@ class Partial(_Page):
         else:
         else:
             self._route = route
             self._route = route
 
 
-    def update_content(self, state: State, content: str | "Page"):
+    def update_content(self, state: State, content: t.Union[str, "Page"]):
         """Update partial content.
         """Update partial content.
 
 
         Arguments:
         Arguments:
@@ -65,7 +65,7 @@ class Partial(_Page):
         else:
         else:
             _warn("'Partial.update_content()' must be called in the context of a callback.")
             _warn("'Partial.update_content()' must be called in the context of a callback.")
 
 
-    def __copy(self, content: str | "Page") -> Partial:
+    def __copy(self, content:  t.Union[str, "Page"]) -> Partial:
         new_partial = Partial(self._route)
         new_partial = Partial(self._route)
         from .page import Page
         from .page import Page
 
 

+ 5 - 5
taipy/gui/server.py

@@ -247,14 +247,14 @@ class _Server:
         return self._flask
         return self._flask
 
 
     def test_client(self):
     def test_client(self):
-        return self._flask.test_client()
+        return t.cast(Flask, self._flask).test_client()
 
 
     def _run_notebook(self):
     def _run_notebook(self):
         self._is_running = True
         self._is_running = True
         self._ws.run(self._flask, host=self._host, port=self._port, debug=False, use_reloader=False)
         self._ws.run(self._flask, host=self._host, port=self._port, debug=False, use_reloader=False)
 
 
     def _get_async_mode(self) -> str:
     def _get_async_mode(self) -> str:
-        return self._ws.async_mode
+        return self._ws.async_mode  # type: ignore[reportAttributeAccessIssue]
 
 
     def _apply_patch(self):
     def _apply_patch(self):
         if self._get_async_mode() == "gevent" and util.find_spec("gevent"):
         if self._get_async_mode() == "gevent" and util.find_spec("gevent"):
@@ -264,7 +264,7 @@ class _Server:
             if not monkey.is_module_patched("time"):
             if not monkey.is_module_patched("time"):
                 monkey.patch_time()
                 monkey.patch_time()
         if self._get_async_mode() == "eventlet" and util.find_spec("eventlet"):
         if self._get_async_mode() == "eventlet" and util.find_spec("eventlet"):
-            from eventlet import monkey_patch, patcher
+            from eventlet import monkey_patch, patcher  # type: ignore[reportMissingImport]
 
 
             if not patcher.is_monkey_patched("time"):
             if not patcher.is_monkey_patched("time"):
                 monkey_patch(time=True)
                 monkey_patch(time=True)
@@ -349,8 +349,8 @@ class _Server:
             self._is_running = False
             self._is_running = False
             with contextlib.suppress(Exception):
             with contextlib.suppress(Exception):
                 if self._get_async_mode() == "gevent":
                 if self._get_async_mode() == "gevent":
-                    if self._ws.wsgi_server is not None:
-                        self._ws.wsgi_server.stop()
+                    if self._ws.wsgi_server is not None:  # type: ignore[reportAttributeAccessIssue]
+                        self._ws.wsgi_server.stop()  # type: ignore[reportAttributeAccessIssue]
                     else:
                     else:
                         self._thread.kill()
                         self._thread.kill()
                 else:
                 else:

+ 13 - 13
taipy/gui/utils/_adapter.py

@@ -137,7 +137,7 @@ class _Adapter:
                 return (
                 return (
                     add(type(result)(tpl_res), result[len(tpl_res) :])
                     add(type(result)(tpl_res), result[len(tpl_res) :])
                     if isinstance(result, (tuple, list)) and isinstance(tpl_res, (tuple, list))
                     if isinstance(result, (tuple, list)) and isinstance(tpl_res, (tuple, list))
-                    else tpl_res
+                    else tpl_res # type: ignore[reportReturnType]
                 )
                 )
         except Exception as e:
         except Exception as e:
             _warn(f"Cannot run adapter for {var_name}", e)
             _warn(f"Cannot run adapter for {var_name}", e)
@@ -168,9 +168,9 @@ class _Adapter:
             if isinstance(value, (list, tuple)) and len(value):
             if isinstance(value, (list, tuple)) and len(value):
                 return self.__get_id(value[0], False)
                 return self.__get_id(value[0], False)
             elif hasattr(value, "id"):
             elif hasattr(value, "id"):
-                return self.__get_id(value.id, False)
+                return self.__get_id(t.cast(t.Any, value).id, False)
             elif hasattr(value, "__getitem__") and "id" in value:
             elif hasattr(value, "__getitem__") and "id" in value:
-                return self.__get_id(value.get("id"), False)
+                return self.__get_id(t.cast(dict, value).get("id"), False)
         if value is not None and type(value).__name__ not in self.__warning_by_type:
         if value is not None and type(value).__name__ not in self.__warning_by_type:
             _warn(f"LoV id must be a string, using a string representation of {type(value)}.")
             _warn(f"LoV id must be a string, using a string representation of {type(value)}.")
             self.__warning_by_type.add(type(value).__name__)
             self.__warning_by_type.add(type(value).__name__)
@@ -183,9 +183,9 @@ class _Adapter:
             if isinstance(value, (list, tuple)) and len(value) > 1:
             if isinstance(value, (list, tuple)) and len(value) > 1:
                 return self.__get_label(value[1], False)
                 return self.__get_label(value[1], False)
             elif hasattr(value, "label"):
             elif hasattr(value, "label"):
-                return self.__get_label(value.label, False)
+                return self.__get_label(t.cast(t.Any, value).label, False)
             elif hasattr(value, "__getitem__") and "label" in value:
             elif hasattr(value, "__getitem__") and "label" in value:
-                return self.__get_label(value["label"], False)
+                return self.__get_label(t.cast(dict, value).get("label"), False)
         return None
         return None
 
 
     def __get_children(self, value: t.Any) -> t.Optional[t.List[t.Any]]:
     def __get_children(self, value: t.Any) -> t.Optional[t.List[t.Any]]:
@@ -193,18 +193,18 @@ class _Adapter:
             return value[2] if isinstance(value[2], list) else None if value[2] is None else [value[2]]
             return value[2] if isinstance(value[2], list) else None if value[2] is None else [value[2]]
         elif hasattr(value, "children"):
         elif hasattr(value, "children"):
             return (
             return (
-                value.children
-                if isinstance(value.children, list)
+                t.cast(t.Any, value).children
+                if isinstance(t.cast(t.Any, value).children, list)
                 else None
                 else None
-                if value.children is None
-                else [value.children]
+                if t.cast(t.Any, value).children is None
+                else [t.cast(t.Any, value).children]
             )
             )
         elif hasattr(value, "__getitem__") and "children" in value:
         elif hasattr(value, "__getitem__") and "children" in value:
             return (
             return (
-                value["children"]
-                if isinstance(value["children"], list)
+                t.cast(dict, value).get("children")
+                if isinstance(t.cast(dict, value).get("children"), list)
                 else None
                 else None
-                if value["children"] is None
-                else [value["children"]]
+                if t.cast(dict, value).get("children") is None
+                else [t.cast(dict, value).get("children")]
             )
             )
         return None
         return None

+ 1 - 1
taipy/gui/utils/_bindings.py

@@ -40,7 +40,7 @@ class _Bindings:
     def __get_property(self, name):
     def __get_property(self, name):
         def __setter(ud: _Bindings, value: t.Any):
         def __setter(ud: _Bindings, value: t.Any):
             if isinstance(value, _MapDict):
             if isinstance(value, _MapDict):
-                value._update_var = None
+                value._update_var = None  # type: ignore[assignment]
             elif isinstance(value, dict):
             elif isinstance(value, dict):
                 value = _MapDict(value, None)
                 value = _MapDict(value, None)
             ud.__gui._update_var(name, value)
             ud.__gui._update_var(name, value)

+ 1 - 1
taipy/gui/utils/_evaluator.py

@@ -242,7 +242,7 @@ class _Evaluator:
             ctx.update(self.__global_ctx)
             ctx.update(self.__global_ctx)
             # entries in var_val are not always seen (NameError) when passed as locals
             # entries in var_val are not always seen (NameError) when passed as locals
             ctx.update(var_val)
             ctx.update(var_val)
-            with gui._get_autorization():
+            with gui._get_authorization():
                 expr_evaluated = eval(not_encoded_expr if is_edge_case else expr_string, ctx)
                 expr_evaluated = eval(not_encoded_expr if is_edge_case else expr_string, ctx)
         except Exception as e:
         except Exception as e:
             _warn(f"Cannot evaluate expression '{not_encoded_expr if is_edge_case else expr_string}'", e)
             _warn(f"Cannot evaluate expression '{not_encoded_expr if is_edge_case else expr_string}'", e)

+ 4 - 4
taipy/gui/utils/_map_dict.py

@@ -22,16 +22,16 @@ class _MapDict(object):
 
 
     __local_vars = ("_dict", "_update_var")
     __local_vars = ("_dict", "_update_var")
 
 
-    def __init__(self, dict_import: dict, app_update_var=None):
+    def __init__(self, dict_import: dict, app_update_var: t.Optional[t.Callable]=None):
         self._dict = dict_import
         self._dict = dict_import
         # Bind app update var function
         # Bind app update var function
-        self._update_var = app_update_var
+        self._update_var = t.cast(t.Callable, app_update_var)
 
 
     def __len__(self):
     def __len__(self):
         return self._dict.__len__()
         return self._dict.__len__()
 
 
     def __length_hint__(self):
     def __length_hint__(self):
-        return self._dict.__length_hint__()
+        return self._dict.__length_hint__() # type: ignore[reportAttributeAccessIssue]
 
 
     def __getitem__(self, key):
     def __getitem__(self, key):
         value = self._dict.__getitem__(key)
         value = self._dict.__getitem__(key)
@@ -52,7 +52,7 @@ class _MapDict(object):
         self._dict.__delitem__(key)
         self._dict.__delitem__(key)
 
 
     def __missing__(self, key):
     def __missing__(self, key):
-        return self._dict.__missing__(key)
+        return self._dict.__missing__(key) # type: ignore[reportAttributeAccessIssue]
 
 
     def __iter__(self):
     def __iter__(self):
         return self._dict.__iter__()
         return self._dict.__iter__()

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

@@ -22,8 +22,8 @@ class _RuntimeManager(object, metaclass=_Singleton):
         self.__port_gui: t.Dict[int, "Gui"] = {}
         self.__port_gui: t.Dict[int, "Gui"] = {}
 
 
     def add_gui(self, gui: "Gui", port: int):
     def add_gui(self, gui: "Gui", port: int):
-        if port in self.__port_gui:
-            self.__port_gui[port].stop()
+        if gui_port := self.__port_gui.get(port):
+            gui_port.stop()
         self.__port_gui[port] = gui
         self.__port_gui[port] = gui
 
 
     def get_used_port(self):
     def get_used_port(self):

+ 4 - 4
taipy/gui/utils/_variable_directory.py

@@ -36,7 +36,7 @@ class _VariableDirectory:
         module_name = _get_module_name_from_frame(frame)
         module_name = _get_module_name_from_frame(frame)
         if module_name not in self._imported_var_dir:
         if module_name not in self._imported_var_dir:
             imported_var_list = _get_imported_var(frame)
             imported_var_list = _get_imported_var(frame)
-            self._imported_var_dir[module_name] = imported_var_list
+            self._imported_var_dir[t.cast(str, module_name)] = imported_var_list
 
 
     def pre_process_module_import_all(self) -> None:
     def pre_process_module_import_all(self) -> None:
         for imported_dir in self._imported_var_dir.values():
         for imported_dir in self._imported_var_dir.values():
@@ -54,7 +54,7 @@ class _VariableDirectory:
 
 
     def process_imported_var(self) -> None:
     def process_imported_var(self) -> None:
         self.pre_process_module_import_all()
         self.pre_process_module_import_all()
-        default_imported_dir = self._imported_var_dir[self._default_module]
+        default_imported_dir = self._imported_var_dir[t.cast(str, self._default_module)]
         with self._locals_context.set_locals_context(self._default_module):
         with self._locals_context.set_locals_context(self._default_module):
             for name, asname, module in default_imported_dir:
             for name, asname, module in default_imported_dir:
                 if name == "*" and asname == "*":
                 if name == "*" and asname == "*":
@@ -85,7 +85,7 @@ class _VariableDirectory:
 
 
     def add_var(self, name: str, module: t.Optional[str], var_name: t.Optional[str] = None) -> str:
     def add_var(self, name: str, module: t.Optional[str], var_name: t.Optional[str] = None) -> str:
         if module is None:
         if module is None:
-            module = self._default_module
+            module = t.cast(str, self._default_module)
         if gv := self.get_var(name, module):
         if gv := self.get_var(name, module):
             return gv
             return gv
         var_encode = _variable_encode(name, module) if module != self._default_module else name
         var_encode = _variable_encode(name, module) if module != self._default_module else name
@@ -95,7 +95,7 @@ class _VariableDirectory:
         if var_encode != var_name:
         if var_encode != var_name:
             var_name_decode, module_decode = _variable_decode(var_name)
             var_name_decode, module_decode = _variable_decode(var_name)
             if module_decode is None:
             if module_decode is None:
-                module_decode = self._default_module
+                module_decode = t.cast(str, self._default_module)
             self.__add_var_head(var_name_decode, module_decode, var_encode)
             self.__add_var_head(var_name_decode, module_decode, var_encode)
         if name not in self._var_dir:
         if name not in self._var_dir:
             self._var_dir[name] = {module: var_name}
             self._var_dir[name] = {module: var_name}

+ 4 - 1
taipy/gui/utils/chart_config_builder.py

@@ -207,7 +207,10 @@ def _build_chart_config(gui: "Gui", attributes: t.Dict[str, t.Any], col_types: t
         decimators.append(None)
         decimators.append(None)
 
 
     # set default columns if not defined
     # set default columns if not defined
-    icols = [[c2 for c2 in [__get_col_from_indexed(c1, i) for c1 in col_dict.keys()] if c2] for i in range(len(traces))]
+    icols = [
+        [c2 for c2 in [__get_col_from_indexed(c1, i) for c1 in t.cast(dict, col_dict).keys()] if c2]
+        for i in range(len(traces))
+    ]
 
 
     for i, tr in enumerate(traces):
     for i, tr in enumerate(traces):
         if i < len(axis):
         if i < len(axis):

+ 1 - 1
taipy/gui/utils/datatype.py

@@ -22,4 +22,4 @@ def _get_data_type(value):
             return "int"
             return "int"
         elif pd.api.types.is_float_dtype(value):
         elif pd.api.types.is_float_dtype(value):
             return "float"
             return "float"
-    return re.match(r"^<class '(.*\.)?(.*?)(\d\d)?'>", str(type(value))).group(2)
+    return re.match(r"^<class '(.*\.)?(.*?)(\d\d)?'>", str(type(value))).group(2) # type: ignore[reportOptionalMemberAccess]

+ 1 - 1
taipy/gui/utils/isnotebook.py

@@ -17,7 +17,7 @@ def _is_in_notebook():  # pragma: no cover
         if not util.find_spec("IPython"):
         if not util.find_spec("IPython"):
             return False
             return False
 
 
-        from IPython import get_ipython
+        from IPython import get_ipython  # type: ignore[reportPrivateImportUsage]
 
 
         ipython = get_ipython()
         ipython = get_ipython()
 
 

+ 1 - 1
taipy/gui_core/__init__.py

@@ -14,5 +14,5 @@
 This package provides classes that can be used in GUI controls dedicated to scenario management.
 This package provides classes that can be used in GUI controls dedicated to scenario management.
 """
 """
 
 
-from ._adapters import CustomScenarioFilter, DataNodeFilter, DataNodeScenarioFilter, ScenarioFilter
 from ._init import *
 from ._init import *
+from .filters import CustomScenarioFilter, DataNodeFilter, DataNodeScenarioFilter, ScenarioFilter

+ 26 - 87
taipy/gui_core/_adapters.py

@@ -43,11 +43,13 @@ from taipy.gui._warnings import _warn
 from taipy.gui.gui import _DoNotUpdate
 from taipy.gui.gui import _DoNotUpdate
 from taipy.gui.utils import _is_boolean, _is_true, _TaipyBase
 from taipy.gui.utils import _is_boolean, _is_true, _TaipyBase
 
 
+from .filters import DataNodeFilter, ScenarioFilter, _Filter
+
 
 
 # prevent gui from trying to push scenario instances to the front-end
 # prevent gui from trying to push scenario instances to the front-end
 class _GuiCoreDoNotUpdate(_DoNotUpdate):
 class _GuiCoreDoNotUpdate(_DoNotUpdate):
     def __repr__(self):
     def __repr__(self):
-        return self.get_label() if hasattr(self, "get_label") else super().__repr__()
+        return self.get_label() if hasattr(self, "get_label") else super().__repr__()  # type: ignore[reportAttributeAccessIssue]
 
 
 
 
 class _EntityType(Enum):
 class _EntityType(Enum):
@@ -353,18 +355,18 @@ def _get_entity_property(col: str, *types: t.Type):
         # we compare only strings
         # we compare only strings
         if isinstance(entity, types):
         if isinstance(entity, types):
             if isinstance(entity, Cycle):
             if isinstance(entity, Cycle):
-                lcol = "creation_date"
-                lfn = None
+                the_col = "creation_date"
+                the_fn = None
             else:
             else:
-                lcol = col
-                lfn = col_fn
+                the_col = col
+                the_fn = col_fn
             try:
             try:
-                val = attrgetter(lfn or lcol)(entity)
-                if lfn:
+                val = attrgetter(the_fn or the_col)(entity)
+                if the_fn:
                     val = val()
                     val = val()
             except AttributeError as e:
             except AttributeError as e:
                 if _is_debugging():
                 if _is_debugging():
-                    _warn("Attribute", e)
+                    _warn(f"sort_key({entity.id}):", e)
                 val = ""
                 val = ""
         else:
         else:
             val = ""
             val = ""
@@ -373,76 +375,17 @@ def _get_entity_property(col: str, *types: t.Type):
     return sort_key
     return sort_key
 
 
 
 
-@dataclass
-class _Filter(_DoNotUpdate):
-    label: str
-    property_type: t.Optional[t.Type]
-
-    def get_property(self):
-        return self.label
-
-    def get_type(self):
-        if self.property_type is bool:
-            return "boolean"
-        elif self.property_type is int or self.property_type is float:
-            return "number"
-        elif self.property_type is datetime or self.property_type is date:
-            return "date"
-        elif self.property_type is str:
-            return "str"
-        return "any"
-
-
-@dataclass
-class ScenarioFilter(_Filter):
-    property_id: str
-
-    def get_property(self):
-        return self.property_id
-
-
-@dataclass
-class DataNodeScenarioFilter(_Filter):
-    datanode_config_id: str
-    property_id: str
-
-    def get_property(self):
-        return f"{self.datanode_config_id}.{self.property_id}"
-
-
-_CUSTOM_PREFIX = "fn:"
-
-
-@dataclass
-class CustomScenarioFilter(_Filter):
-    filter_function: t.Callable[[Scenario], t.Any]
-
-    def __post_init__(self):
-        if self.filter_function.__name__ == "<lambda>":
-            raise TypeError("ScenarioCustomFilter does not support lambda functions.")
-        mod = self.filter_function.__module__
-        self.module = mod if isinstance(mod, str) else mod.__name__
-
-    def get_property(self):
-        return f"{_CUSTOM_PREFIX}{self.module}:{self.filter_function.__name__}"
-
-    @staticmethod
-    def _get_custom(col: str) -> t.Optional[t.List[str]]:
-        return col[len(_CUSTOM_PREFIX) :].split(":") if col.startswith(_CUSTOM_PREFIX) else None
-
-
-@dataclass
-class DataNodeFilter(_Filter):
-    property_id: str
-
-    def get_property(self):
-        return self.property_id
+@dataclass(frozen=True)
+class _GuiCorePropDesc:
+    filter: _Filter
+    extended: bool = False
+    for_sort: bool = False
 
 
 
 
 class _GuiCoreProperties(ABC):
 class _GuiCoreProperties(ABC):
     @staticmethod
     @staticmethod
     @abstractmethod
     @abstractmethod
-    def get_default_list() -> t.List[_Filter]:
+    def get_default_list() -> t.List[_GuiCorePropDesc]:
         raise NotImplementedError
         raise NotImplementedError
 
 
     @staticmethod
     @staticmethod
@@ -454,7 +397,7 @@ class _GuiCoreProperties(ABC):
         return {}
         return {}
 
 
     def get(self):
     def get(self):
-        data = super().get()
+        data = super().get()  # type: ignore[reportAttributeAccessIssue]
         if _is_boolean(data):
         if _is_boolean(data):
             if _is_true(data):
             if _is_true(data):
                 data = self.get_default_list()
                 data = self.get_default_list()
@@ -465,18 +408,21 @@ class _GuiCoreProperties(ABC):
         if isinstance(data, _Filter):
         if isinstance(data, _Filter):
             data = (data,)
             data = (data,)
         if isinstance(data, (list, tuple)):
         if isinstance(data, (list, tuple)):
-            flist: t.List[_Filter] = []  # type: ignore[annotation-unchecked]
+            f_list: t.List[_Filter] = []  # type: ignore[annotation-unchecked]
             for f in data:
             for f in data:
                 if isinstance(f, str):
                 if isinstance(f, str):
                     f = f.strip()
                     f = f.strip()
                     if f == "*":
                     if f == "*":
-                        flist.extend(p.filter for p in self.get_default_list())
+                        f_list.extend(p.filter for p in self.get_default_list())
                     elif f:
                     elif f:
-                        flist.append(
-                            next((p.filter for p in self.get_default_list() if p.get_property() == f), _Filter(f))
+                        f_list.append(
+                            next(
+                                (p.filter for p in self.get_default_list() if p.filter.get_property() == f),
+                                _Filter(f, None),
+                            )
                         )
                         )
                 elif isinstance(f, _Filter):
                 elif isinstance(f, _Filter):
-                    flist.append(f)
+                    f_list.append(f)
             return json.dumps(
             return json.dumps(
                 [
                 [
                     (
                     (
@@ -487,19 +433,12 @@ class _GuiCoreProperties(ABC):
                     )
                     )
                     if self.full_desc()
                     if self.full_desc()
                     else (attr.label, attr.get_property())
                     else (attr.label, attr.get_property())
-                    for attr in flist
+                    for attr in f_list
                 ]
                 ]
             )
             )
         return None
         return None
 
 
 
 
-@dataclass(frozen=True)
-class _GuiCorePropDesc:
-    filter: _Filter
-    extended: bool = False
-    for_sort: bool = False
-
-
 class _GuiCoreScenarioProperties(_GuiCoreProperties):
 class _GuiCoreScenarioProperties(_GuiCoreProperties):
     _SC_PROPS: t.List[_GuiCorePropDesc] = [
     _SC_PROPS: t.List[_GuiCorePropDesc] = [
         _GuiCorePropDesc(ScenarioFilter("Config id", str, "config_id"), for_sort=True),
         _GuiCorePropDesc(ScenarioFilter("Config id", str, "config_id"), for_sort=True),

+ 71 - 64
taipy/gui_core/_context.py

@@ -67,13 +67,13 @@ from taipy.gui.gui import _DoNotUpdate
 from taipy.gui.utils._map_dict import _MapDict
 from taipy.gui.utils._map_dict import _MapDict
 
 
 from ._adapters import (
 from ._adapters import (
-    CustomScenarioFilter,
     _EntityType,
     _EntityType,
     _get_entity_property,
     _get_entity_property,
     _GuiCoreDatanodeAdapter,
     _GuiCoreDatanodeAdapter,
     _GuiCoreScenarioProperties,
     _GuiCoreScenarioProperties,
     _invoke_action,
     _invoke_action,
 )
 )
+from .filters import CustomScenarioFilter
 
 
 
 
 class _GuiCoreContext(CoreEventConsumerBase):
 class _GuiCoreContext(CoreEventConsumerBase):
@@ -113,20 +113,20 @@ class _GuiCoreContext(CoreEventConsumerBase):
 
 
     def process_event(self, event: Event):
     def process_event(self, event: Event):
         self.__lazy_start()
         self.__lazy_start()
-        if event.entity_type == EventEntityType.SCENARIO:
-            with self.gui._get_autorization(system=True):
+        if event.entity_type is EventEntityType.SCENARIO:
+            with self.gui._get_authorization(system=True):
                 self.scenario_refresh(
                 self.scenario_refresh(
                     event.entity_id
                     event.entity_id
-                    if event.operation == EventOperation.DELETION or is_readable(t.cast(ScenarioId, event.entity_id))
+                    if event.operation is EventOperation.DELETION or is_readable(t.cast(ScenarioId, event.entity_id))
                     else None
                     else None
                 )
                 )
-        elif event.entity_type == EventEntityType.SEQUENCE and event.entity_id:
+        elif event.entity_type is EventEntityType.SEQUENCE and event.entity_id:
             sequence = None
             sequence = None
             try:
             try:
-                with self.gui._get_autorization(system=True):
+                with self.gui._get_authorization(system=True):
                     sequence = (
                     sequence = (
                         core_get(event.entity_id)
                         core_get(event.entity_id)
-                        if event.operation != EventOperation.DELETION
+                        if event.operation is not EventOperation.DELETION
                         and is_readable(t.cast(SequenceId, event.entity_id))
                         and is_readable(t.cast(SequenceId, event.entity_id))
                         else None
                         else None
                     )
                     )
@@ -134,13 +134,15 @@ class _GuiCoreContext(CoreEventConsumerBase):
                         self.broadcast_core_changed({"scenario": list(sequence.parent_ids)})  # type: ignore
                         self.broadcast_core_changed({"scenario": list(sequence.parent_ids)})  # type: ignore
             except Exception as e:
             except Exception as e:
                 _warn(f"Access to sequence {event.entity_id} failed", e)
                 _warn(f"Access to sequence {event.entity_id} failed", e)
-        elif event.entity_type == EventEntityType.JOB:
+        elif event.entity_type is EventEntityType.JOB:
             with self.lock:
             with self.lock:
                 self.jobs_list = None
                 self.jobs_list = None
-            self.broadcast_core_changed({"jobs": event.entity_id})
-        elif event.entity_type == EventEntityType.SUBMISSION:
+            # no broadcast because the submission status will do the job
+            if event.operation is EventOperation.DELETION:
+                self.broadcast_core_changed({"jobs": True})
+        elif event.entity_type is EventEntityType.SUBMISSION:
             self.submission_status_callback(event.entity_id, event)
             self.submission_status_callback(event.entity_id, event)
-        elif event.entity_type == EventEntityType.DATA_NODE:
+        elif event.entity_type is EventEntityType.DATA_NODE:
             with self.lock:
             with self.lock:
                 self.data_nodes_by_owner = None
                 self.data_nodes_by_owner = None
             self.broadcast_core_changed(
             self.broadcast_core_changed(
@@ -178,9 +180,9 @@ class _GuiCoreContext(CoreEventConsumerBase):
             client_id = submission.properties.get("client_id")
             client_id = submission.properties.get("client_id")
             if client_id:
             if client_id:
                 running_tasks = {}
                 running_tasks = {}
-                with self.gui._get_autorization(client_id):
+                with self.gui._get_authorization(client_id):
                     for job in submission.jobs:
                     for job in submission.jobs:
-                        job = job if isinstance(job, Job) else core_get(job)
+                        job = job if isinstance(job, Job) else t.cast(Job, core_get(job))
                         running_tasks[job.task.id] = (
                         running_tasks[job.task.id] = (
                             SubmissionStatus.RUNNING.value
                             SubmissionStatus.RUNNING.value
                             if job.is_running()
                             if job.is_running()
@@ -190,7 +192,7 @@ class _GuiCoreContext(CoreEventConsumerBase):
                         )
                         )
                     payload.update(tasks=running_tasks)
                     payload.update(tasks=running_tasks)
 
 
-                    if last_status != new_status:
+                    if last_status is not new_status:
                         # callback
                         # callback
                         submission_name = submission.properties.get("on_submission")
                         submission_name = submission.properties.get("on_submission")
                         if submission_name:
                         if submission_name:
@@ -308,7 +310,7 @@ class _GuiCoreContext(CoreEventConsumerBase):
 
 
     def get_filtered_scenario_list(
     def get_filtered_scenario_list(
         self,
         self,
-        entities: t.List[t.Union[t.List, Scenario]],
+        entities: t.List[t.Union[t.List, Scenario, None]],
         filters: t.Optional[t.List[t.Dict[str, t.Any]]],
         filters: t.Optional[t.List[t.Dict[str, t.Any]]],
     ):
     ):
         if not filters:
         if not filters:
@@ -340,13 +342,15 @@ class _GuiCoreContext(CoreEventConsumerBase):
                 e
                 e
                 for e in filtered_list
                 for e in filtered_list
                 if not isinstance(e, Scenario)
                 if not isinstance(e, Scenario)
-                or _invoke_action(e, col, col_type, is_datanode_prop, action, val, col_fn)
+                or _invoke_action(e, t.cast(str, col), col_type, is_datanode_prop, action, val, col_fn)
             ]
             ]
             # level 2 filtering
             # level 2 filtering
             filtered_list = [
             filtered_list = [
                 e
                 e
                 if isinstance(e, Scenario)
                 if isinstance(e, Scenario)
-                else self.filter_entities(e, col, col_type, is_datanode_prop, action, val, col_fn)
+                else self.filter_entities(
+                    t.cast(list, e), t.cast(str, col), col_type, is_datanode_prop, action, val, col_fn
+                )
                 for e in filtered_list
                 for e in filtered_list
             ]
             ]
         # remove empty cycles
         # remove empty cycles
@@ -414,17 +418,17 @@ class _GuiCoreContext(CoreEventConsumerBase):
             or not isinstance(args[start_idx + 2], dict)
             or not isinstance(args[start_idx + 2], dict)
         ):
         ):
             return
             return
-        error_var = payload.get("error_id")
+        error_var = t.cast(str, payload.get("error_id"))
         update = args[start_idx]
         update = args[start_idx]
         delete = args[start_idx + 1]
         delete = args[start_idx + 1]
-        data = args[start_idx + 2]
+        data = t.cast(dict, args[start_idx + 2])
         with_dialog = True if len(args) < start_idx + 4 else bool(args[start_idx + 3])
         with_dialog = True if len(args) < start_idx + 4 else bool(args[start_idx + 3])
         scenario = None
         scenario = None
         user_scenario = None
         user_scenario = None
 
 
         name = data.get(_GuiCoreContext.__PROP_ENTITY_NAME)
         name = data.get(_GuiCoreContext.__PROP_ENTITY_NAME)
         if update:
         if update:
-            scenario_id = data.get(_GuiCoreContext.__PROP_ENTITY_ID)
+            scenario_id = t.cast(ScenarioId, data.get(_GuiCoreContext.__PROP_ENTITY_ID))
             if delete:
             if delete:
                 if not (reason := is_deletable(scenario_id)):
                 if not (reason := is_deletable(scenario_id)):
                     state.assign(error_var, f"Scenario. {scenario_id} is not deletable: {_get_reason(reason)}.")
                     state.assign(error_var, f"Scenario. {scenario_id} is not deletable: {_get_reason(reason)}.")
@@ -454,11 +458,11 @@ class _GuiCoreContext(CoreEventConsumerBase):
                 scenario_config = None
                 scenario_config = None
                 date = None
                 date = None
             scenario_id = None
             scenario_id = None
+            gui = state.get_gui()
             try:
             try:
-                gui = state.get_gui()
                 on_creation = args[0] if isinstance(args[0], str) else None
                 on_creation = args[0] if isinstance(args[0], str) else None
                 on_creation_function = gui._get_user_function(on_creation) if on_creation else None
                 on_creation_function = gui._get_user_function(on_creation) if on_creation else None
-                if callable(on_creation_function):
+                if callable(on_creation_function) and on_creation:
                     try:
                     try:
                         res = gui._call_function_with_state(
                         res = gui._call_function_with_state(
                             on_creation_function,
                             on_creation_function,
@@ -503,9 +507,8 @@ class _GuiCoreContext(CoreEventConsumerBase):
                             + f"({len(Config.scenarios) - 1}) found.",
                             + f"({len(Config.scenarios) - 1}) found.",
                         )
                         )
                         return
                         return
-
-                scenario = create_scenario(scenario_config, date, name)
-                scenario_id = scenario.id
+                scenario = create_scenario(scenario_config, date, name) if scenario_config else None
+                scenario_id = scenario.id if scenario else None
             except Exception as e:
             except Exception as e:
                 state.assign(error_var, f"Error creating Scenario. {e}")
                 state.assign(error_var, f"Error creating Scenario. {e}")
             finally:
             finally:
@@ -547,17 +550,17 @@ class _GuiCoreContext(CoreEventConsumerBase):
         if args is None or not isinstance(args, list) or len(args) < 1 or not isinstance(args[0], dict):
         if args is None or not isinstance(args, list) or len(args) < 1 or not isinstance(args[0], dict):
             return
             return
         error_var = payload.get("error_id")
         error_var = payload.get("error_id")
-        data = args[0]
-        entity_id = data.get(_GuiCoreContext.__PROP_ENTITY_ID)
+        data = t.cast(dict, args[0])
+        entity_id = t.cast(str, data.get(_GuiCoreContext.__PROP_ENTITY_ID))
         sequence = data.get("sequence")
         sequence = data.get("sequence")
         if not self.__check_readable_editable(state, entity_id, "Scenario", error_var):
         if not self.__check_readable_editable(state, entity_id, "Scenario", error_var):
             return
             return
-        scenario: Scenario = core_get(entity_id)
+        scenario = t.cast(Scenario, core_get(entity_id))
         if scenario:
         if scenario:
             try:
             try:
                 if not sequence:
                 if not sequence:
                     if isinstance(sequence, str) and (name := data.get(_GuiCoreContext.__PROP_ENTITY_NAME)):
                     if isinstance(sequence, str) and (name := data.get(_GuiCoreContext.__PROP_ENTITY_NAME)):
-                        scenario.add_sequence(name, data.get("task_ids"))
+                        scenario.add_sequence(name, t.cast(list, data.get("task_ids")))
                     else:
                     else:
                         primary = data.get(_GuiCoreContext.__PROP_SCENARIO_PRIMARY)
                         primary = data.get(_GuiCoreContext.__PROP_SCENARIO_PRIMARY)
                         if primary is True:
                         if primary is True:
@@ -572,11 +575,11 @@ class _GuiCoreContext(CoreEventConsumerBase):
                     if data.get("del", False):
                     if data.get("del", False):
                         scenario.remove_sequence(sequence)
                         scenario.remove_sequence(sequence)
                     else:
                     else:
-                        name = data.get(_GuiCoreContext.__PROP_ENTITY_NAME)
+                        name = t.cast(str, data.get(_GuiCoreContext.__PROP_ENTITY_NAME))
                         if sequence != name:
                         if sequence != name:
                             scenario.rename_sequence(sequence, name)
                             scenario.rename_sequence(sequence, name)
                         if seqEntity := scenario.sequences.get(name):
                         if seqEntity := scenario.sequences.get(name):
-                            seqEntity.tasks = data.get("task_ids")
+                            seqEntity.tasks = t.cast(list, data.get("task_ids"))
                             self.__edit_properties(seqEntity, data)
                             self.__edit_properties(seqEntity, data)
                         else:
                         else:
                             _GuiCoreContext.__assign_var(
                             _GuiCoreContext.__assign_var(
@@ -595,13 +598,13 @@ class _GuiCoreContext(CoreEventConsumerBase):
         args = payload.get("args")
         args = payload.get("args")
         if args is None or not isinstance(args, list) or len(args) < 1 or not isinstance(args[0], dict):
         if args is None or not isinstance(args, list) or len(args) < 1 or not isinstance(args[0], dict):
             return
             return
-        data = args[0]
+        data = t.cast(dict, args[0])
         error_var = payload.get("error_id")
         error_var = payload.get("error_id")
         try:
         try:
-            scenario_id = data.get(_GuiCoreContext.__PROP_ENTITY_ID)
-            entity = core_get(scenario_id)
-            if sequence := data.get("sequence"):
-                entity = entity.sequences.get(sequence)
+            scenario_id = t.cast(str, data.get(_GuiCoreContext.__PROP_ENTITY_ID))
+            entity = t.cast(Scenario, core_get(scenario_id))
+            if sequence := t.cast(str, data.get("sequence")):
+                entity = t.cast(Sequence, entity.sequences.get(sequence))
 
 
             if not (reason := is_submittable(entity)):
             if not (reason := is_submittable(entity)):
                 _GuiCoreContext.__assign_var(
                 _GuiCoreContext.__assign_var(
@@ -631,7 +634,7 @@ class _GuiCoreContext(CoreEventConsumerBase):
 
 
     def get_filtered_datanode_list(
     def get_filtered_datanode_list(
         self,
         self,
-        entities: t.List[t.Union[t.List, DataNode]],
+        entities: t.List[t.Union[t.List, DataNode, None]],
         filters: t.Optional[t.List[t.Dict[str, t.Any]]],
         filters: t.Optional[t.List[t.Dict[str, t.Any]]],
     ):
     ):
         if not filters or not entities:
         if not filters or not entities:
@@ -660,13 +663,16 @@ class _GuiCoreContext(CoreEventConsumerBase):
             filtered_list = [
             filtered_list = [
                 e
                 e
                 for e in filtered_list
                 for e in filtered_list
-                if not isinstance(e, DataNode) or _invoke_action(e, col, col_type, False, action, val, col_fn)
+                if not isinstance(e, DataNode)
+                or _invoke_action(e, t.cast(str, col), col_type, False, action, val, col_fn)
             ]
             ]
             # level 3 filtering
             # level 3 filtering
             filtered_list = [
             filtered_list = [
-                e if isinstance(e, DataNode) else self.filter_entities(d, col, col_type, False, action, val, col_fn)
+                e
+                if isinstance(e, DataNode)
+                else self.filter_entities(d, t.cast(str, col), col_type, False, action, val, col_fn)
                 for e in filtered_list
                 for e in filtered_list
-                for d in e[2]
+                for d in t.cast(list, t.cast(list, e)[2])
             ]
             ]
         # remove empty cycles
         # remove empty cycles
         return [e for e in filtered_list if isinstance(e, DataNode) or (isinstance(e, (tuple, list)) and len(e[2]))]
         return [e for e in filtered_list if isinstance(e, DataNode) or (isinstance(e, (tuple, list)) and len(e[2]))]
@@ -724,8 +730,8 @@ class _GuiCoreContext(CoreEventConsumerBase):
                         base_list = []
                         base_list = []
         else:
         else:
             base_list = datanodes
             base_list = datanodes
-        adapted_list = self.get_sorted_datanode_list(base_list, sorts)
-        return self.get_filtered_datanode_list(adapted_list, filters)
+        adapted_list = self.get_sorted_datanode_list(t.cast(list, base_list), sorts)
+        return self.get_filtered_datanode_list(t.cast(list, adapted_list), filters)
 
 
     def data_node_adapter(
     def data_node_adapter(
         self,
         self,
@@ -737,8 +743,8 @@ class _GuiCoreContext(CoreEventConsumerBase):
         if isinstance(data, tuple):
         if isinstance(data, tuple):
             raise NotImplementedError
             raise NotImplementedError
         if isinstance(data, list):
         if isinstance(data, list):
-            if data[2] and isinstance(data[2][0], (Cycle, Scenario, Sequence, DataNode)):
-                data[2] = self.get_sorted_datanode_list(data[2], sorts, False)
+            if data[2] and isinstance(t.cast(list, data[2])[0], (Cycle, Scenario, Sequence, DataNode)):
+                data[2] = self.get_sorted_datanode_list(t.cast(list, data[2]), sorts, False)
             return data
             return data
         try:
         try:
             if hasattr(data, "id") and is_readable(data.id) and core_get(data.id) is not None:
             if hasattr(data, "id") and is_readable(data.id) and core_get(data.id) is not None:
@@ -770,7 +776,7 @@ class _GuiCoreContext(CoreEventConsumerBase):
                             data.id,
                             data.id,
                             data.get_simple_label(),
                             data.get_simple_label(),
                             self.get_sorted_datanode_list(
                             self.get_sorted_datanode_list(
-                                self.data_nodes_by_owner.get(data.id, []) + list(data.sequences.values()),
+                                t.cast(list, self.data_nodes_by_owner.get(data.id, []) + list(data.sequences.values())),
                                 sorts,
                                 sorts,
                                 False,
                                 False,
                             ),
                             ),
@@ -828,7 +834,7 @@ class _GuiCoreContext(CoreEventConsumerBase):
         args = payload.get("args")
         args = payload.get("args")
         if args is None or not isinstance(args, list) or len(args) < 1 or not isinstance(args[0], dict):
         if args is None or not isinstance(args, list) or len(args) < 1 or not isinstance(args[0], dict):
             return
             return
-        data = args[0]
+        data = t.cast(dict, args[0])
         job_ids = data.get(_GuiCoreContext.__PROP_ENTITY_ID)
         job_ids = data.get(_GuiCoreContext.__PROP_ENTITY_ID)
         job_action = data.get(_GuiCoreContext.__ACTION)
         job_action = data.get(_GuiCoreContext.__ACTION)
         if job_action and isinstance(job_ids, list):
         if job_action and isinstance(job_ids, list):
@@ -879,7 +885,7 @@ class _GuiCoreContext(CoreEventConsumerBase):
                         [] if job.stacktrace is None else job.stacktrace,
                         [] if job.stacktrace is None else job.stacktrace,
                     )
                     )
         except Exception as e:
         except Exception as e:
-            _warn(f"Access to job ({job.id if hasattr(job, 'id') else 'No_id'}) failed", e)
+            _warn(f"Access to job ({job_id}) failed", e)
         return None
         return None
 
 
     def edit_data_node(self, state: State, id: str, payload: t.Dict[str, str]):
     def edit_data_node(self, state: State, id: str, payload: t.Dict[str, str]):
@@ -888,11 +894,11 @@ class _GuiCoreContext(CoreEventConsumerBase):
         if args is None or not isinstance(args, list) or len(args) < 1 or not isinstance(args[0], dict):
         if args is None or not isinstance(args, list) or len(args) < 1 or not isinstance(args[0], dict):
             return
             return
         error_var = payload.get("error_id")
         error_var = payload.get("error_id")
-        data = args[0]
-        entity_id = data.get(_GuiCoreContext.__PROP_ENTITY_ID)
+        data = t.cast(dict, args[0])
+        entity_id = t.cast(str, data.get(_GuiCoreContext.__PROP_ENTITY_ID))
         if not self.__check_readable_editable(state, entity_id, "DataNode", error_var):
         if not self.__check_readable_editable(state, entity_id, "DataNode", error_var):
             return
             return
-        entity: DataNode = core_get(entity_id)
+        entity = t.cast(DataNode, core_get(entity_id))
         if isinstance(entity, DataNode):
         if isinstance(entity, DataNode):
             try:
             try:
                 self.__edit_properties(entity, data)
                 self.__edit_properties(entity, data)
@@ -905,13 +911,13 @@ class _GuiCoreContext(CoreEventConsumerBase):
         args = payload.get("args")
         args = payload.get("args")
         if args is None or not isinstance(args, list) or len(args) < 1 or not isinstance(args[0], dict):
         if args is None or not isinstance(args, list) or len(args) < 1 or not isinstance(args[0], dict):
             return
             return
-        data = args[0]
+        data = t.cast(dict, args[0])
         error_var = payload.get("error_id")
         error_var = payload.get("error_id")
-        entity_id = data.get(_GuiCoreContext.__PROP_ENTITY_ID)
+        entity_id = t.cast(str, data.get(_GuiCoreContext.__PROP_ENTITY_ID))
         if not self.__check_readable_editable(state, entity_id, "Data node", error_var):
         if not self.__check_readable_editable(state, entity_id, "Data node", error_var):
             return
             return
         lock = data.get("lock", True)
         lock = data.get("lock", True)
-        entity: DataNode = core_get(entity_id)
+        entity = t.cast(DataNode, core_get(entity_id))
         if isinstance(entity, DataNode):
         if isinstance(entity, DataNode):
             try:
             try:
                 if lock:
                 if lock:
@@ -937,13 +943,13 @@ class _GuiCoreContext(CoreEventConsumerBase):
             props = data.get("properties")
             props = data.get("properties")
             if isinstance(props, (list, tuple)):
             if isinstance(props, (list, tuple)):
                 for prop in props:
                 for prop in props:
-                    key = prop.get("key")
+                    key = t.cast(dict, prop).get("key")
                     if key and key not in _GuiCoreContext.__ENTITY_PROPS:
                     if key and key not in _GuiCoreContext.__ENTITY_PROPS:
-                        ent.properties[key] = prop.get("value")
+                        ent.properties[key] = t.cast(dict, prop).get("value")
             deleted_props = data.get("deleted_properties")
             deleted_props = data.get("deleted_properties")
             if isinstance(deleted_props, (list, tuple)):
             if isinstance(deleted_props, (list, tuple)):
                 for prop in deleted_props:
                 for prop in deleted_props:
-                    key = prop.get("key")
+                    key = t.cast(dict, prop).get("key")
                     if key and key not in _GuiCoreContext.__ENTITY_PROPS:
                     if key and key not in _GuiCoreContext.__ENTITY_PROPS:
                         ent.properties.pop(key, None)
                         ent.properties.pop(key, None)
 
 
@@ -1006,23 +1012,24 @@ class _GuiCoreContext(CoreEventConsumerBase):
         args = payload.get("args")
         args = payload.get("args")
         if args is None or not isinstance(args, list) or len(args) < 1 or not isinstance(args[0], dict):
         if args is None or not isinstance(args, list) or len(args) < 1 or not isinstance(args[0], dict):
             return
             return
-        data = args[0]
+        data = t.cast(dict, args[0])
         error_var = payload.get("error_id")
         error_var = payload.get("error_id")
-        entity_id = data.get(_GuiCoreContext.__PROP_ENTITY_ID)
+        entity_id = t.cast(str, data.get(_GuiCoreContext.__PROP_ENTITY_ID))
         if not self.__check_readable_editable(state, entity_id, "Data node", error_var):
         if not self.__check_readable_editable(state, entity_id, "Data node", error_var):
             return
             return
-        entity: DataNode = core_get(entity_id)
+        entity = t.cast(DataNode, core_get(entity_id))
         if isinstance(entity, DataNode):
         if isinstance(entity, DataNode):
             try:
             try:
+                val = t.cast(str, data.get("value"))
                 entity.write(
                 entity.write(
-                    parser.parse(data.get("value"))
+                    parser.parse(val)
                     if data.get("type") == "date"
                     if data.get("type") == "date"
-                    else int(data.get("value"))
+                    else int(val)
                     if data.get("type") == "int"
                     if data.get("type") == "int"
-                    else float(data.get("value"))
+                    else float(val)
                     if data.get("type") == "float"
                     if data.get("type") == "float"
                     else data.get("value"),
                     else data.get("value"),
-                    comment=data.get(_GuiCoreContext.__PROP_ENTITY_COMMENT),
+                    comment=t.cast(dict, data.get(_GuiCoreContext.__PROP_ENTITY_COMMENT)),
                 )
                 )
                 entity.unlock_edit(self.gui._get_client_id())
                 entity.unlock_edit(self.gui._get_client_id())
                 _GuiCoreContext.__assign_var(state, error_var, "")
                 _GuiCoreContext.__assign_var(state, error_var, "")
@@ -1184,7 +1191,7 @@ class _GuiCoreContext(CoreEventConsumerBase):
             try:
             try:
                 entity = (
                 entity = (
                     core_get(args[0])
                     core_get(args[0])
-                    if (reason := is_readable(args[0]))
+                    if (reason := is_readable(t.cast(ScenarioId, args[0])))
                     else f"{args[0]} is not readable: {_get_reason(reason)}"
                     else f"{args[0]} is not readable: {_get_reason(reason)}"
                 )
                 )
                 self.gui._call_function_with_state(
                 self.gui._call_function_with_state(

+ 99 - 0
taipy/gui_core/filters.py

@@ -0,0 +1,99 @@
+# Copyright 2021-2024 Avaiga Private Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# 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.
+
+import typing as t
+from dataclasses import dataclass
+from datetime import date, datetime
+
+from taipy.core import Scenario
+from taipy.gui.gui import _DoNotUpdate
+
+
+@dataclass
+class _Filter(_DoNotUpdate):
+    label: str
+    property_type: t.Optional[t.Type]
+
+    def get_property(self):
+        return self.label
+
+    def get_type(self):
+        if self.property_type is bool:
+            return "boolean"
+        elif self.property_type is int or self.property_type is float:
+            return "number"
+        elif self.property_type is datetime or self.property_type is date:
+            return "date"
+        elif self.property_type is str:
+            return "str"
+        return "any"
+
+
+@dataclass
+class ScenarioFilter(_Filter):
+    """
+    used to describe a filter on a scenario property
+    """
+
+    property_id: str
+
+    def get_property(self):
+        return self.property_id
+
+
+@dataclass
+class DataNodeScenarioFilter(_Filter):
+    """
+    used to describe a filter on a scenario datanode's property
+    """
+
+    datanode_config_id: str
+    property_id: str
+
+    def get_property(self):
+        return f"{self.datanode_config_id}.{self.property_id}"
+
+
+_CUSTOM_PREFIX = "fn:"
+
+
+@dataclass
+class CustomScenarioFilter(_Filter):
+    """
+    used to describe a custom scenario filter ie based on a user defined function
+    """
+
+    filter_function: t.Callable[[Scenario], t.Any]
+
+    def __post_init__(self):
+        if self.filter_function.__name__ == "<lambda>":
+            raise TypeError("CustomScenarioFilter does not support lambda functions.")
+        mod = self.filter_function.__module__
+        self.module = mod if isinstance(mod, str) else mod.__name__
+
+    def get_property(self):
+        return f"{_CUSTOM_PREFIX}{self.module}:{self.filter_function.__name__}"
+
+    @staticmethod
+    def _get_custom(col: str) -> t.Optional[t.List[str]]:
+        return col[len(_CUSTOM_PREFIX) :].split(":") if col.startswith(_CUSTOM_PREFIX) else None
+
+
+@dataclass
+class DataNodeFilter(_Filter):
+    """
+    used to describe a filter on a datanode property
+    """
+
+    property_id: str
+
+    def get_property(self):
+        return self.property_id

+ 1 - 1
tests/gui_core/test_context_is_readable.py

@@ -178,7 +178,7 @@ class TestGuiCoreContext_is_readable:
         with patch("taipy.gui_core._context.core_get", side_effect=mock_core_get) as mockget:
         with patch("taipy.gui_core._context.core_get", side_effect=mock_core_get) as mockget:
             mockget.reset_mock()
             mockget.reset_mock()
             mockGui = Mock(Gui)
             mockGui = Mock(Gui)
-            mockGui._get_autorization = lambda s: contextlib.nullcontext()
+            mockGui._get_authorization = lambda s: contextlib.nullcontext()
             gui_core_context = _GuiCoreContext(mockGui)
             gui_core_context = _GuiCoreContext(mockGui)
 
 
             def sub_cb():
             def sub_cb():