Browse Source

Merge branch 'develop' into refactor/allow-passing-extra-package-to-check-dependencies

Đỗ Trường Giang 2 tuần trước cách đây
mục cha
commit
c81e0f2836

+ 1 - 1
.github/actions/install/action.yml

@@ -37,7 +37,7 @@ runs:
         cache-dependency-path: 'Pipfile'
         cache-dependency-path: 'Pipfile'
 
 
     - name: Install pipenv
     - name: Install pipenv
-      run: pip install pipenv --upgrade
+      run: pip install "pipenv<2025.0.0"
       shell: bash
       shell: bash
 
 
     - name: Install pipenv dependencies
     - name: Install pipenv dependencies

+ 2 - 2
.github/workflows/build-and-release-single-package.yml

@@ -135,7 +135,7 @@ jobs:
       - name: Install dependencies
       - name: Install dependencies
         run: |
         run: |
           python -m pip install --upgrade pip
           python -m pip install --upgrade pip
-          pip install build wheel pipenv mypy black isort
+          pip install build wheel "pipenv<2025.0.0" mypy black isort
 
 
       - name: Install GUI dependencies
       - name: Install GUI dependencies
         if: ${{ github.event.inputs.target_package == 'gui' || github.event.inputs.target_package == 'taipy' }}
         if: ${{ github.event.inputs.target_package == 'gui' || github.event.inputs.target_package == 'taipy' }}
@@ -168,7 +168,7 @@ jobs:
           if compgen -G "./dist/*_*" > /dev/null; then
           if compgen -G "./dist/*_*" > /dev/null; then
             for file in ./dist/*_*; do mv "$file" "${file//_/-}"; done
             for file in ./dist/*_*; do mv "$file" "${file//_/-}"; done
           fi
           fi
-          
+
       - name: Create tag and release
       - name: Create tag and release
         working-directory: "build_${{ github.event.inputs.target_package }}"
         working-directory: "build_${{ github.event.inputs.target_package }}"
         run: |
         run: |

+ 2 - 2
.github/workflows/build-and-release.yml

@@ -128,7 +128,7 @@ jobs:
       - name: Install dependencies
       - name: Install dependencies
         run: |
         run: |
           python -m pip install --upgrade pip
           python -m pip install --upgrade pip
-          pip install build wheel pipenv mypy black isort
+          pip install build wheel "pipenv<2025.0.0" mypy black isort
 
 
       - name: Build GUI front-end
       - name: Build GUI front-end
         if: ${{ matrix.package == 'gui' }}
         if: ${{ matrix.package == 'gui' }}
@@ -211,7 +211,7 @@ jobs:
       - name: Install dependencies
       - name: Install dependencies
         run: |
         run: |
           python -m pip install --upgrade pip
           python -m pip install --upgrade pip
-          pip install build wheel pipenv mypy black isort
+          pip install build wheel "pipenv<2025.0.0" mypy black isort
 
 
       - uses: actions/download-artifact@v4
       - uses: actions/download-artifact@v4
         with:
         with:

+ 4 - 5
.github/workflows/partial-tests.yml

@@ -17,8 +17,7 @@ jobs:
           mypy_flags:  "--ignore-missing-imports --implicit-optional --disable-error-code attr-defined --no-namespace-packages --exclude (taipy/templates/|tools/|doc/gui/examples/.*/builder.py|taipy/common/config/config.pyi) --follow-imports skip --disable-error-code import-untyped"
           mypy_flags:  "--ignore-missing-imports --implicit-optional --disable-error-code attr-defined --no-namespace-packages --exclude (taipy/templates/|tools/|doc/gui/examples/.*/builder.py|taipy/common/config/config.pyi) --follow-imports skip --disable-error-code import-untyped"
 
 
       - uses: chartboost/ruff-action@v1
       - uses: chartboost/ruff-action@v1
-        with:
-          src: './taipy ./tests'
+
   tests:
   tests:
     needs: linter
     needs: linter
     timeout-minutes: 50
     timeout-minutes: 50
@@ -58,7 +57,7 @@ jobs:
         run: pip install --upgrade setuptools wheel
         run: pip install --upgrade setuptools wheel
 
 
       - name: Install pipenv
       - name: Install pipenv
-        run: pip install pipenv --upgrade
+        run: pip install "pipenv<2025.0.0"
 
 
       - name: Install Dependencies
       - name: Install Dependencies
         run: pipenv install --dev --python=${{ matrix.python-version }}
         run: pipenv install --dev --python=${{ matrix.python-version }}
