Browse Source

Merge branch '1297-value-format' of github.com:Avaiga/taipy into 1297-value-format

namnguyen 11 months ago
parent
commit
32b06042cb

+ 2 - 2
.github/workflows/dependencies-management.yml

@@ -12,8 +12,8 @@ name: Dependencies management
 
 on:
   schedule:
-    # Run each Sunday at mid day
-    - cron: 00 12 * * 0
+    # Run every 2 weeks on Sunday at mid day UTC
+    - cron: 00 12 */14 * 0
 
   workflow_dispatch:
 

+ 1 - 1
Pipfile

@@ -10,7 +10,7 @@ apispec-webframeworks = "==0.5.2"
 cookiecutter = "==2.1.1"
 deepdiff = "==6.7.1"
 flask = "==3.0.0"
-flask-cors = "==4.0.0"
+flask-cors = "==4.0.1"
 flask-socketio = "==5.3.6"
 Flask-RESTful = ">=0.3.9"
 gevent = "==23.7.0"

+ 18 - 4
taipy/_cli/_scaffold_cli.py

@@ -11,6 +11,7 @@
 
 import pathlib
 import sys
+from typing import Dict, Optional
 
 from cookiecutter.exceptions import OutputDirExistsException
 from cookiecutter.main import cookiecutter
@@ -22,12 +23,25 @@ from ._base_cli._taipy_parser import _TaipyParser
 
 
 class _ScaffoldCLI(_AbstractCLI):
-    __TAIPY_PATH = pathlib.Path(taipy.__file__).parent.resolve() / "templates"
-    _TEMPLATE_MAP = {str(x.name): str(x) for x in __TAIPY_PATH.iterdir() if x.is_dir() and not x.name.startswith("_")}
+    _template_map: Dict[str, str] = {}
 
     _COMMAND_NAME = "create"
     _ARGUMENTS = ["--template"]
 
+    @classmethod
+    def generate_template_map(cls, template_path: Optional[pathlib.Path] = None):
+        if not template_path:
+            template_path = pathlib.Path(taipy.__file__).parent.resolve() / "templates"
+
+        # Update the template map with the new templates but do not override the existing ones
+        cls._template_map.update(
+            {
+                str(x.name): str(x)
+                for x in template_path.iterdir()
+                if x.is_dir() and not x.name.startswith("_") and x.name not in cls._template_map
+            }
+        )
+
     @classmethod
     def create_parser(cls):
         create_parser = _TaipyParser._add_subparser(
@@ -36,7 +50,7 @@ class _ScaffoldCLI(_AbstractCLI):
         )
         create_parser.add_argument(
             "--template",
-            choices=list(cls._TEMPLATE_MAP.keys()),
+            choices=list(cls._template_map.keys()),
             default="default",
             help="The Taipy template to create new application.",
         )
@@ -47,7 +61,7 @@ class _ScaffoldCLI(_AbstractCLI):
         if not args:
             return
         try:
-            cookiecutter(cls._TEMPLATE_MAP[args.template])
+            cookiecutter(cls._template_map[args.template])
         except OutputDirExistsException as err:
             error_msg = f"{str(err)}. Please remove the existing directory or provide a new folder name."
             print(error_msg)  # noqa: T201

+ 8 - 2
taipy/_entrypoint.py

@@ -36,19 +36,25 @@ def _entrypoint():
         help="Print the current Taipy version and exit.",
     )
 
+    if find_spec("taipy.enterprise"):
+        from taipy.enterprise._entrypoint import _entrypoint_initialize as _enterprise_entrypoint_initialize
+
+        _enterprise_entrypoint_initialize()
+
     _RunCLI.create_parser()
     _GuiCLI.create_run_parser()
     _CoreCLI.create_run_parser()
 
     _VersionCLI.create_parser()
+    _ScaffoldCLI.generate_template_map()
     _ScaffoldCLI.create_parser()
     _MigrateCLI.create_parser()
     _HelpCLI.create_parser()
 
     if find_spec("taipy.enterprise"):
