Răsfoiți Sursa

Package management script (#638)

Vuillemot Florian 1 an în urmă
părinte
comite
e637fdf524

+ 65 - 0
.github/actions/install/action.yml

@@ -0,0 +1,65 @@
+name: install
+description: Install the package and dependencies
+inputs:
+  python-version:
+    required: true
+    description: 'Python version'
+  os:
+    required: true
+    description: 'Operating system'
+  install-gui:
+    required: false
+    description: 'Whether to install the GUI dependencies'
+    default: true
+  pipfile-version:
+    required: false
+    description: 'The Pipfile type: one of min, max'
+    default: 'min'
+outputs:
+  to-update:
+    description: "True if the dependencies can be updated"
+    value: ${{ steps.install-dependencies.outputs.diff != '' }}
+runs:
+  using: "composite"
+  steps:
+    - uses: actions/setup-python@v5
+      with:
+        python-version: ${{ inputs.python-version }}
+
+    - name: Install pipenv
+      run: curl https://raw.githubusercontent.com/pypa/pipenv/master/get-pipenv.py | python
+      shell: bash
+
+    - name: Install minimum supported dependencies
+      if: inputs.pipfile-version == 'min'
+      run: pipenv install --dev --python=${{ inputs.python-version }}
+      shell: bash
+
+    - name: Install maximum version supported dependencies
+      if: inputs.pipfile-version == 'max'
+      run: |
+        # Override the current Pipfile with the Pipfile containing the max versions
+        rm Pipfile
+        mv tools/packages/pipfiles/Pipfile${{ inputs.python-version }}.max Pipfile
+        pipenv install --dev --python=${{ inputs.python-version }}
+      shell: bash
+
+    - name: Setup LibMagic (MacOS)
+      if: inputs.os == 'macos-latest' && inputs.install-gui
+      run: brew install libmagic
+      shell: bash
+
+    - uses: actions/setup-node@v4
+      if: inputs.install-gui
+      with:
+        node-version: 20
+
+    - name: Frontend Bundle Build
+      if: inputs.install-gui
+      run: pipenv run python tools/frontend/bundle_build.py
+      shell: bash
+
+    - name: Install Playwright
+      if: inputs.install-gui
+      run: pipenv run playwright install chromium --with-deps
+      shell: bash

+ 59 - 0
.github/workflows/dependencies-management.yml

@@ -0,0 +1,59 @@
+name: Dependencies management
+
+on:
+  schedule:
+    # Run each Sunday at mid day
+    - cron: 00 12 * * 0
+
+  workflow_dispatch:
+
+jobs:
+  latest-versions:
+    timeout-minutes: 20
+    strategy:
+      matrix:
+        python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
+        os: [ubuntu-latest]
+    permissions:
+      contents: write
+      pull-requests: write
+    runs-on: ${{ matrix.os }}
+    steps:
+      - uses: actions/checkout@v4
+
+      - uses: actions/setup-python@v5
+        with:
+          python-version: ${{matrix.python-version}}
+
+      - name: Check if the latest version supported is up to date.
+        id: ensure-dependencies-are-up-to-date
+        working-directory: tools/packages
+        run: |
+          # Ensure dependencies are aligned between Taipy packages
+          pip install -r requirements.txt
+          python check-dependencies.py ensure-same-version
+
+          # Try to update the Pipfile.
+          # Any new packages available are printed to stdout.
+          # If nothing is printed, the Pipfile is up to date and workflow can stop.
+          echo 'diff<<EOF' >> "$GITHUB_OUTPUT"
+          bash check-dependencies.sh pipfiles/Pipfile${{matrix.python-version}}.max >> "$GITHUB_OUTPUT"
+          echo EOF >> "$GITHUB_OUTPUT"
+          cat pipfiles/Pipfile${{matrix.python-version}}.max
+
+      - name: Create the pull_request
+        # Python3.8 must not update the dependencies.
+        if: steps.ensure-dependencies-are-up-to-date.outputs.diff != '' and ${{matrix.python-version}} != '3.8'
+        uses: peter-evans/create-pull-request@v5
+        with:
+          token: ${{ secrets.GITHUB_TOKEN }}
+          commit-message: Update dependencies
+          branch: dependencies/update-python${{matrix.python-version}}
+          base: develop
+          title: 'New dependencies available for Python${{matrix.python-version}}'
+          body: |
+            ${{ steps.ensure-dependencies-are-up-to-date.outputs.diff }}
+          draft: false
+          add-paths: |
+            tools/packages/pipfiles/Pipfile${{matrix.python-version}}.max
+            tools/packages/taipy*/*requirements.txt

+ 20 - 53
.github/workflows/overall-tests.yml

@@ -18,6 +18,7 @@ jobs:
       matrix:
         python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
         os: [ubuntu-latest, windows-latest, macos-latest]
+        pipfile-version: ['min', 'max']
     runs-on: ${{ matrix.os }}
     steps:
       - uses: actions/checkout@v4
@@ -26,30 +27,25 @@ jobs:
         with:
           python-version: ${{matrix.python-version}}
 
-      - name: Install pipenv
-        run: pip install --upgrade pipenv
-
-      - name: Install Setuptools and wheel
-        run: pipenv run pip install --upgrade setuptools wheel
-
-      - name: Install Dependencies
-        run: pipenv install --dev --python=${{ matrix.python-version }}
-
-      - name: Setup LibMagic (MacOS)
-        if: matrix.os == 'macos-latest'
-        run: brew install libmagic
+      - name: Install dependencies
+        id: install-dependencies
+        uses: ./.github/actions/install
+        with:
+          python-version: ${{ matrix.python-version }}
+          os: ${{ matrix.os }}
+          pipfile-version: ${{ matrix.pipfile-version }}
 
       - name: Pytest
         run: pipenv run pytest -m "not orchestrator_dispatcher and not standalone and not teste2e" --cov=taipy --cov-append --cov-report="xml:overall-coverage.xml" --cov-report term-missing tests
 
       - name: Coverage
-        if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.11'
+        if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.11' && matrix.pipfile-version == 'min'
         uses: MishaKav/pytest-coverage-comment@main
         with:
           pytest-xml-coverage-path: ./overall-coverage.xml
           title: Taipy Overall Coverage Report
 
-  submit_tests:
+  intermittent-tests:
     needs: [partial-tests]
     timeout-minutes: 40
     strategy:
@@ -57,6 +53,8 @@ jobs:
       matrix:
         python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
         os: [ubuntu-latest, windows-latest, macos-latest]
+        orchestrator: ['orchestrator_dispatcher', 'standalone']
+        pipfile-version: ['min', 'max']
     runs-on: ${{ matrix.os }}
     steps:
       - uses: actions/checkout@v4
@@ -64,44 +62,13 @@ jobs:
         with:
           python-version: ${{matrix.python-version}}
 
-      - name: Install pipenv
-        run: pip install --upgrade pipenv
-
-      - name: Install Dependencies
-        run: pipenv install --dev --python=${{ matrix.python-version }}
-
-      - name: Setup LibMagic (MacOS)
-        if: matrix.os == 'macos-latest'
-        run: brew install libmagic
-
-      - name: Pytest Core orchestrator_dispatcher
-        run: pipenv run pytest -m "orchestrator_dispatcher" tests/core
-
-  standalone_tests:
-    needs: [partial-tests]
-    timeout-minutes: 40
-    strategy:
-      fail-fast: false
-      matrix:
-        python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
-        os: [ubuntu-latest, windows-latest, macos-latest]
-    runs-on: ${{ matrix.os }}
-    steps:
-      - uses: actions/checkout@v4
-
-      - uses: actions/setup-python@v5
+      - name: Install dependencies
+        uses: ./.github/actions/install
         with:
-          python-version: ${{matrix.python-version}}
-
-      - name: Install pipenv
-        run: pip install --upgrade pipenv
+          os: ${{ matrix.os }}
+          install-gui: false
+          python-version: ${{ matrix.python-version }}
+          pipfile-version: ${{ matrix.pipfile-version }}
 
-      - name: Install Dependencies
-        run: pipenv install --dev --python=${{ matrix.python-version }}
-
-      - name: Setup LibMagic (MacOS)
-        if: matrix.os == 'macos-latest'
-        run: brew install libmagic
-
-      - name: Pytest Core standalone
-        run: pipenv run pytest -m "standalone" tests/core
+      - name: Pytest Core orchestrator_dispatcher
+        run: pipenv run pytest -m "${{ matrix.orchestrator }}" tests/core

+ 3 - 1
setup.py

@@ -45,7 +45,9 @@ def get_requirements():
     # get requirements from the different setups in tools/packages (removing taipy packages)
     reqs = set()
     for pkg in (root_folder / "tools" / "packages").iterdir():
-        reqs.update((pkg / "setup.requirements.txt").read_text("UTF-8").splitlines())
+        requirements_file = pkg / "setup.requirements.txt"
+        if requirements_file.exists():
+            reqs.update(requirements_file.read_text("UTF-8").splitlines())
 
     return [r for r in reqs if r and not r.startswith("taipy")]
 

+ 453 - 0
tools/packages/check-dependencies.py

@@ -0,0 +1,453 @@
+'''
+This script is a helper on the dependencies management of the project.
+It can be used:
+- To check that the same version of a package is set across files.
+- To generate a Pipfile and requirements files with the latest version installables.
+- To display a summary of the dependencies to update.
+'''
+import glob
+import itertools
+import sys
+from dataclasses import dataclass, field
+from datetime import datetime
+from pathlib import Path
+from typing import Dict, List
+
+import tabulate
+import toml
+
+
+@dataclass
+class Release:
+    """
+    Information about a release of a package.
+    """
+    version: str
+    upload_date: datetime.date
+
+
+@dataclass
+class Package:
+    """
+    Information about a package.
+    """
+    # Package name
+    name: str
+    # Min version of the package set as requirements.
+    min_version: str
+    # Max version of the package set as requirements.
+    max_version: str
+    # Setup installation markers of the package.
+    # ex: ;python_version>="3.6"
+    installation_markers: str
+    # Taipy dependencies are ignored.
+    is_taipy: bool
+    # Optional dependencies
+    extras_dependencies: List[str]
+    # Files where the package is set as requirement.
+    files: List[str]
+    # List of releases of the package.
+    releases: List[Release] = field(default_factory=list)
+    # Min release of the package.
+    # Also present in the releases list.
+    min_release: Release = None
+    # Max release of the package.
+    # Also present in the releases list.
+    max_release: Release = None
+    # Latest version available on PyPI.
+    latest_release: Release = None
+
+    def __eq__(self, other):
+        return self.name == other.name
+
+    def __hash__(self):
+        return hash(self.name)
+
+    def load_releases(self):
+        """
+        Retrieve all releases of the package from PyPI.
+        """
+        import requests  # pylint: disable=import-outside-toplevel
+
+        releases = requests.get(
+            f"https://pypi.org/pypi/{self.name}/json",
+            timeout=5
+        ).json().get('releases', {})
+
+        for version, info in releases.items():
+            # Ignore old releases without upload time.
+            if not info:
+                continue
+            # Ignore pre and post releases.
+            if any(str.isalpha(c) for c in version):
+                continue
+            date = datetime.strptime(info[0]['upload_time'], "%Y-%m-%dT%H:%M:%S").date()
+            release = Release(version, date)
+            self.releases.append(release)
+            if self.min_version == version:
+                self.min_release = release
+            # Min and max version can be the same.
+            if self.max_version == version:
+                self.max_release = release
+
+        self.releases.sort(key=lambda x: x.upload_date, reverse=True)
+        self.latest_release = self.releases[0]
+
+    def as_requirements_line(self, with_version: bool = True) -> str:
+        """
+        Return the package as a requirements line.
+        """
+        if self.is_taipy:
+            return self.name
+
+        name = self.name
+        if self.extras_dependencies:
+            name += f'[{",".join(self.extras_dependencies)}]'
+
+        if with_version:
+            if self.installation_markers:
+                return f'{name}>={self.min_version},<={self.max_version};{self.installation_markers}'
+            return f'{name}>={self.min_version},<={self.max_version}'
+
+        if self.installation_markers:
+            return f'{name};{self.installation_markers}'
+        return name
+
+    def as_pipfile_line(self) -> str:
+        """
+        Return the package as a pipfile line.
+        If min_version is True, the min version is used.
+        """
+        line = f'"{self.name}" = {{version="=={self.max_version}"'
+
+        if self.installation_markers:
+            line += f', markers="{self.installation_markers}"'
+
+        if self.extras_dependencies:
+            dep = ','.join(f'"{p}"' for p in self.extras_dependencies)
+            line += f', extras=[{dep}]'
+
+        line += '}'
+        return line
+
+    @classmethod
+    def check_format(cls, package: str):
+        """
+        Check if a package definition is correctly formatted.
+        """
+        if '>=' not in package or '<' not in package:
+            # Only Taipy dependencies can be without version.
+            if 'taipy' not in package:
+                raise Exception(f"Invalid package: {package}")
+
+    @classmethod
+    def from_requirements(cls, package: str, filename: str):
+        """
+        Create a package from a requirements line.
+        ex: "pandas>=1.0.0,<2.0.0;python_version<'3.9'"
+        """
+        try:
+            # Lower the name to avoid case issues.
+            name = extract_name(package).lower()
+            is_taipy = 'taipy' in name
+            return cls(
+                name,
+                extract_min_version(package) if not is_taipy else '',
+                extract_max_version(package) if not is_taipy else '',
+                extract_installation_markers(package) if not is_taipy else '',
+                is_taipy,
+                extract_extras_dependencies(package),
+                [filename]
+            )
+        except Exception as e:
+            print(f"Error while parsing package {package}: {e}") # noqa: T201
+            raise
+
+
+def extract_installation_markers(package: str) -> str:
+    """
+    Extract the installation markers of a package from a requirements line.
+    ex: "pandas>=1.0.0,<2.0.0;python_version<'3.9'" -> "python_version<'3.9'"
+    """
+    if ';' not in package:
+        return ''
+    return package.split(';')[1]
+
+
+def extract_min_version(package: str) -> str:
+    """
+    Extract the min version of a package from a requirements line.
+    ex: "pandas>=1.0.0,<2.0.0;python_version<'3.9'" -> "1.0.0"
+    """
+    # The max version is the defined version if it is a fixed version.
+    if '==' in package:
+        version = package.split('==')[1]
+        if ';' in version:
+            # Remove installation markers.
+            version = version.split(';')[0]
+        return version
+
+    return package.split('>=')[1].split(',')[0]
+
+
+def extract_max_version(package: str) -> str:
+    """
+    Extract the max version of a package from a requirements line.
+    Ex:
+        - pandas==1.0.0 -> 1.0.0
+        - pandas>=1.0.0,<=2.0.0 -> 2.0.0
+        - pandas==1.0.0;python_version<'3.9' -> 1.0.0
+        - pandas>=1.0.0,<2.0.0;python_version<'3.9' -> 2.0.0
+    """
+    # The max version is the defined version if it is a fixed version.
+    if '==' in package:
+        version = package.split('==')[1]
+        if ';' in version:
+            # Remove installation markers.
+            version = version.split(';')[0]
+        return version
+
+    version = None
+
+    if ',<=' in package:
+        version = package.split(',<=')[1]
+    else:
+        version = package.split(',<')[1]
+
+    if ';' in version:
+        # Remove installation markers.
+        version = version.split(';')[0]
+
+    return version
+
+
+def extract_name(package: str) -> str:
+    """
+    Extract the name of a package from a requirements line.
+    Ex:
+        - pandas==1.0.0 -> pandas
+        - pandas>=1.0.0,<2.0.0 -> pandas
+        - pandas==1.0.0;python_version<'3.9' -> pandas
+        - pandas>=1.0.0,<2.0.0;python_version<'3.9' -> pandas
+    """
+    if '==' in package:
+        return package.split('==')[0]
+
+    name = package.split('>=')[0]
+
+    # Remove optional dependencies.
+    # Ex: "pandas[sql]" -> "pandas"
+    if '[' in name:
+        name = name.split('[')[0]
+    return name
+
+
+def extract_extras_dependencies(package: str) -> List[str]:
+    """
+    Extract the extras dependencies of a package from a requirements line.
+    Ex:
+        - pymongo[srv]>=4.2.0,<=4.6.1 -> ["srv"]
+    """
+    if '[' not in package:
+        return []
+
+    return package.split('[')[1].split(']')[0].split(',')
+
+
+def load_dependencies(requirements_filenames: List[str], enforce_format: bool) -> Dict[str, Package]:
+    """
+    Load and concat dependencies from requirements files.
+    """
+    # Extracted dependencies from requirements files.
+    dependencies = {}
+
+    for filename in requirements_filenames:
+        file_dependencies = Path(filename).read_text("UTF-8").split("\n")
+
+        for package_requirements in file_dependencies:
+            # Ignore empty lines.
+            if not package_requirements:
+                continue
+
+            # Ensure the package is correctly formatted with born min and max.
+            if enforce_format:
+                Package.check_format(package_requirements)
+
+            package = Package.from_requirements(package_requirements, filename)
+
+            # dependencies may be present multiple times in different files.
+            # In that case, do not load the releases again but ensure versions are the same.
+            if package.name in dependencies:
+                existing_package = dependencies[package.name]
+                if not existing_package.min_version == package.min_version or \
+                    not existing_package.max_version == package.max_version:
+                    raise Exception(
+                        f"Inconsistent version of '{package.name}' between '{filename}' and {','.join(package.files)}."
+                    )
+
+                # Add the file as dependency of the package.
+                existing_package.files.append(filename)
+                # Stop processing, package is already extracted.
+                continue
+
+            dependencies[package.name] = package
+
+    return dependencies
+
+
+def display_dependencies_versions(dependencies: Dict[str, Package]):
+    """
+    Display dependencies information.
+    """
+    to_print = []
+
+    for package_name, package in dependencies.items():
+        if package.is_taipy:
+            continue
+
+        # Load the latest releases of the package.
+        package.load_releases()
+
+        to_print.append((
+            package_name,
+            f'{package.min_version} ({package.min_release.upload_date if package.min_release else "N.A."})',
+            f'{package.max_version} ({package.max_release.upload_date if package.max_release else "N.C."})',
+            f'{package.releases[0].version} ({package.releases[0].upload_date})',
+            len(list(itertools.takewhile(lambda x: x.version != package.max_version, package.releases))),  # noqa: B023
+        ))
+
+    to_print.sort(key=lambda x: x[0])
+    h = ['name', 'version-min', 'version-max', 'current-version', 'nb-releases-behind']
+    print(tabulate.tabulate(to_print, headers=h, tablefmt='pretty')) # noqa: T201
+
+
+def update_dependencies(
+        # Dependencies installed in the environment.
+        dependencies_installed: Dict[str, Package],
+        # Dependencies set in requirements files.
+        dependencies_set: Dict[str, Package],
+        # Requirements files to update.
+        requirements_filenames: List[str]
+    ):
+    """
+    Display and updates dependencies.
+    """
+    to_print = []
+
+    for name, ds in dependencies_set.items():
+        if ds.is_taipy:
+            continue
+
+        # Find the package in use.
+        di = dependencies_installed.get(name)
+        # Some package as 'gitignore-parser' becomes 'gitignore_parser' during the installation.
+        if not di:
+            di = dependencies_installed.get(name.replace('-', '_'))
+
+        if di:
+            if di.max_version != ds.max_version:
+                to_print.append((
+                    name,
+                    di.max_version,
+                    ','.join(f.split('/')[0] for f in ds.files)
+                ))
+                # Save the new dependency version.
+                ds.max_version = di.max_version
+
+    # Print the dependencies to update.
+    to_print.sort(key=lambda x: x[0])
+    print(tabulate.tabulate(to_print, headers=['name', 'version', 'files'], tablefmt='pretty')) # noqa: T201
+
+    # Update requirements files.
+    for fd in requirements_filenames:
+        requirements = '\n'.join(
+            d.as_requirements_line()
+            for d in sorted(dependencies_set.values(), key=lambda d: d.name)
+            if fd in d.files
+        )
+        # Add a new line at the end of the file.
+        requirements += '\n'
+        Path(fd).write_text(requirements, 'UTF-8')
+
+
+def generate_raw_requirements_txt(dependencies: Dict[str, Package]):
+    """
+    Print the dependencies as requirements lines without version.
+    """
+    for package in dependencies.values():
+        if not package.is_taipy:
+            print(package.as_requirements_line(with_version=False)) # noqa: T201
+
+
+def update_pipfile(pipfile: str, dependencies_version: Dict[str, Package]):
+    """
+    Update in place dependencies version of a Pipfile.
+    Warning:
+      Dependencies are loaded from requirements files without extras or markers.
+      The Pipfile contains extras and markers information.
+    """
+    dependencies_str = ""
+    pipfile_obj = toml.load(pipfile)
+
+    packages = pipfile_obj.pop('packages')
+    for name, dep in packages.items():
+        # Find the package in use.
+        rp = dependencies_version.get(name)
+        # Some package as 'gitignore-parser' becomes 'gitignore_parser' during the installation.
+        if not rp:
+            rp = dependencies_version.get(name.replace('-', '_'))
+            if rp:
+                # Change for the real name of the package.
+                rp.name = name
+
+        if not rp:
+            # Package not found. Can be due to python version.
+            # Ex: backports.zoneinfo
+            if isinstance(dep, dict):
+                new_dep = ''
+                # Format as a Pipfile line.
+                new_dep = f'version="{dep["version"]}"'
+                if dep.get('markers'):
+                    new_dep += f', markers="{dep["markers"]}"'
+                if dep.get('extras'):
+                    new_dep += f', extras={dep["extras"]}'
+                dep = f"{{{new_dep}}}"
+            dependencies_str += f'"{name}" = {dep}\n'
+        else:
+            if isinstance(dep, dict):
+                # Requirements does not have installation markers and extras.
+                rp.installation_markers = dep.get('markers', '')
+                rp.extras_dependencies = [dep.get('extras')[0]] if dep.get('extras') else []
+            dependencies_str += f'{rp.as_pipfile_line()}\n'
+
+    toml_str = toml.dumps(pipfile_obj)
+    Path(pipfile).write_text(f'{toml_str}\n\n[packages]\n{dependencies_str}', 'UTF-8')
+
+
+if __name__ == '__main__':
+    if sys.argv[1] == 'ensure-same-version':
+        # Load dependencies from requirements files.
+        # Verify that the same version is set for the same package across files.
+        _requirements_filenames = glob.glob('taipy*/*requirements.txt')
+        _dependencies = load_dependencies(_requirements_filenames, True)
+        display_dependencies_versions(_dependencies)
+    if sys.argv[1] == 'dependencies-summary':
+        # Load and compare dependencies from requirements files.
+        # The first file is the reference to the other.
+        # Display the differences including new version available on Pypi.
+        _requirements_filenames = glob.glob('taipy*/*requirements.txt')
+        _dependencies_installed = load_dependencies([sys.argv[2]], False)
+        _dependencies_set = load_dependencies(_requirements_filenames, False)
+        update_dependencies(_dependencies_installed, _dependencies_set, _requirements_filenames)
+    if sys.argv[1] == 'generate-raw-requirements':
+        # Load dependencies from requirements files.
+        # Print the dependencies as requirements lines without born.
+        _requirements_filenames = glob.glob('taipy*/*requirements.txt')
+        _dependencies = load_dependencies(_requirements_filenames, False)
+        generate_raw_requirements_txt(_dependencies)
+    if sys.argv[1] == 'generate-pipfile':
+        # Generate a new Pipfile from requirements files using dependencies versions
+        # set in the requirement file.
+        _pipfile_path = sys.argv[2]
+        _dependencies_version = load_dependencies([sys.argv[3]], False)
+        update_pipfile(_pipfile_path, _dependencies_version)

+ 21 - 0
tools/packages/check-dependencies.sh

@@ -0,0 +1,21 @@
+# This script is used to check the latest installable requirements for each package.
+# It will generate a requirements.txt file without bound for each package.
+# Then install and dump them to check the latest installable requirements.
+# If the program detect a new package available, it print it on stdout.
+# Finally, it will generate a Pipfile with the latest version available.
+
+# Generate requirements.txt without bound.
+python check-dependencies.py generate-raw-requirements > raw-requirements.txt
+
+# Create a virtual environment, install dependencies, and freeze them
+python -m venv tmp-venv > /dev/null
+./tmp-venv/bin/python3 -m pip install -r raw-requirements.txt > /dev/null
+./tmp-venv/bin/python3 -m pip freeze > new-requirements.txt
+
+# Display dependencies summary.
+python check-dependencies.py dependencies-summary new-requirements.txt
+
+# Generate a Pipfile based on the new dependencies.
+python check-dependencies.py generate-pipfile $1 new-requirements.txt
+
+rm -f new-requirements.txt raw-requirements.txt

+ 81 - 0
tools/packages/pipfiles/Pipfile3.10.max

@@ -0,0 +1,81 @@
+[[source]]
+url = "https://pypi.org/simple"
+verify_ssl = true
+name = "pypi"
+
+[dev-packages]
+freezegun = "*"
+ipython = "*"
+ipykernel = "*"
+mkdocs = "*"
+mkdocs-autorefs = "*"
+mkdocs-include-markdown-plugin = "*"
+mkdocs-macros-plugin = "*"
+mkdocs-material = "==7.3.0"
+mkdocs-material-extensions = "*"
+mkdocstrings = "*"
+mongomock = "*"
+requests = "*"
+ruff = "*"
+pandas-stubs = "*"
+playwright = "*"
+pre-commit = "*"
+pyopenssl = "*"
+pytest = "==7.4.3"
+pytest-cov = "*"
+pytest-mock = "*"
+pytest-playwright = "*"
+pytest-timeout = "*"
+python-dotenv = "*"
+testbook = "*"
+tox = "*"
+twine = "*"
+types-flask = "*"
+types-Flask-Cors = "*"
+types-Markdown = "*"
+types-python-dateutil = "*"
+types-pytz = "*"
+types-toml = ">=0.10.0"
+types-tzlocal = "*"
+
+[requires]
+python_version = "3"
+
+[pipenv]
+allow_prereleases = true
+
+[dev-packages.moto]
+extras = [ "s3",]
+version = "==4.2.13"
+
+
+[packages]
+"pyarrow" = {version="==15.0.0"}
+"networkx" = {version="==3.2.1", markers="python_version>'3.8'"}
+"openpyxl" = {version="==3.1.2"}
+"pandas" = {version="==2.2.0", markers="python_version>'3.8'"}
+"pymongo" = {version="==4.6.1", extras=["srv"]}
+"sqlalchemy" = {version="==2.0.25"}
+"toml" = {version="==0.10.2"}
+"boto3" = {version="==1.34.25"}
+"backports.zoneinfo" = {version="==0.2.1", markers="python_version<'3.9'"}
+"cookiecutter" = {version="==2.5.0"}
+"flask" = {version="==3.0.1"}
+"flask-cors" = {version="==4.0.0"}
+"flask-socketio" = {version="==5.3.6"}
+"markdown" = {version="==3.5.2"}
+"python-dotenv" = {version="==1.0.1"}
+"pytz" = {version="==2023.3.post1"}
+"tzlocal" = {version="==5.2"}
+"gevent" = {version="==23.9.1"}
+"gevent-websocket" = {version="==0.10.1"}
+"kthread" = {version="==0.2.3"}
+"gitignore-parser" = {version="==0.1.11"}
+"simple-websocket" = {version="==1.0.0"}
+"twisted" = {version="==23.10.0"}
+"deepdiff" = {version="==6.7.1"}
+"flask-restful" = {version="==0.3.10"}
+"passlib" = {version="==1.7.4"}
+"marshmallow" = {version="==3.20.2"}
+"apispec" = {version="==6.4.0", extras=["yaml"]}
+"apispec-webframeworks" = {version="==1.0.0"}

+ 81 - 0
tools/packages/pipfiles/Pipfile3.11.max

@@ -0,0 +1,81 @@
+[[source]]
+url = "https://pypi.org/simple"
+verify_ssl = true
+name = "pypi"
+
+[dev-packages]
+freezegun = "*"
+ipython = "*"
+ipykernel = "*"
+mkdocs = "*"
+mkdocs-autorefs = "*"
+mkdocs-include-markdown-plugin = "*"
+mkdocs-macros-plugin = "*"
+mkdocs-material = "==7.3.0"
+mkdocs-material-extensions = "*"
+mkdocstrings = "*"
+mongomock = "*"
+requests = "*"
+ruff = "*"
+pandas-stubs = "*"
+playwright = "*"
+pre-commit = "*"
+pyopenssl = "*"
+pytest = "==7.4.3"
+pytest-cov = "*"
+pytest-mock = "*"
+pytest-playwright = "*"
+pytest-timeout = "*"
+python-dotenv = "*"
+testbook = "*"
+tox = "*"
+twine = "*"
+types-flask = "*"
+types-Flask-Cors = "*"
+types-Markdown = "*"
+types-python-dateutil = "*"
+types-pytz = "*"
+types-toml = ">=0.10.0"
+types-tzlocal = "*"
+
+[requires]
+python_version = "3"
+
+[pipenv]
+allow_prereleases = true
+
+[dev-packages.moto]
+extras = [ "s3",]
+version = "==4.2.13"
+
+
+[packages]
+"pyarrow" = {version="==15.0.0"}
+"networkx" = {version="==3.2.1", markers="python_version>'3.8'"}
+"openpyxl" = {version="==3.1.2"}
+"pandas" = {version="==2.2.0", markers="python_version>'3.8'"}
+"pymongo" = {version="==4.6.1", extras=["srv"]}
+"sqlalchemy" = {version="==2.0.25"}
+"toml" = {version="==0.10.2"}
+"boto3" = {version="==1.34.25"}
+"backports.zoneinfo" = {version="==0.2.1", markers="python_version<'3.9'"}
+"cookiecutter" = {version="==2.5.0"}
+"flask" = {version="==3.0.1"}
+"flask-cors" = {version="==4.0.0"}
+"flask-socketio" = {version="==5.3.6"}
+"markdown" = {version="==3.5.2"}
+"python-dotenv" = {version="==1.0.1"}
+"pytz" = {version="==2023.3.post1"}
+"tzlocal" = {version="==5.2"}
+"gevent" = {version="==23.9.1"}
+"gevent-websocket" = {version="==0.10.1"}
+"kthread" = {version="==0.2.3"}
+"gitignore-parser" = {version="==0.1.11"}
+"simple-websocket" = {version="==1.0.0"}
+"twisted" = {version="==23.10.0"}
+"deepdiff" = {version="==6.7.1"}
+"flask-restful" = {version="==0.3.10"}
+"passlib" = {version="==1.7.4"}
+"marshmallow" = {version="==3.20.2"}
+"apispec" = {version="==6.4.0", extras=["yaml"]}
+"apispec-webframeworks" = {version="==1.0.0"}

+ 81 - 0
tools/packages/pipfiles/Pipfile3.12.max

@@ -0,0 +1,81 @@
+[[source]]
+url = "https://pypi.org/simple"
+verify_ssl = true
+name = "pypi"
+
+[dev-packages]
+freezegun = "*"
+ipython = "*"
+ipykernel = "*"
+mkdocs = "*"
+mkdocs-autorefs = "*"
+mkdocs-include-markdown-plugin = "*"
+mkdocs-macros-plugin = "*"
+mkdocs-material = "==7.3.0"
+mkdocs-material-extensions = "*"
+mkdocstrings = "*"
+mongomock = "*"
+requests = "*"
+ruff = "*"
+pandas-stubs = "*"
+playwright = "*"
+pre-commit = "*"
+pyopenssl = "*"
+pytest = "==7.4.3"
+pytest-cov = "*"
+pytest-mock = "*"
+pytest-playwright = "*"
+pytest-timeout = "*"
+python-dotenv = "*"
+testbook = "*"
+tox = "*"
+twine = "*"
+types-flask = "*"
+types-Flask-Cors = "*"
+types-Markdown = "*"
+types-python-dateutil = "*"
+types-pytz = "*"
+types-toml = ">=0.10.0"
+types-tzlocal = "*"
+
+[requires]
+python_version = "3"
+
+[pipenv]
+allow_prereleases = true
+
+[dev-packages.moto]
+extras = [ "s3",]
+version = "==4.2.13"
+
+
+[packages]
+"pyarrow" = {version="==15.0.0"}
+"networkx" = {version="==3.2.1", markers="python_version>'3.8'"}
+"openpyxl" = {version="==3.1.2"}
+"pandas" = {version="==2.2.0", markers="python_version>'3.8'"}
+"pymongo" = {version="==4.6.1", extras=["srv"]}
+"sqlalchemy" = {version="==2.0.25"}
+"toml" = {version="==0.10.2"}
+"boto3" = {version="==1.34.25"}
+"backports.zoneinfo" = {version="==0.2.1", markers="python_version<'3.9'"}
+"cookiecutter" = {version="==2.5.0"}
+"flask" = {version="==3.0.1"}
+"flask-cors" = {version="==4.0.0"}
+"flask-socketio" = {version="==5.3.6"}
+"markdown" = {version="==3.5.2"}
+"python-dotenv" = {version="==1.0.1"}
+"pytz" = {version="==2023.3.post1"}
+"tzlocal" = {version="==5.2"}
+"gevent" = {version="==23.9.1"}
+"gevent-websocket" = {version="==0.10.1"}
+"kthread" = {version="==0.2.3"}
+"gitignore-parser" = {version="==0.1.11"}
+"simple-websocket" = {version="==1.0.0"}
+"twisted" = {version="==23.10.0"}
+"deepdiff" = {version="==6.7.1"}
+"flask-restful" = {version="==0.3.10"}
+"passlib" = {version="==1.7.4"}
+"marshmallow" = {version="==3.20.2"}
+"apispec" = {version="==6.4.0", extras=["yaml"]}
+"apispec-webframeworks" = {version="==1.0.0"}

+ 81 - 0
tools/packages/pipfiles/Pipfile3.8.max

@@ -0,0 +1,81 @@
+[[source]]
+url = "https://pypi.org/simple"
+verify_ssl = true
+name = "pypi"
+
+[dev-packages]
+freezegun = "*"
+ipython = "*"
+ipykernel = "*"
+mkdocs = "*"
+mkdocs-autorefs = "*"
+mkdocs-include-markdown-plugin = "*"
+mkdocs-macros-plugin = "*"
+mkdocs-material = "==7.3.0"
+mkdocs-material-extensions = "*"
+mkdocstrings = "*"
+mongomock = "*"
+requests = "*"
+ruff = "*"
+pandas-stubs = "*"
+playwright = "*"
+pre-commit = "*"
+pyopenssl = "*"
+pytest = "==7.4.3"
+pytest-cov = "*"
+pytest-mock = "*"
+pytest-playwright = "*"
+pytest-timeout = "*"
+python-dotenv = "*"
+testbook = "*"
+tox = "*"
+twine = "*"
+types-flask = "*"
+types-Flask-Cors = "*"
+types-Markdown = "*"
+types-python-dateutil = "*"
+types-pytz = "*"
+types-toml = ">=0.10.0"
+types-tzlocal = "*"
+
+[requires]
+python_version = "3"
+
+[pipenv]
+allow_prereleases = true
+
+[dev-packages.moto]
+extras = [ "s3",]
+version = "==4.2.13"
+
+
+[packages]
+"pyarrow" = {version="==15.0.0"}
+"networkx" = {version="==3.1", markers="python_version<'3.9'"}
+"openpyxl" = {version="==3.1.2"}
+"pandas" = {version="==2.0.3", markers="python_version<'3.9'"}
+"pymongo" = {version="==4.6.1", extras=["srv"]}
+"sqlalchemy" = {version="==2.0.25"}
+"toml" = {version="==0.10.2"}
+"boto3" = {version="==1.34.25"}
+"backports.zoneinfo" = {version="==0.2.1", markers="python_version<'3.9'"}
+"cookiecutter" = {version="==2.5.0"}
+"flask" = {version="==3.0.1"}
+"flask-cors" = {version="==4.0.0"}
+"flask-socketio" = {version="==5.3.6"}
+"markdown" = {version="==3.5.2"}
+"python-dotenv" = {version="==1.0.1"}
+"pytz" = {version="==2023.3.post1"}
+"tzlocal" = {version="==5.2"}
+"gevent" = {version="==23.9.1"}
+"gevent-websocket" = {version="==0.10.1"}
+"kthread" = {version="==0.2.3"}
+"gitignore-parser" = {version="==0.1.11"}
+"simple-websocket" = {version="==1.0.0"}
+"twisted" = {version="==23.10.0"}
+"deepdiff" = {version="==6.7.1"}
+"flask-restful" = {version="==0.3.10"}
+"passlib" = {version="==1.7.4"}
+"marshmallow" = {version="==3.20.2"}
+"apispec" = {version="==6.4.0", extras=["yaml"]}
+"apispec-webframeworks" = {version="==1.0.0"}

+ 81 - 0
tools/packages/pipfiles/Pipfile3.9.max

@@ -0,0 +1,81 @@
+[[source]]
+url = "https://pypi.org/simple"
+verify_ssl = true
+name = "pypi"
+
+[dev-packages]
+freezegun = "*"
+ipython = "*"
+ipykernel = "*"
+mkdocs = "*"
+mkdocs-autorefs = "*"
+mkdocs-include-markdown-plugin = "*"
+mkdocs-macros-plugin = "*"
+mkdocs-material = "==7.3.0"
+mkdocs-material-extensions = "*"
+mkdocstrings = "*"
+mongomock = "*"
+requests = "*"
+ruff = "*"
+pandas-stubs = "*"
+playwright = "*"
+pre-commit = "*"
+pyopenssl = "*"
+pytest = "==7.4.3"
+pytest-cov = "*"
+pytest-mock = "*"
+pytest-playwright = "*"
+pytest-timeout = "*"
+python-dotenv = "*"
+testbook = "*"
+tox = "*"
+twine = "*"
+types-flask = "*"
+types-Flask-Cors = "*"
+types-Markdown = "*"
+types-python-dateutil = "*"
+types-pytz = "*"
+types-toml = ">=0.10.0"
+types-tzlocal = "*"
+
+[requires]
+python_version = "3"
+
+[pipenv]
+allow_prereleases = true
+
+[dev-packages.moto]
+extras = [ "s3",]
+version = "==4.2.13"
+
+
+[packages]
+"pyarrow" = {version="==15.0.0"}
+"networkx" = {version="==3.2.1", markers="python_version>'3.8'"}
+"openpyxl" = {version="==3.1.2"}
+"pandas" = {version="==2.2.0", markers="python_version>'3.8'"}
+"pymongo" = {version="==4.6.1", extras=["srv"]}
+"sqlalchemy" = {version="==2.0.25"}
+"toml" = {version="==0.10.2"}
+"boto3" = {version="==1.34.25"}
+"backports.zoneinfo" = {version="==0.2.1", markers="python_version<'3.9'"}
+"cookiecutter" = {version="==2.5.0"}
+"flask" = {version="==3.0.1"}
+"flask-cors" = {version="==4.0.0"}
+"flask-socketio" = {version="==5.3.6"}
+"markdown" = {version="==3.5.2"}
+"python-dotenv" = {version="==1.0.1"}
+"pytz" = {version="==2023.3.post1"}
+"tzlocal" = {version="==5.2"}
+"gevent" = {version="==23.9.1"}
+"gevent-websocket" = {version="==0.10.1"}
+"kthread" = {version="==0.2.3"}
+"gitignore-parser" = {version="==0.1.11"}
+"simple-websocket" = {version="==1.0.0"}
+"twisted" = {version="==23.10.0"}
+"deepdiff" = {version="==6.7.1"}
+"flask-restful" = {version="==0.3.10"}
+"passlib" = {version="==1.7.4"}
+"marshmallow" = {version="==3.20.2"}
+"apispec" = {version="==6.4.0", extras=["yaml"]}
+"apispec-webframeworks" = {version="==1.0.0"}

+ 3 - 0
tools/packages/requirements.txt

@@ -0,0 +1,3 @@
+tabulate
+toml
+requests

+ 2 - 2
tools/packages/taipy-config/setup.requirements.txt

@@ -1,2 +1,2 @@
-toml>=0.10,<0.11
-deepdiff>=6.2,<6.3
+deepdiff>=6.2.2,<=6.7.1
+toml>=0.10,<=0.10.2

+ 8 - 8
tools/packages/taipy-core/setup.requirements.txt

@@ -1,9 +1,9 @@
-pyarrow>=14.0.2,<15.0
-networkx>=2.6,<3.0
-openpyxl>=3.1.2,<3.2
-pandas>=2.0.0,<3.0
-pymongo[srv]>=4.2.0,<5.0
-sqlalchemy>=2.0.16,<2.1
-toml>=0.10,<0.11
-boto3>=1.29.4,<1.33.12
+boto3>=1.29.4,<=1.34.25
+networkx>=2.6,<=3.2.1
+openpyxl>=3.1.2,<=3.1.2
+pandas>=2.0.0,<=2.2.0
+pyarrow>=14.0.2,<=15.0.0
+pymongo[srv]>=4.2.0,<=4.6.1
+sqlalchemy>=2.0.16,<=2.0.25
 taipy-config
+toml>=0.10,<=0.10.2

+ 15 - 15
tools/packages/taipy-gui/setup.requirements.txt

@@ -1,16 +1,16 @@
-flask>=3.0.0,<3.1
-flask-cors>=4.0.0,<5.0
-flask-socketio>=5.3.6,<6.0
-markdown>=3.4.4,<4.0
-pandas>=2.0.0,<3.0
-python-dotenv>=1.0.0,<1.1
-pytz>=2021.3,<2022.2
-tzlocal>=3.0,<5.0
-backports.zoneinfo>=0.2.1,<0.3;python_version<'3.9'
-gevent>=23.7.0,<24.0
-gevent-websocket>=0.10.1,<0.11
-kthread>=0.2.3,<0.3
+backports.zoneinfo>=0.2.1,<=0.2.1;python_version<'3.9'
+flask>=3.0.0,<=3.0.1
+flask-cors>=4.0.0,<=4.0.0
+flask-socketio>=5.3.6,<=5.3.6
+gevent>=23.7.0,<=23.9.1
+gevent-websocket>=0.10.1,<=0.10.1
+gitignore-parser>=0.1,<=0.1.11
+kthread>=0.2.3,<=0.2.3
+markdown>=3.4.4,<=3.5.2
+pandas>=2.0.0,<=2.2.0
+python-dotenv>=1.0.0,<=1.0.1
+pytz>=2021.3,<=2023.3.post1
+simple-websocket>=0.10.1,<=1.0.0
 taipy-config
-gitignore-parser>=0.1,<0.2
-simple-websocket>=0.10.1,<1.0
-twisted>=23.8.0,<24.0
+twisted>=23.8.0,<=23.10.0
+tzlocal>=3.0,<=5.2

+ 6 - 6
tools/packages/taipy-rest/setup.requirements.txt

@@ -1,7 +1,7 @@
-flask>=3.0.0,<3.1
-flask-restful>=0.3.9,<0.4
-passlib>=1.7.4,<1.8
-marshmallow>=3.20.1,<3.30
-apispec[yaml]>=6.3,<7.0
-apispec-webframeworks>=0.5.2,<0.6
+apispec[yaml]>=6.3,<=6.4.0
+apispec-webframeworks>=0.5.2,<=1.0.0
+flask>=3.0.0,<=3.0.1
+flask-restful>=0.3.9,<=0.3.10
+marshmallow>=3.20.1,<=3.20.2
+passlib>=1.7.4,<=1.7.4
 taipy-core

+ 1 - 0
tools/packages/taipy-templates/setup.requirements.txt

@@ -0,0 +1 @@
+

+ 2 - 2
tools/packages/taipy/setup.requirements.txt

@@ -1,5 +1,5 @@
-backports.zoneinfo>=0.2.1,<0.3;python_version<'3.9'
-cookiecutter>=2.1.1,<2.2
+backports.zoneinfo>=0.2.1,<=0.2.1;python_version<'3.9'
+cookiecutter>=2.1.1,<=2.5.0
 taipy-gui
 taipy-rest
 taipy-templates