@@ -156,7 +155,7 @@ jobs:
 
 
       - name: Install pipenv
       - name: Install pipenv
         if: steps.changes.outputs.core == 'true'
         if: steps.changes.outputs.core == 'true'
-        run: pip install pipenv --upgrade
+        run: pip install "pipenv<2025.0.0"
 
 
       - name: Install Dependencies
       - name: Install Dependencies
         if: steps.changes.outputs.core == 'true'
         if: steps.changes.outputs.core == 'true'
@@ -199,7 +198,7 @@ jobs:
 
 
       - name: Install pipenv
       - name: Install pipenv
         if: steps.changes.outputs.core == 'true'
         if: steps.changes.outputs.core == 'true'
-        run: pip install pipenv --upgrade
+        run: pip install "pipenv<2025.0.0"
 
 
       - name: Install Dependencies
       - name: Install Dependencies
         if: steps.changes.outputs.core == 'true'
         if: steps.changes.outputs.core == 'true'

+ 0 - 1
Pipfile

@@ -63,7 +63,6 @@ pytest-cov = "*"
 pytest-mock = "*"
 pytest-mock = "*"
 pytest-playwright = "*"
 pytest-playwright = "*"
 pytest-timeout = "*"
 pytest-timeout = "*"
-python-dotenv = "*"
 testbook = "*"
 testbook = "*"
 twine = "*"
 twine = "*"
 types-flask = "*"
 types-flask = "*"

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

@@ -23,6 +23,7 @@ import { stringIcon } from "../../utils/icon";
 import { TableValueType } from "./tableUtils";
 import { TableValueType } from "./tableUtils";
 
 
 import { toDataUrl } from "../../utils/image";
 import { toDataUrl } from "../../utils/image";
+import { LoVElt } from "./lovUtils";
 jest.mock('../../utils/image', () => ({
 jest.mock('../../utils/image', () => ({
     toDataUrl: (url: string) => new Promise((resolve) => resolve(url)),
     toDataUrl: (url: string) => new Promise((resolve) => resolve(url)),
   }));
   }));
@@ -40,9 +41,10 @@ const messages: TableValueType = {
         start: 0,
         start: 0,
     },
     },
 };
 };
-const user1: [string, stringIcon] = ["Fred", { path: "/images/favicon.png", text: "Fred.png" }];
+const user1: [string, stringIcon] = ["Fred", { path: "/images/favicon.png", text: "Fred" }];
 const user2: [string, stringIcon] = ["Fredi", { path: "/images/fred.png", text: "Fredi.png" }];
 const user2: [string, stringIcon] = ["Fredi", { path: "/images/fred.png", text: "Fredi.png" }];
 const users = [user1, user2];
 const users = [user1, user2];
+const users2 = [["<taipy.gui.icon.Icon 1", user1[1]], ["<taipy.gui.icon.Icon 2", user2[1]]] as LoVElt[];
 
 
 const searchMsg = messages[valueKey].data[0][1];
 const searchMsg = messages[valueKey].data[0][1];
 
 