-        from taipy.enterprise._entrypoint import _entrypoint as _enterprise_entrypoint
+        from taipy.enterprise._entrypoint import _entrypoint_handling as _enterprise_entrypoint_handling
 
-        _enterprise_entrypoint()
+        _enterprise_entrypoint_handling()
 
     args, _ = _TaipyParser._parser.parse_known_args()
     if args.version:

+ 20 - 17
taipy/config/common/scope.py

@@ -43,33 +43,36 @@ class Scope(_OrderedEnum):
     - `CYCLE`
     - `SCENARIO` (Default value)
 
-    Each data node config has a scope. It is an attribute propagated to the `DataNode^` when instantiated from
-    a `DataNodeConfig^`. The scope is used to determine the _visibility_ of the data node, and which scenarios can
-    access it.
+    Each data node config has a scope. It is an attribute propagated to the `DataNode^`
+    when instantiated from a `DataNodeConfig^`. The scope is used to determine the
+    _visibility_ of the data node, and which scenarios can access it.
 
     In other words :
 
-    - There can be only one data node instantiated from a `DataNodeConfig^` with a `GLOBAL` scope. All the
-        scenarios share the unique data node. When a new scenario is created, the data node is also created if
-        and only if it does not exist yet.
-    - Only one data node instantiated from a `DataNodeConfig^` with a `CYCLE` scope is created for each cycle.
-        All the scenarios of the same cycle share the same data node. When a new scenario is created within a
-        cycle, Taipy instantiates a new data node if and only if there is no data node for the cycle yet.
-    - A data node that has the scope set to `SCENARIO` belongs to a unique scenario and cannot be used by others
-        When creating a new scenario, data nodes with a `SCENARIO` scope are systematically created along with
-        the new scenario.
+    - There can be only one data node instantiated from a `DataNodeConfig^` with a `GLOBAL`
+        scope. All the scenarios share the unique data node. When a new scenario is created,
+        the data node is also created if and only if it does not exist yet.
+    - Only one data node instantiated from a `DataNodeConfig^` with a `CYCLE` scope is
+        created for each cycle. All the scenarios of the same cycle share the same data node.
+        When a new scenario is created within a cycle, Taipy instantiates a new data node if
+        and only if there is no data node for the cycle yet.
+    - A data node that has the scope set to `SCENARIO` belongs to a unique scenario and cannot
+        be used by others. When creating a new scenario, data nodes with a `SCENARIO` scope
+        are systematically created along with the new scenario.
 
     !!! example
 
-        Let's consider a simple example where a company wants to predict its sales for the next month. The company
-        has a trained model that predicts the sales based on the current month and the historical sales. Based on
-        the sales forecasts the company wants to plan its production orders. The company wants to simulate two
-        scenarios every month: one with low capacity and one with high capacity.
+        Let's consider a simple example where a company wants to predict its sales for the next
+        month. The company has a trained model that predicts the sales based on the current month
+        and the historical sales. Based on the sales forecasts the company wants to plan its
+        production orders. The company wants to simulate two scenarios every month: one with
+        low capacity and one with high capacity.
 
         We can create the `DataNodeConfig^`s with the following scopes:
 
         - One data node for the historical sales with a `GLOBAL` scope.
-        - Three data nodes with a `CYCLE` scope, for the trained model, the current month, and the sales predictions.
+        - Three data nodes with a `CYCLE` scope, for the trained model, the current month,
+            and the sales predictions.
         - Two data nodes with a `SCENARIO` scope, for the capacity and the production orders.
 
         The code snippet below shows how to configure the data nodes with the different scopes:

+ 61 - 0
taipy/core/cycle/cycle.py