@@ -65,7 +67,12 @@ describe("Chat Component", () => {
     });
     });
     it("can display an avatar", async () => {
     it("can display an avatar", async () => {
         const { getByAltText } = render(<Chat messages={messages} users={users} defaultKey={valueKey} mode="raw" />);
         const { getByAltText } = render(<Chat messages={messages} users={users} defaultKey={valueKey} mode="raw" />);
-        const elt = getByAltText("Fred.png");
+        const elt = getByAltText("Fred");
+        expect(elt.tagName).toBe("IMG");
+    });
+    it("can display an avatar from Icon", async () => {
+        const { getByAltText } = render(<Chat messages={messages} users={users2} defaultKey={valueKey} mode="raw" />);
+        const elt = getByAltText("Fred");
         expect(elt.tagName).toBe("IMG");
         expect(elt.tagName).toBe("IMG");
     });
     });
     it("is disabled", async () => {
     it("is disabled", async () => {

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

@@ -368,7 +368,8 @@ const Chat = (props: ChatProps) => {
     const avatars = useMemo(() => {
     const avatars = useMemo(() => {
         return users.reduce((pv, elt) => {
         return users.reduce((pv, elt) => {
             if (elt.id) {
             if (elt.id) {
-                pv[elt.id] =
+                const id = elt.id.startsWith("<taipy.gui.icon.Icon") && typeof elt.item !== "string" ? elt.item.text : elt.id;
+                pv[id] =
                     typeof elt.item == "string" ? (
                     typeof elt.item == "string" ? (
                         <Tooltip title={elt.item}>
                         <Tooltip title={elt.item}>
                             <Avatar sx={chatAvatarSx}>{getInitials(elt.item)}</Avatar>
                             <Avatar sx={chatAvatarSx}>{getInitials(elt.item)}</Avatar>

+ 1 - 1
frontend/taipy-gui/src/utils/index.ts

@@ -206,7 +206,7 @@ export const formatWSValue = (
 export const getInitials = (value: string, max = 2): string =>
 export const getInitials = (value: string, max = 2): string =>
     (value || "")
     (value || "")
         .split(" ", max)
         .split(" ", max)
-        .map((word) => (word.length ? word.charAt(0) : ""))
+        .map((word) => (word.length ? String.fromCodePoint(word.codePointAt(0) || 0) : ""))
         .join("")
         .join("")
         .toUpperCase();
         .toUpperCase();
 
 

+ 26 - 23
tools/coverage_check.py

@@ -10,19 +10,20 @@
 # specific language governing permissions and limitations under the License.
 # specific language governing permissions and limitations under the License.
 
 
 import argparse
 import argparse
-import xmltodict
-import sys
 import subprocess
 import subprocess
+import sys
+
+import xmltodict
 
 
 
 
 def check_total_coverage(coverage_file, threshold=80):
 def check_total_coverage(coverage_file, threshold=80):
     """Check the total project coverage."""
     """Check the total project coverage."""
     with open(coverage_file) as f:
     with open(coverage_file) as f:
         data = xmltodict.parse(f.read())
         data = xmltodict.parse(f.read())
-    total_coverage = float(data['coverage']['@line-rate']) * 100
-    print(f"Total Coverage: {total_coverage:.2f}%")
+    total_coverage = float(data["coverage"]["@line-rate"]) * 100
+    print(f"Total Coverage: {total_coverage:.2f}%")  # noqa: T201
     if total_coverage < threshold:
     if total_coverage < threshold:
-        print(f"Total project coverage is below {threshold}%: {total_coverage:.2f}%")
+        print(f"Total project coverage is below {threshold}%: {total_coverage:.2f}%")  # noqa: T201
         sys.exit(1)
         sys.exit(1)
 
 
 
 
@@ -49,56 +50,58 @@ def check_changed_files_coverage(coverage_file, changed_files, threshold=80):
     for file in changed_files:
     for file in changed_files:
         if file in files:
         if file in files:
             coverage = files[file]
             coverage = files[file]
-            print(f"Coverage for {file}: {coverage:.2f}%")
+            print(f"Coverage for {file}: {coverage:.2f}%")  # noqa: T201
             sum_coverage += coverage
             sum_coverage += coverage
             qty += 1
             qty += 1
         else:
         else:
-            print(f"No coverage data found for {file}")
+            print(f"No coverage data found for {file}")  # noqa: T201
 
 
     if qty:
     if qty:
-        if sum_coverage/qty < threshold:
-            print(f"Coverage for changed files is below {threshold}%: {sum_coverage/qty:.2f}%")
+        if sum_coverage / qty < threshold:
+            print(f"Coverage for changed files is below {threshold}%: {sum_coverage/qty:.2f}%")  # noqa: T201
             sys.exit(1)
             sys.exit(1)
-        print(f"Coverage for changed files: {sum_coverage/qty:.2f}%")
+        print(f"Coverage for changed files: {sum_coverage/qty:.2f}%")  # noqa: T201
     else:
     else:
-        print("No file detected to run coverage for.")
+        print("No file detected to run coverage for.")  # noqa: T201
 
 
 
 
 def get_changed_files(base_branch):
 def get_changed_files(base_branch):
     """Get the list of changed Python files in the pull request."""
     """Get the list of changed Python files in the pull request."""
     try:
     try:
         result = subprocess.run(
         result = subprocess.run(
-            ['git', 'diff', '--name-only', f"origin/{base_branch}", '--', '*.py'],
+            ["git", "diff", "--name-only", f"origin/{base_branch}", "--", "*.py"],
             capture_output=True,
             capture_output=True,
             text=True,
             text=True,
             check=True,
             check=True,
         )
         )
         changed_files = [
         changed_files = [
-            file.replace("taipy/", "") for file in result.stdout.strip().splitlines() if not file.startswith(('tests/', 'tools/'))
+            file.replace("taipy/", "")
+            for file in result.stdout.strip().splitlines()
+            if not file.startswith(("tests/", "tools/"))
         ]
         ]
         return changed_files
         return changed_files
     except subprocess.CalledProcessError as e:
     except subprocess.CalledProcessError as e:
-        print(f"Error fetching changed files: {e}")
+        print(f"Error fetching changed files: {e}") # noqa: T201
         sys.exit(1)
         sys.exit(1)
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     parser = argparse.ArgumentParser(description="Coverage check script.")
     parser = argparse.ArgumentParser(description="Coverage check script.")
-    parser.add_argument('command', choices=['check-total', 'check-changed'], help="Command to execute")
-    parser.add_argument('--coverage-file', default='coverage.xml', help="Path to the coverage XML file")
-    parser.add_argument('--threshold', type=float, default=80, help="Coverage threshold percentage")
-    parser.add_argument('--base-branch', help="Base branch for comparing changed files")
+    parser.add_argument("command", choices=["check-total", "check-changed"], help="Command to execute")
+    parser.add_argument("--coverage-file", default="coverage.xml", help="Path to the coverage XML file")
+    parser.add_argument("--threshold", type=float, default=80, help="Coverage threshold percentage")
+    parser.add_argument("--base-branch", help="Base branch for comparing changed files")
 
 
     args = parser.parse_args()
     args = parser.parse_args()
 
 
-    if args.command == 'check-total':
+    if args.command == "check-total":
         check_total_coverage(args.coverage_file, args.threshold)
         check_total_coverage(args.coverage_file, args.threshold)
-    elif args.command == 'check-changed':
+    elif args.command == "check-changed":
         if not args.base_branch:
         if not args.base_branch:
-            print("Error: --base-branch is required for check-changed")
+            print("Error: --base-branch is required for check-changed") # noqa: T201
             sys.exit(1)
             sys.exit(1)
         changed_files = get_changed_files(args.base_branch)
         changed_files = get_changed_files(args.base_branch)
         if not changed_files:
         if not changed_files:
-            print("No relevant Python files changed.")
+            print("No relevant Python files changed.") # noqa: T201
             sys.exit(0)
             sys.exit(0)
         check_changed_files_coverage(args.coverage_file, changed_files, args.threshold)
         check_changed_files_coverage(args.coverage_file, changed_files, args.threshold)

+ 5 - 4
tools/gui/generate_pyi.py

@@ -81,9 +81,10 @@ with open(gui_pyi_file, "w", encoding="utf-8") as write_file:
 # Generate Page Builder pyi file (gui/builder/__init__.pyi)
 # Generate Page Builder pyi file (gui/builder/__init__.pyi)
 # ##################################################################################################
 # ##################################################################################################
 # Types that appear in viselements.json
 # Types that appear in viselements.json
-from taipy.gui import Icon  # noqa: E402
-from taipy.core import Cycle, DataNode, Job, Scenario  # noqa: E402
-from datetime import datetime
+from datetime import datetime  # noqa: E402, F401
+
+from taipy.core import Cycle, DataNode, Job, Scenario  # noqa: E402, F401
+from taipy.gui import Icon  # noqa: E402, F401
 
 
 # Read the version
 # Read the version
 current_version = "latest"
 current_version = "latest"
@@ -187,7 +188,7 @@ def format_as_parameter(property: Dict[str, str], element_name: str):
         elif hasattr(type_desc, "__name__") and type_desc.__name__ not in ["str", "Any"]:
         elif hasattr(type_desc, "__name__") and type_desc.__name__ not in ["str", "Any"]:
             type = f"Union[{type}, str]"
             type = f"Union[{type}, str]"
     except NameError:
     except NameError:
-        print(f"WARNING - Couldn't parse type '{type}' in {element_name}.{name}")
+        print(f"WARNING - Couldn't parse type '{type}' in {element_name}.{name}") # noqa: T201
 
 
     if default_value is None or default_value == "None":
     if default_value is None or default_value == "None":
         default_value = " = None"
         default_value = " = None"

+ 1 - 1
tools/release/common.py

@@ -20,7 +20,7 @@ import typing as t
 from dataclasses import asdict, dataclass
 from dataclasses import asdict, dataclass
 from datetime import datetime
 from datetime import datetime
 from pathlib import Path
 from pathlib import Path
-from functools import total_ordering
+
 import requests
 import requests