@@ -28,6 +28,31 @@ from .cycle_id import CycleId
 class Cycle(_Entity, _Labeled):
     """An iteration of a recurrent work pattern.
 
+    Many business operations are periodic, such as weekly predictions of sales data, monthly
+    master planning of supply chains, quarterly financial reports, yearly budgeting, etc.
+    The data applications to solve these business problems often require modeling the
+    corresponding periods (i.e., cycles).
+
+    For this purpose, a `Cycle^` represents a single iteration of such a time pattern.
+    Each _cycle_ has a start date and a duration. Examples of cycles are:
+
+    - Monday, 2. January 2023 as a daily cycle
+    - Week 01 2023, from 2. January as a weekly cycle
+    - January 2023 as a monthly cycle
+    - etc.
+
+    `Cycle^`s are created along with the `Scenario^`s that are attached to them.
+    At its creation, a new scenario is attached to a single cycle, the one that
+    matches its optional _frequency_ and its _creation_date_.
+
+    The possible frequencies are:
+
+    - `Frequency.DAILY`
+    - `Frequency.WEEKLY`
+    - `Frequency.MONTHLY`
+    - `Frequency.QUARTERLY`
+    - `Frequency.YEARLY`
+
     Attributes:
         id (str): The unique identifier of the cycle.
         frequency (Frequency^): The frequency of this cycle.
@@ -36,6 +61,42 @@ class Cycle(_Entity, _Labeled):
         end_date (datetime): The date and time of the end of this cycle.
         name (str): The name of this cycle.
         properties (dict[str, Any]): A dictionary of additional properties.
+
+    !!! example "Example for January cycle"
+
+        ![cycles](../refmans/img/cycles_january_colored.svg){ align=left width="250" }
+
+        Let's assume an end-user publishes production orders (i.e., a production plan) every
+        month. During each month (the cycle), he/she will be interested in experimenting with
+        different scenarios until only one of those scenarios is selected as the official
+        production plan to be published. Each month is modeled as a cycle, and each cycle
+        can contain one or more scenarios.
+
+        The picture on the left shows the tree of entities: Cycles, Scenarios, and their
+        associated Sequence(s). There is an existing past cycle for December and a current
+        cycle for January containing a single scenario.
+
+    When comes the end of a _cycle_ (start date + duration), only one of the scenarios is
+    applied in production. This "official" scenario is called the _**primary scenario**_.
+    Only one _**primary scenario**_ per cycle is allowed.
+
+    !!! example "Example for February cycle"
+
+        ![cycles](../pic/cycles_colored.svg){ align=left width="250" }
+        Now the user starts working on the February work cycle. He or she creates two
+        scenarios for the February cycle (one with a low capacity assumption and one with
+        a high capacity assumption). The user can then decide to elect the low capacity
+        scenario as the "official" scenario for February. To accomplish that, he just
+        needs to promote the low capacity scenario as _**primary**_ for the February cycle.
+
+        The tree of entities resulting from the various scenarios created is represented
+        in the picture on the left. The underlined scenarios are _**primary**_.
+
+    !!! note
+
+        For a scenario, cycles are optional. If a scenario has no Frequency, it will not be
+        attached to any cycle.
+
     """
 
     _ID_PREFIX = "CYCLE"

+ 2 - 0
taipy/core/data/data_node.py

@@ -75,6 +75,8 @@ class DataNode(_Entity, _Labeled):
             and use the `create_global_data_node()^` function as illustrated in the following
             example.
 
+    A data node's attributes are populated based on its configuration `DataNodeConfig^`.
+
     !!! Example
 
         ```python

+ 14 - 5
taipy/core/job/job.py

@@ -42,8 +42,16 @@ def _run_callbacks(fn):
 class Job(_Entity, _Labeled):
     """Execution of a `Task^`.
 
-    A job handles the status of the execution, contains the stacktrace of exceptions that were
-    raised during the execution, and notifies subscribers on status change.
+    Task, Sequence, and Scenario entities can be submitted for execution. The submission
+    of a scenario triggers the submission of all the contained tasks. Similarly, the submission
+    of a sequence also triggers the execution of all the ordered tasks.
+
+    Every time a task is submitted for execution, a new *Job* is created. A job represents a
+    single execution of a task. It holds all the information related to the task execution,
+    including the **creation date**, the execution `Status^`, and the **stacktrace** of any
+    exception that may be raised by the user function.
+
+    In addition, a job notifies scenario or sequence subscribers on its status change.
 
     Attributes:
         id (str): The identifier of this job.
@@ -52,9 +60,10 @@ class Job(_Entity, _Labeled):
             not.
         status (Status^): The current status of this job.
         creation_date (datetime): The date of this job's creation.
-        stacktrace (List[str]): The list of stacktraces of the exceptions raised during the execution.
-        version (str): The string indicates the application version of the job to instantiate. If not provided,
-            the latest version is used.
+        stacktrace (List[str]): The list of stacktraces of the exceptions raised during the
+            execution.
+        version (str): The string indicates the application version of the job to instantiate.
+            If not provided, the latest version is used.
     """
 
     _MANAGER_NAME = "job"

+ 73 - 2
taipy/core/sequence/sequence.py

@@ -37,8 +37,79 @@ from .sequence_id import SequenceId
 
 
 class Sequence(_Entity, Submittable, _Labeled):
-    """List of `Task^`s and additional attributes representing a set of data processing
-    elements connected as a direct acyclic graph.
+    """A subset of scenario tasks grouped to be executed together independently of the others.
+
+    A sequence is attached to a `Scenario^`. It represents a subset of its tasks that need to
+    be executed together, independently of the other tasks in the scenario. They must form a
+    connected subgraph of the scenario's task graph. A scenario can hold multiple sequences.
+
+    For instance, in a typical machine learning scenario, we may have several sequences:
+    a sequence dedicated to preprocessing and preparing data, a sequence for computing a
+    training model, and a sequence dedicated to scoring.
+
+    !!! Example
+
+        Let's assume we have a scenario configuration modelling a manufacturer that is
+        training an ML model, predicting sales forecasts, and finally, based on
+        the forecasts, planning its production. Three task are configured and linked
+        together through data nodes.
+
+        ![sequences](../refmans/img/sequences.svg){ align=left }
+
+        First, the sales sequence (boxed in green in the picture) contains **training**
+        and **predict** tasks. Second, a production sequence (boxed in dark gray in the
+        picture) contains the **planning** task.
+
+        This problem has been modeled in two sequences - one sequence for the forecasting
+        part and one for the production planning part. As a consequence, the two algorithms
+        can have two different life cycles. They can run independently, under different
+        schedules. For example, one on a fixed schedule (e.g. every week) and one on demand,
+        interactively triggered by end-users.
+
+        ```python
+        import taipy as tp
+        from taipy import Config
+
+        def training(history):
+            ...
+
+        def predict(model, month):
+            ...
+
+        def planning(forecast, capacity):
+            ...
+
+        # Configure data nodes
+        sales_history_cfg = Config.configure_csv_data_node("sales_history")
+        trained_model_cfg = Config.configure_data_node("trained_model")
+        current_month_cfg = Config.configure_data_node("current_month")
+        forecasts_cfg = Config.configure_data_node("sales_predictions")
+        capacity_cfg = Config.configure_data_node("capacity")
+        production_orders_cfg = Config.configure_sql_data_node("production_orders")
+
+        # Configure tasks and scenarios
+        train_cfg = Config.configure_task("train", function=training, input=sales_history_cfg, output=trained_model_cfg)
+        predict_cfg = Config.configure_task("predict", function=predict,
+                                            input=[trained_model_cfg, current_month_cfg],
+                                            output=forecasts_cfg)
+        plan_cfg = Config.configure_task("planning", function=planning,
+                                         input=[forecasts_cfg, capacity_cfg],
+                                         output=production_orders_cfg)
+        scenario_cfg = Config.configure_scenario("scenario", task_configs=[train_cfg, predict_cfg, plan_cfg])
+
+        # Create a new scenario and sequences
+        scenario = tp.create_scenario(scenario_cfg)
+        scenario.add_sequence("sales_sequence", [train_cfg, predict_cfg])
+        scenario.add_sequence("production_sequence", [plan_cfg])
+
+        # Get all sequences
+        all_sequences = tp.get_sequences()
+
+        # Submit one sequence only
+        tp.submit(scenario.sales_sequence)
+        ```
+
+    Note that the sequences are not necessarily disjoint and may share some tasks.
 
     Attributes:
         properties (dict[str, Any]): A dictionary of additional properties.

+ 36 - 1
taipy/core/submission/submission.py

@@ -26,7 +26,14 @@ from .submission_status import SubmissionStatus
 
 
 class Submission(_Entity, _Labeled):
-    """Hold the jobs and submission status when a Scenario^, Sequence^ or Task^ is submitted.
+    """ Submission of a submittable entity: `Task^`, a `Sequence^` or a `Scenario^`.
+
+    Task, Sequence, and Scenario entities can be submitted for execution. The submission
+    represents the unique request to execute a submittable entity. The submission is created
+    at the time the entity is submitted.
+
+    The submission holds the jobs created by the execution of the submittable and the
+    `SubmissionStatus^`. The status is lively updated by Taipy during the execution of the jobs.
 
     Attributes:
         entity_id (str): The identifier of the entity that was submitted.
@@ -37,6 +44,34 @@ class Submission(_Entity, _Labeled):
         submission_status (Optional[SubmissionStatus]): The current status of this submission.
         version (Optional[str]): The string indicates the application version of the submission to instantiate.
             If not provided, the latest version is used.
+
+    !!! example
+
+        ```python
+        import taipy as tp
+        from taipy import Config
+
+        def by_two(x: int):
+            return x * 2
+
+        # Configure scenarios
+        input_cfg = Config.configure_data_node("my_input")
+        result_cfg = Config.configure_data_node("my_result")
+        task_cfg = Config.configure_task("my_double", function=by_two, input=input_cfg, output=result_cfg)
+        scenario_cfg = Config.configure_scenario("my_scenario", task_configs=[task_cfg])
+
+        # Create a new scenario from the configuration
+        scenario = tp.create_scenario(scenario_cfg)
+
+        # Write the input data and submit the scenario
+        scenario.my_input.write(3)
+        submission = scenario.submit()
+
+        # Retrieve the list of jobs, the submission status, and the creation date
+        jobs = submission.jobs
+        status = submission.submission_status
+        creation_date = submission.creation_date
+        ```
     """
 
     _ID_PREFIX = "SUBMISSION"

+ 5 - 2
taipy/core/task/task.py

@@ -30,8 +30,8 @@ from .task_id import TaskId
 class Task(_Entity, _Labeled):
     """Hold a user function that will be executed, its parameters and the results.
 
-    A `Task` brings together the user code as function, the inputs and the outputs as data nodes
-    (instances of the `DataNode^` class).
+    A `Task` brings together the user code as function, the inputs and the outputs
+    as data nodes (instances of the `DataNode^` class).
 
     !!! note
         It is not recommended to instantiate a `Task` directly. Instead, it should be
@@ -39,6 +39,9 @@ class Task(_Entity, _Labeled):
         the related data nodes and tasks are created automatically. Please refer to
         the `Scenario^` class for more information.
 
+    A task's attributes (the input data nodes, the output data nodes, the Python
+    function) are populated based on its task configuration `TaskConfig^`.
+
     !!! Example
 
         ```python

+ 3 - 2
taipy/templates/default/hooks/post_gen_project.py

@@ -42,7 +42,8 @@ def handle_services(use_rest, use_core):
         # Create and submit the placeholder scenario
         with open(os.path.join(os.getcwd(), "sections", "main.txt"), "a") as main_file:
             main_file.write("    core = Core()\n")
-            main_file.write("    core.run()\n")
+            if not use_rest:
+                main_file.write("    core.run()\n")
             main_file.write("    # #############################################################################\n")
             main_file.write("    # PLACEHOLDER: Create and submit your scenario here                           #\n")
             main_file.write("    #                                                                             #\n")
@@ -111,7 +112,7 @@ def handle_multi_page_app(pages):
 
     shutil.rmtree(os.path.join(os.getcwd(), "pages", "page_example"))
 
-    newline = ",\n\t"
+    newline = ",\n    "
     user_page_dict = newline.join(f'"{page_name}": {page_name}' for page_name in pages)
     page_dict = """
 pages = {

+ 1 - 1
tests/templates/test_default_template.py

@@ -119,7 +119,7 @@ def test_with_both_core_rest_services(tmpdir):
         output_dir=str(tmpdir),
         no_input=True,
         extra_context={
-            "Does the application use scenario management or version management?": "n",
+            "Does the application use scenario management or version management?": "y",
             "Does the application use Rest API?": "yes",
         },
     )

+ 7 - 7
tools/packages/pipfiles/Pipfile3.10.max

@@ -50,18 +50,18 @@ version = "==4.2.13"
 
 
 [packages]
-"pyarrow" = {version="==16.0.0"}
+"pyarrow" = {version="==16.1.0"}
 "networkx" = {version="==3.3", markers="python_version>'3.8'"}
 "openpyxl" = {version="==3.1.2"}
 "pandas" = {version="==2.2.2", markers="python_version>'3.8'"}
-"pymongo" = {version="==4.7.0", extras=["srv"]}
-"sqlalchemy" = {version="==2.0.29"}
+"pymongo" = {version="==4.7.2", extras=["srv"]}
+"sqlalchemy" = {version="==2.0.30"}
 "toml" = {version="==0.10.2"}
-"boto3" = {version="==1.34.93"}
+"boto3" = {version="==1.34.113"}
 "backports.zoneinfo" = {version="==0.2.1", markers="python_version<'3.9'"}
 "cookiecutter" = {version="==2.6.0"}
 "flask" = {version="==3.0.3"}
-"flask-cors" = {version="==4.0.0"}
+"flask-cors" = {version="==4.0.1"}
 "flask-socketio" = {version="==5.3.6"}
 "markdown" = {version="==3.6"}
 "python-dotenv" = {version="==1.0.1"}
@@ -76,8 +76,8 @@ version = "==4.2.13"
 "deepdiff" = {version="==7.0.1"}
 "flask-restful" = {version="==0.3.10"}
 "passlib" = {version="==1.7.4"}
-"marshmallow" = {version="==3.21.1"}
+"marshmallow" = {version="==3.21.2"}
 "apispec" = {version="==6.6.1", extras=["yaml"]}
 "apispec-webframeworks" = {version="==1.1.0"}
-"watchdog" = {version="==4.0.0"}
+"watchdog" = {version="==4.0.1"}
 "charset-normalizer" = {version="==3.3.2"}

+ 7 - 7
tools/packages/pipfiles/Pipfile3.11.max

@@ -50,18 +50,18 @@ version = "==4.2.13"
 
 
 [packages]
-"pyarrow" = {version="==16.0.0"}
+"pyarrow" = {version="==16.1.0"}
 "networkx" = {version="==3.3", markers="python_version>'3.8'"}
 "openpyxl" = {version="==3.1.2"}
 "pandas" = {version="==2.2.2", markers="python_version>'3.8'"}
-"pymongo" = {version="==4.7.0", extras=["srv"]}
-"sqlalchemy" = {version="==2.0.29"}
+"pymongo" = {version="==4.7.2", extras=["srv"]}
+"sqlalchemy" = {version="==2.0.30"}
 "toml" = {version="==0.10.2"}
-"boto3" = {version="==1.34.93"}
+"boto3" = {version="==1.34.113"}
 "backports.zoneinfo" = {version="==0.2.1", markers="python_version<'3.9'"}
 "cookiecutter" = {version="==2.6.0"}
 "flask" = {version="==3.0.3"}
-"flask-cors" = {version="==4.0.0"}
+"flask-cors" = {version="==4.0.1"}
 "flask-socketio" = {version="==5.3.6"}
 "markdown" = {version="==3.6"}
 "python-dotenv" = {version="==1.0.1"}
@@ -76,8 +76,8 @@ version = "==4.2.13"
 "deepdiff" = {version="==7.0.1"}
 "flask-restful" = {version="==0.3.10"}
 "passlib" = {version="==1.7.4"}
-"marshmallow" = {version="==3.21.1"}
+"marshmallow" = {version="==3.21.2"}
 "apispec" = {version="==6.6.1", extras=["yaml"]}
 "apispec-webframeworks" = {version="==1.1.0"}
-"watchdog" = {version="==4.0.0"}
+"watchdog" = {version="==4.0.1"}
 "charset-normalizer" = {version="==3.3.2"}

+ 7 - 7
tools/packages/pipfiles/Pipfile3.12.max

@@ -50,18 +50,18 @@ version = "==4.2.13"
 
 
 [packages]
-"pyarrow" = {version="==16.0.0"}
+"pyarrow" = {version="==16.1.0"}
 "networkx" = {version="==3.3", markers="python_version>'3.8'"}
 "openpyxl" = {version="==3.1.2"}
 "pandas" = {version="==2.2.2", markers="python_version>'3.8'"}
-"pymongo" = {version="==4.7.0", extras=["srv"]}
-"sqlalchemy" = {version="==2.0.29"}
+"pymongo" = {version="==4.7.2", extras=["srv"]}
+"sqlalchemy" = {version="==2.0.30"}
 "toml" = {version="==0.10.2"}
-"boto3" = {version="==1.34.93"}
+"boto3" = {version="==1.34.113"}
 "backports.zoneinfo" = {version="==0.2.1", markers="python_version<'3.9'"}
 "cookiecutter" = {version="==2.6.0"}
 "flask" = {version="==3.0.3"}
-"flask-cors" = {version="==4.0.0"}
+"flask-cors" = {version="==4.0.1"}
 "flask-socketio" = {version="==5.3.6"}
 "markdown" = {version="==3.6"}
 "python-dotenv" = {version="==1.0.1"}
@@ -76,8 +76,8 @@ version = "==4.2.13"
 "deepdiff" = {version="==7.0.1"}
 "flask-restful" = {version="==0.3.10"}
 "passlib" = {version="==1.7.4"}
-"marshmallow" = {version="==3.21.1"}
+"marshmallow" = {version="==3.21.2"}
 "apispec" = {version="==6.6.1", extras=["yaml"]}
 "apispec-webframeworks" = {version="==1.1.0"}
-"watchdog" = {version="==4.0.0"}
+"watchdog" = {version="==4.0.1"}
 "charset-normalizer" = {version="==3.3.2"}

+ 7 - 7
tools/packages/pipfiles/Pipfile3.8.max

@@ -50,18 +50,18 @@ version = "==4.2.13"
 
 
 [packages]
-"pyarrow" = {version="==16.0.0"}
+"pyarrow" = {version="==16.1.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.7.0", extras=["srv"]}
-"sqlalchemy" = {version="==2.0.29"}
+"pymongo" = {version="==4.7.2", extras=["srv"]}
+"sqlalchemy" = {version="==2.0.30"}
 "toml" = {version="==0.10.2"}
-"boto3" = {version="==1.34.93"}
+"boto3" = {version="==1.34.113"}
 "backports.zoneinfo" = {version="==0.2.1", markers="python_version<'3.9'"}
 "cookiecutter" = {version="==2.6.0"}
 "flask" = {version="==3.0.3"}
-"flask-cors" = {version="==4.0.0"}
+"flask-cors" = {version="==4.0.1"}
 "flask-socketio" = {version="==5.3.6"}
 "markdown" = {version="==3.6"}
 "python-dotenv" = {version="==1.0.1"}
@@ -76,8 +76,8 @@ version = "==4.2.13"
 "deepdiff" = {version="==7.0.1"}
 "flask-restful" = {version="==0.3.10"}
 "passlib" = {version="==1.7.4"}
-"marshmallow" = {version="==3.21.1"}
+"marshmallow" = {version="==3.21.2"}
 "apispec" = {version="==6.6.1", extras=["yaml"]}
 "apispec-webframeworks" = {version="==1.1.0"}
-"watchdog" = {version="==4.0.0"}
+"watchdog" = {version="==4.0.1"}
 "charset-normalizer" = {version="==3.3.2"}

+ 7 - 7
tools/packages/pipfiles/Pipfile3.9.max

@@ -50,18 +50,18 @@ version = "==4.2.13"
 
 
 [packages]
-"pyarrow" = {version="==16.0.0"}
+"pyarrow" = {version="==16.1.0"}
 "networkx" = {version="==3.2.1", markers="python_version>'3.8'"}
 "openpyxl" = {version="==3.1.2"}
 "pandas" = {version="==2.2.2", markers="python_version>'3.8'"}
-"pymongo" = {version="==4.7.0", extras=["srv"]}
-"sqlalchemy" = {version="==2.0.29"}
+"pymongo" = {version="==4.7.2", extras=["srv"]}
+"sqlalchemy" = {version="==2.0.30"}
 "toml" = {version="==0.10.2"}
-"boto3" = {version="==1.34.93"}
+"boto3" = {version="==1.34.113"}
 "backports.zoneinfo" = {version="==0.2.1", markers="python_version<'3.9'"}
 "cookiecutter" = {version="==2.6.0"}
 "flask" = {version="==3.0.3"}
-"flask-cors" = {version="==4.0.0"}
+"flask-cors" = {version="==4.0.1"}
 "flask-socketio" = {version="==5.3.6"}
 "markdown" = {version="==3.6"}
 "python-dotenv" = {version="==1.0.1"}
@@ -76,8 +76,8 @@ version = "==4.2.13"
 "deepdiff" = {version="==7.0.1"}
 "flask-restful" = {version="==0.3.10"}
 "passlib" = {version="==1.7.4"}
-"marshmallow" = {version="==3.21.1"}
+"marshmallow" = {version="==3.21.2"}
 "apispec" = {version="==6.6.1", extras=["yaml"]}
 "apispec-webframeworks" = {version="==1.1.0"}
-"watchdog" = {version="==4.0.0"}
+"watchdog" = {version="==4.0.1"}
 "charset-normalizer" = {version="==3.3.2"}

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

@@ -1,9 +1,9 @@
-boto3>=1.29.4,<=1.34.93
+boto3>=1.29.4,<=1.34.113
 networkx>=2.6,<=3.3
 openpyxl>=3.1.2,<=3.1.2
 pandas>=1.3.5,<=2.2.2
-pyarrow>=14.0.2,<=16.0.0
-pymongo[srv]>=4.2.0,<=4.7.0
-sqlalchemy>=2.0.16,<=2.0.29
+pyarrow>=14.0.2,<=16.1.0
+pymongo[srv]>=4.2.0,<=4.7.2
+sqlalchemy>=2.0.16,<=2.0.30
 taipy-config
 toml>=0.10,<=0.10.2

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

@@ -1,7 +1,7 @@
 backports.zoneinfo>=0.2.1,<=0.2.1;python_version<'3.9'
 charset-normalizer>=3.3.2,<=3.3.2
 flask>=3.0.0,<=3.0.3
-flask-cors>=4.0.0,<=4.0.0
+flask-cors>=4.0.0,<=4.0.1
 flask-socketio>=5.3.6,<=5.3.6
 gevent>=23.7.0,<=24.2.1
 gevent-websocket>=0.10.1,<=0.10.1
@@ -15,4 +15,4 @@ simple-websocket>=0.10.1,<=1.0.0
 taipy-config
 twisted>=23.8.0,<=24.3.0
 tzlocal>=3.0,<=5.2
-watchdog>=4.0.0,<=4.0.0
+watchdog>=4.0.0,<=4.0.1

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

@@ -2,6 +2,6 @@ apispec[yaml]>=6.3,<=6.6.1
 apispec-webframeworks>=0.5.2,<=1.1.0
 flask>=3.0.0,<=3.0.3
 flask-restful>=0.3.9,<=0.3.10
-marshmallow>=3.20.1,<=3.21.1
+marshmallow>=3.20.1,<=3.21.2
 passlib>=1.7.4,<=1.7.4
 taipy-core