Skip to content

Commit

Permalink
Merge pull request #833 from Avaiga/fix/cannot-submit-futures-after-s…
Browse files Browse the repository at this point in the history
…hutdown

Clean standalone run running under with context to automatically shutdown
  • Loading branch information
jrobinAV authored Feb 20, 2024
2 parents 8548aa6 + 28ecea4 commit 16dfc8c
Show file tree
Hide file tree
Showing 10 changed files with 116 additions and 141 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
from typing import Optional

from ...job.job import Job
from .._abstract_orchestrator import _AbstractOrchestrator
from ._job_dispatcher import _JobDispatcher
Expand All @@ -19,7 +17,7 @@
class _DevelopmentJobDispatcher(_JobDispatcher):
"""Manages job dispatching (instances of `Job^` class) in a synchronous way."""

def __init__(self, orchestrator: Optional[_AbstractOrchestrator]):
def __init__(self, orchestrator: _AbstractOrchestrator):
super().__init__(orchestrator)

def start(self):
Expand Down
8 changes: 3 additions & 5 deletions taipy/core/_orchestrator/_dispatcher/_job_dispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import threading
from abc import abstractmethod
from queue import Empty
from typing import Dict, Optional
from typing import Dict

from taipy.config.config import Config
from taipy.logger._taipy_logger import _TaipyLogger
Expand All @@ -32,7 +32,7 @@ class _JobDispatcher(threading.Thread):
_logger = _TaipyLogger._get_logger()
_nb_available_workers: int = 1

def __init__(self, orchestrator: Optional[_AbstractOrchestrator]):
def __init__(self, orchestrator: _AbstractOrchestrator):
threading.Thread.__init__(self, name="Thread-Taipy-JobDispatcher")
self.daemon = True
self.orchestrator = orchestrator
Expand Down Expand Up @@ -66,9 +66,7 @@ def run(self):
except Exception as e:
_TaipyLogger._get_logger().exception(e)
pass

# The dispatcher is now shutting down, let's shutdown its executor.
self._executor.shutdown(wait=True)
self._logger.info("Job dispatcher stopped.")

def _can_execute(self) -> bool:
"""Returns True if the dispatcher have resources to execute a new job."""
Expand Down
14 changes: 11 additions & 3 deletions taipy/core/_orchestrator/_dispatcher/_standalone_job_dispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,28 @@
class _StandaloneJobDispatcher(_JobDispatcher):
"""Manages job dispatching (instances of `Job^` class) in an asynchronous way using a ProcessPoolExecutor."""

def __init__(self, orchestrator: Optional[_AbstractOrchestrator], subproc_initializer: Optional[Callable] = None):
def __init__(self, orchestrator: _AbstractOrchestrator, subproc_initializer: Optional[Callable] = None):
super().__init__(orchestrator)
max_workers = Config.job_config.max_nb_of_workers or 1
self._executor: Executor = ProcessPoolExecutor(max_workers=max_workers, initializer=subproc_initializer) # type: ignore
self._executor: Executor = ProcessPoolExecutor(
max_workers=max_workers,
initializer=subproc_initializer
) # type: ignore
self._nb_available_workers = self._executor._max_workers # type: ignore

def run(self):
with self._executor:
super().run()
self._logger.info("Standalone job dispatcher: Pool executor shut down")

def _dispatch(self, job: Job):
"""Dispatches the given `Job^` on an available worker for execution.
Parameters:
job (Job^): The job to submit on an executor with an available worker.
"""
self._nb_available_workers -= 1

self._nb_available_workers -= 1
config_as_string = _TomlSerializer()._serialize(Config._applied_config) # type: ignore[attr-defined]
future = self._executor.submit(_TaskFunctionWrapper(job.id, job.task), config_as_string=config_as_string)

Expand Down
10 changes: 5 additions & 5 deletions taipy/core/_orchestrator/_orchestrator_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

import typing
from importlib import util
from typing import Optional, Type

Expand All @@ -27,7 +27,7 @@ class _OrchestratorFactory:
_TAIPY_ENTERPRISE_CORE_DISPATCHER_MODULE = _TAIPY_ENTERPRISE_MODULE + ".core._orchestrator._dispatcher"
__TAIPY_ENTERPRISE_BUILD_DISPATCHER_METHOD = "_build_dispatcher"

_orchestrator: Optional[_Orchestrator] = None
_orchestrator: Optional[_AbstractOrchestrator] = None
_dispatcher: Optional[_JobDispatcher] = None

@classmethod
Expand Down Expand Up @@ -80,20 +80,20 @@ def __build_standalone_job_dispatcher(cls, force_restart=False):
cls._TAIPY_ENTERPRISE_CORE_DISPATCHER_MODULE, cls.__TAIPY_ENTERPRISE_BUILD_DISPATCHER_METHOD
)(cls._orchestrator)
else:
cls._dispatcher = _StandaloneJobDispatcher(cls._orchestrator) # type: ignore
cls._dispatcher = _StandaloneJobDispatcher(typing.cast(_AbstractOrchestrator, cls._orchestrator))
cls._dispatcher.start() # type: ignore

@classmethod
def __build_development_job_dispatcher(cls):
if isinstance(cls._dispatcher, _StandaloneJobDispatcher):
cls._dispatcher.stop()
cls._dispatcher = _DevelopmentJobDispatcher(cls._orchestrator) # type: ignore
cls._dispatcher = _DevelopmentJobDispatcher(typing.cast(_AbstractOrchestrator, cls._orchestrator))

@classmethod
def __build_enterprise_job_dispatcher(cls, force_restart=False):
cls._dispatcher = _load_fct(
cls._TAIPY_ENTERPRISE_CORE_DISPATCHER_MODULE, cls.__TAIPY_ENTERPRISE_BUILD_DISPATCHER_METHOD
)(cls._orchestrator, force_restart)
)(typing.cast(_AbstractOrchestrator, cls._orchestrator), force_restart)
if cls._dispatcher:
cls._dispatcher.start()
else:
Expand Down
105 changes: 53 additions & 52 deletions taipy/core/data/excel.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,58 +195,59 @@ def _read(self):
return self._read_as()

def _read_as(self):
excel_file = load_workbook(self._path)
exposed_type = self.properties[self._EXPOSED_TYPE_PROPERTY]
work_books = dict()
sheet_names = excel_file.sheetnames

user_provided_sheet_names = self.properties.get(self.__SHEET_NAME_PROPERTY) or []
if not isinstance(user_provided_sheet_names, (List, Set, Tuple)):
user_provided_sheet_names = [user_provided_sheet_names]

provided_sheet_names = user_provided_sheet_names or sheet_names

for sheet_name in provided_sheet_names:
if sheet_name not in sheet_names:
raise NonExistingExcelSheet(sheet_name, self._path)

if isinstance(exposed_type, List):
if len(provided_sheet_names) != len(self.properties[self._EXPOSED_TYPE_PROPERTY]):
raise ExposedTypeLengthMismatch(
f"Expected {len(provided_sheet_names)} exposed types, got "
f"{len(self.properties[self._EXPOSED_TYPE_PROPERTY])}"
)

for i, sheet_name in enumerate(provided_sheet_names):
work_sheet = excel_file[sheet_name]
sheet_exposed_type = exposed_type

if not isinstance(sheet_exposed_type, str):
if isinstance(exposed_type, dict):
sheet_exposed_type = exposed_type.get(sheet_name, self._EXPOSED_TYPE_PANDAS)
elif isinstance(exposed_type, List):
sheet_exposed_type = exposed_type[i]

if isinstance(sheet_exposed_type, str):
if sheet_exposed_type == self._EXPOSED_TYPE_NUMPY:
work_books[sheet_name] = self._read_as_pandas_dataframe(sheet_name).to_numpy()
elif sheet_exposed_type == self._EXPOSED_TYPE_PANDAS:
work_books[sheet_name] = self._read_as_pandas_dataframe(sheet_name)
continue

res = list()
for row in work_sheet.rows:
res.append([col.value for col in row])
if self.properties[self._HAS_HEADER_PROPERTY] and res:
header = res.pop(0)
for i, row in enumerate(res):
res[i] = sheet_exposed_type(**dict([[h, r] for h, r in zip(header, row)]))
else:
for i, row in enumerate(res):
res[i] = sheet_exposed_type(*row)
work_books[sheet_name] = res

excel_file.close()
try:
excel_file = load_workbook(self._path)
exposed_type = self.properties[self._EXPOSED_TYPE_PROPERTY]
work_books = dict()
sheet_names = excel_file.sheetnames

user_provided_sheet_names = self.properties.get(self.__SHEET_NAME_PROPERTY) or []
if not isinstance(user_provided_sheet_names, (List, Set, Tuple)):
user_provided_sheet_names = [user_provided_sheet_names]

provided_sheet_names = user_provided_sheet_names or sheet_names

for sheet_name in provided_sheet_names:
if sheet_name not in sheet_names:
raise NonExistingExcelSheet(sheet_name, self._path)

if isinstance(exposed_type, List):
if len(provided_sheet_names) != len(self.properties[self._EXPOSED_TYPE_PROPERTY]):
raise ExposedTypeLengthMismatch(
f"Expected {len(provided_sheet_names)} exposed types, got "
f"{len(self.properties[self._EXPOSED_TYPE_PROPERTY])}"
)

for i, sheet_name in enumerate(provided_sheet_names):
work_sheet = excel_file[sheet_name]
sheet_exposed_type = exposed_type

if not isinstance(sheet_exposed_type, str):
if isinstance(exposed_type, dict):
sheet_exposed_type = exposed_type.get(sheet_name, self._EXPOSED_TYPE_PANDAS)
elif isinstance(exposed_type, List):
sheet_exposed_type = exposed_type[i]

if isinstance(sheet_exposed_type, str):
if sheet_exposed_type == self._EXPOSED_TYPE_NUMPY:
work_books[sheet_name] = self._read_as_pandas_dataframe(sheet_name).to_numpy()
elif sheet_exposed_type == self._EXPOSED_TYPE_PANDAS:
work_books[sheet_name] = self._read_as_pandas_dataframe(sheet_name)
continue

res = list()
for row in work_sheet.rows:
res.append([col.value for col in row])
if self.properties[self._HAS_HEADER_PROPERTY] and res:
header = res.pop(0)
for i, row in enumerate(res):
res[i] = sheet_exposed_type(**dict([[h, r] for h, r in zip(header, row)]))
else:
for i, row in enumerate(res):
res[i] = sheet_exposed_type(*row)
work_books[sheet_name] = res
finally:
excel_file.close()

if len(provided_sheet_names) == 1:
return work_books[provided_sheet_names[0]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
# specific language governing permissions and limitations under the License.

from concurrent.futures import Executor, Future
from typing import List, Optional
from typing import List

from taipy.core import Job
from taipy.core._orchestrator._abstract_orchestrator import _AbstractOrchestrator
Expand All @@ -35,7 +35,7 @@ def submit(self, fn, *args, **kwargs):


class MockStandaloneDispatcher(_StandaloneJobDispatcher):
def __init__(self, orchestrator: Optional[_AbstractOrchestrator]):
def __init__(self, orchestrator: _AbstractOrchestrator):
super(_StandaloneJobDispatcher, self).__init__(orchestrator)
self._executor: Executor = MockProcessPoolExecutor()
self.dispatch_calls: List = []
Expand Down
25 changes: 19 additions & 6 deletions tests/core/data/test_write_multiple_sheet_excel_data_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@ def tmp_excel_file():
def cleanup(tmp_excel_file):
yield
if os.path.exists(tmp_excel_file):
os.remove(tmp_excel_file)
try:
os.remove(tmp_excel_file)
except Exception as e:
from taipy.logger._taipy_logger import _TaipyLogger
logger = _TaipyLogger._get_logger()
logger.error(f"Failed to delete {tmp_excel_file}. {e}")


@dataclasses.dataclass
Expand Down Expand Up @@ -167,7 +172,6 @@ def test_write_with_header_multiple_sheet_custom_exposed_type_with_sheet_name(tm
Scope.SCENARIO,
properties={"path": tmp_excel_file, "sheet_name": sheet_names, "exposed_type": MyCustomObject},
)

row_1 = [MyCustomObject(0, 1, "hi"), MyCustomObject(1, 2, "world"), MyCustomObject(2, 3, "text")]
row_2 = [MyCustomObject(0, 4, "hello"), MyCustomObject(1, 5, "abc"), MyCustomObject(2, 6, ".")]
sheet_data = {"Sheet1": row_1, "Sheet2": row_2}
Expand All @@ -180,7 +184,10 @@ def test_write_with_header_multiple_sheet_custom_exposed_type_with_sheet_name(tm


def test_write_with_header_multiple_sheet_custom_exposed_type_without_sheet_name(tmp_excel_file):
excel_dn = ExcelDataNode("foo", Scope.SCENARIO, properties={"path": tmp_excel_file, "exposed_type": MyCustomObject})
excel_dn = ExcelDataNode(
"foo",
Scope.SCENARIO,
properties={"path": tmp_excel_file, "exposed_type": MyCustomObject})

row_1 = [MyCustomObject(0, 1, "hi"), MyCustomObject(1, 2, "world"), MyCustomObject(2, 3, "text")]
row_2 = [MyCustomObject(0, 4, "hello"), MyCustomObject(1, 5, "abc"), MyCustomObject(2, 6, ".")]
Expand All @@ -195,7 +202,9 @@ def test_write_with_header_multiple_sheet_custom_exposed_type_without_sheet_name

def test_write_without_header_multiple_sheet_pandas_with_sheet_name(tmp_excel_file):
excel_dn = ExcelDataNode(
"foo", Scope.SCENARIO, properties={"path": tmp_excel_file, "sheet_name": sheet_names, "has_header": False}
"foo",
Scope.SCENARIO,
properties={"path": tmp_excel_file, "sheet_name": sheet_names, "has_header": False}
)

df_1 = pd.DataFrame([*zip([1, 2, 3])])
Expand Down Expand Up @@ -283,7 +292,9 @@ def test_write_without_header_multiple_sheet_numpy_with_sheet_name(tmp_excel_fil

def test_write_without_header_multiple_sheet_numpy_without_sheet_name(tmp_excel_file):
excel_dn = ExcelDataNode(
"foo", Scope.SCENARIO, properties={"path": tmp_excel_file, "exposed_type": "numpy", "has_header": False}
"foo",
Scope.SCENARIO,
properties={"path": tmp_excel_file, "exposed_type": "numpy", "has_header": False}
)

arr_1 = np.array([[1], [2], [3]])
Expand Down Expand Up @@ -332,7 +343,9 @@ def test_write_without_header_multiple_sheet_custom_exposed_type_with_sheet_name

def test_write_without_header_multiple_sheet_custom_exposed_type_without_sheet_name(tmp_excel_file):
excel_dn = ExcelDataNode(
"foo", Scope.SCENARIO, properties={"path": tmp_excel_file, "exposed_type": MyCustomObject, "has_header": False}
"foo",
Scope.SCENARIO,
properties={"path": tmp_excel_file, "exposed_type": MyCustomObject, "has_header": False}
)

row_1 = [MyCustomObject(0, 1, "hi"), MyCustomObject(1, 2, "world"), MyCustomObject(2, 3, "text")]
Expand Down
25 changes: 17 additions & 8 deletions tests/core/data/test_write_single_sheet_excel_data_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
# 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 dataclasses
import os
import pathlib
Expand All @@ -32,7 +31,12 @@ def tmp_excel_file():
def cleanup(tmp_excel_file):
yield
if os.path.exists(tmp_excel_file):
os.remove(tmp_excel_file)
try:
os.remove(tmp_excel_file)
except Exception as e:
from taipy.logger._taipy_logger import _TaipyLogger
logger = _TaipyLogger._get_logger()
logger.error(f"Failed to delete {tmp_excel_file}. {e}")


@dataclasses.dataclass
Expand Down Expand Up @@ -248,17 +252,21 @@ def test_write_with_header_single_sheet_custom_exposed_type_with_sheet_name(tmp_
Scope.SCENARIO,
properties={"path": tmp_excel_file, "sheet_name": "Sheet1", "exposed_type": MyCustomObject},
)
expected_data = [MyCustomObject(0, 1, "hi"), MyCustomObject(1, 2, "world"), MyCustomObject(2, 3, "text")]

data = [MyCustomObject(0, 1, "hi"), MyCustomObject(1, 2, "world"), MyCustomObject(2, 3, "text")]
excel_dn.write(data)
assert all(actual == expected for actual, expected in zip(excel_dn.read(), data))
excel_dn.write(expected_data)
actual_data = excel_dn.read()

assert all(actual == expected for actual, expected in zip(actual_data, expected_data))

excel_dn.write(None)
assert excel_dn.read() == []
actual_data = excel_dn.read()
assert actual_data == []


def test_write_with_header_single_sheet_custom_exposed_type_without_sheet_name(tmp_excel_file):
excel_dn = ExcelDataNode("foo", Scope.SCENARIO, properties={"path": tmp_excel_file, "exposed_type": MyCustomObject})
excel_dn = ExcelDataNode("foo", Scope.SCENARIO,
properties={"path": tmp_excel_file, "exposed_type": MyCustomObject})

data = [MyCustomObject(0, 1, "hi"), MyCustomObject(1, 2, "world"), MyCustomObject(2, 3, "text")]
excel_dn.write(data)
Expand Down Expand Up @@ -290,7 +298,8 @@ def test_write_without_header_single_sheet_custom_exposed_type_with_sheet_name(t

def test_write_without_header_single_sheet_custom_exposed_type_without_sheet_name(tmp_excel_file):
excel_dn = ExcelDataNode(
"foo", Scope.SCENARIO, properties={"path": tmp_excel_file, "exposed_type": MyCustomObject, "has_header": False}
"foo", Scope.SCENARIO,
properties={"path": tmp_excel_file, "exposed_type": MyCustomObject, "has_header": False}
)

data = [MyCustomObject(0, 1, "hi"), MyCustomObject(1, 2, "world"), MyCustomObject(2, 3, "text")]
Expand Down
Loading

1 comment on commit 16dfc8c

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage

Taipy Overall Coverage Report
FileStmtsMissCoverMissing
__init__.py16193%31
_entrypoint.py36488%44, 46, 50–51
_run.py38294%77–78
version.py90100% 
_cli
   __init__.py00100% 
   _help_cli.py190100% 
   _run_cli.py251156%40, 42–46, 48–49, 51, 53, 59
   _scaffold_cli.py18288%41–42
_cli/_base_cli
   __init__.py10100% 
   _cli.py410100% 
config
   __init__.py38976%41–42, 44–49, 70
   _config.py60198%68
   _init.py30100% 
   config.py145497%87, 154, 178, 233
   section.py42685%35, 40, 44, 48, 53, 57
   setup.py15150%14–16, 18, 20–21, 23, 25–29, 31, 33, 35
   unique_section.py50100% 
   version.py90100% 
config/_config_comparator
   __init__.py00100% 
   _comparator_result.py95396%67, 110–111
   _config_comparator.py680100% 
config/_serializer
   __init__.py00100% 
   _base_serializer.py124595%39, 141–142, 144–145
   _json_serializer.py24387%33–35
   _toml_serializer.py230100% 
config/checker
   __init__.py00100% 
   _checker.py140100% 
   issue.py150100% 
   issue_collector.py280100% 
config/checker/_checkers
   __init__.py00100% 
   _auth_config_checker.py25250%12–14, 17–19, 21–24, 26–30, 32–34, 39–40, 47–49, 54–55
   _config_checker.py30196%28
config/common
   __init__.py00100% 
   _classproperty.py50100% 
   _config_blocker.py240100% 
   _repr_enum.py70100% 
   _template_handler.py1111190%39, 53, 55, 59, 61, 63, 99–100, 118, 149–150
   _validate_id.py100100% 
   frequency.py70100% 
   scope.py220100% 
   typing.py30100% 
config/exceptions
   __init__.py10100% 
   exceptions.py50100% 
config/global_app
   __init__.py00100% 
   global_app_config.py32196%38
core
   __init__.py70100% 
   _core.py560100% 
   _core_cli.py460100% 
   _init.py180100% 
   _init_version.py100100% 
   setup.py16160%14–16, 18, 20–21, 23–28, 30, 39, 41, 50
   taipy.py3904588%163, 219, 262, 267, 272, 277, 282, 287, 292, 297, 302, 340–342, 347, 352, 357, 362, 367, 372, 377, 382, 422, 461, 490–491, 498–504, 530–533, 941, 980, 996, 1045–1049
core/_backup
   __init__.py00100% 
   _backup.py33293%54–55
core/_entity
   __init__.py00100% 
   _dag.py65198%94
   _entity.py220100% 
   _entity_ids.py220100% 
   _labeled.py480100% 
   _migrate_cli.py74494%98–99, 117–118
   _properties.py350100% 
   _reload.py62198%72
   submittable.py75494%47, 102, 106, 110
core/_entity/_migrate
   __init__.py30100% 
   _migrate_fs.py66592%43, 68, 104–106
   _migrate_mongo.py76593%52, 71, 87, 105, 127
   _migrate_sql.py1065350%30–31, 33–36, 38–46, 53, 57, 64–65, 69, 76–77, 81, 89–90, 94, 100–101, 105, 110–111, 115, 120–121, 125, 127–137, 139–140, 154, 198–199, 201–202
   _utils.py1993084%32, 34–36, 38, 59–61, 63–67, 69–71, 73–74, 157, 160, 169, 197–199, 202–204, 241–242, 316
core/_manager
   __init__.py00100% 
   _manager.py81198%113
   _manager_factory.py21290%28, 32
core/_orchestrator
   __init__.py00100% 
   _abstract_orchestrator.py23482%27, 40, 53, 58
   _orchestrator.py1891492%146, 190–191, 193–199, 201–203, 291
   _orchestrator_factory.py641084%38, 58, 73–74, 76, 79, 94, 97–98, 100
core/_orchestrator/_dispatcher
   __init__.py30100% 
   _development_job_dispatcher.py18477%24, 27, 30, 33
   _job_dispatcher.py91891%61, 66–68, 91–92, 109, 127
   _standalone_job_dispatcher.py310100% 
   _task_function_wrapper.py57394%75–76, 84
core/_repository
   __init__.py00100% 
   _abstract_converter.py10280%19, 24
   _abstract_repository.py361072%29, 41, 53, 63, 73, 80, 90, 101, 113, 124
   _base_taipy_model.py31487%27–28, 50, 53
   _decoder.py21290%36, 51
   _encoder.py19384%30, 36, 41
   _filesystem_repository.py1592186%147, 173–174, 176, 178–181, 190–193, 197–200, 224, 237–238, 257–258
   _sql_repository.py1311687%79–80, 123, 137, 143–144, 147–148, 153–157, 163, 180, 216
core/_repository/db
   __init__.py00100% 
   _sql_base_model.py30100% 
   _sql_connection.py46295%82–83
core/_version
   __init__.py00100% 
   _utils.py260100% 
   _version.py140100% 
   _version_converter.py140100% 
   _version_fs_repository.py68592%44–45, 100–101, 134
   _version_manager.py149894%98, 104, 148–149, 171, 212, 220, 230
   _version_manager_factory.py19289%26, 29
   _version_mixin.py190100% 
   _version_model.py210100% 
   _version_repository_interface.py26773%22, 26, 30, 34, 38, 42, 46
   _version_sql_repository.py530100% 
core/_version/_cli
   __init__.py00100% 
   _bcolor.py241058%21, 23–25, 27–31, 33
   _version_cli.py138497%81, 85, 193, 195
core/common
   __init__.py10100% 
   _check_dependencies.py5180%28
   _check_instance.py230100% 
   _listattributes.py29389%21–22, 32
   _mongo_connector.py17288%37, 45
   _repr_enum.py70100% 
   _utils.py390100% 
   _warnings.py210100% 
   mongo_default_document.py50100% 
   typing.py70100% 
   warn_if_inputs_not_ready.py140100% 
core/config
   __init__.py300100% 
   core_section.py197597%139, 148, 157, 166, 179
   data_node_config.py350798%287, 291, 637, 639, 643, 645, 1044
   job_config.py73395%125–127
   migration_config.py46197%57
   scenario_config.py149497%211, 215, 223, 227
   task_config.py97792%70–71, 149, 152, 220–222
core/config/checkers
   __init__.py00100% 
   _config_id_checker.py180100% 
   _core_section_checker.py180100% 
   _data_node_config_checker.py91297%112, 147
   _job_config_checker.py190100% 
   _migration_config_checker.py300100% 
   _scenario_config_checker.py610100% 
   _task_config_checker.py340100% 
core/cycle
   __init__.py00100% 
   _cycle_converter.py110100% 
   _cycle_fs_repository.py60100% 
   _cycle_manager.py1010100% 
   _cycle_manager_factory.py20290%28, 31
   _cycle_model.py230100% 
   _cycle_sql_repository.py60100% 
   cycle.py109298%81, 147
   cycle_id.py30100% 
core/data
   __init__.py130100% 
   _abstract_file.py170100% 
   _abstract_sql.py1833083%95, 143, 145, 149, 162–163, 166–167, 179, 185, 187, 189, 195, 202, 235, 254–261, 268, 274, 290, 299–301, 307
   _abstract_tabular.py45197%40
   _data_converter.py175597%114, 183, 225, 258, 267
   _data_fs_repository.py60100% 
   _data_manager.py117298%93, 179
   _data_manager_factory.py20290%28, 31
   _data_model.py320100% 
   _data_sql_repository.py60100% 
   _filter.py1931791%41, 63, 83–91, 143, 183, 190, 205, 213, 222
   aws_s3.py40295%103, 106
   csv.py109397%91, 205, 209
   data_node.py293797%158, 226, 278, 287, 464, 467, 471
   data_node_id.py50100% 
   excel.py2022886%95, 279, 282, 284–288, 292–293, 296, 298–300, 302, 304–305, 307, 312, 314–315, 317–320, 322, 343, 365
   generic.py48197%83
   in_memory.py280100% 
   json.py115595%91, 170, 174, 179, 183
   mongo.py1131289%207–214, 222, 232, 237, 289
   operator.py110100% 
   parquet.py114397%107, 122, 230
   pickle.py630100% 
   sql.py44197%100
   sql_table.py660100% 
core/exceptions
   __init__.py10100% 
   exceptions.py118595%183, 191, 229, 267, 309
core/job
   __init__.py00100% 
   _job_converter.py31293%59–60
   _job_fs_repository.py60100% 
   _job_manager.py570100% 
   _job_manager_factory.py20290%28, 31
   _job_model.py260100% 
   _job_sql_repository.py60100% 
   job.py187597%30, 77, 149, 295, 336
   job_id.py30100% 
   status.py110100% 
core/notification
   __init__.py70100% 
   _registration.py180100% 
   _topic.py23195%65
   core_event_consumer.py26196%84
   event.py40197%139
   notifier.py470100% 
   registration_id.py30100% 
core/scenario
   __init__.py00100% 
   _scenario_converter.py320100% 
   _scenario_fs_repository.py60100% 
   _scenario_manager.py273996%88–91, 183, 188, 384, 429, 440
   _scenario_manager_factory.py20290%28, 31
   _scenario_model.py300100% 
   _scenario_sql_repository.py60100% 
   scenario.py3191495%129, 132, 134–135, 177, 224, 243, 324, 366, 409, 601, 603, 659, 665
   scenario_id.py30100% 
core/sequence
   __init__.py00100% 
   _sequence_converter.py190100% 
   _sequence_manager.py2381294%227, 241, 243, 246, 251, 257, 283–286, 353, 371
   _sequence_manager_factory.py11190%23
   sequence.py144397%81, 160, 172
   sequence_id.py30100% 
core/submission
   __init__.py00100% 
   _submission_converter.py200100% 
   _submission_fs_repository.py60100% 
   _submission_manager.py600100% 
   _submission_manager_factory.py20290%28, 31
   _submission_model.py310100% 
   _submission_sql_repository.py60100% 
   submission.py171795%111, 119, 140, 186, 189, 195, 248
   submission_id.py30100% 
   submission_status.py100100% 
core/task
   __init__.py00100% 
   _task_converter.py28196%68
   _task_fs_repository.py60100% 
   _task_manager.py121595%153–154, 159–160, 200
   _task_manager_factory.py20290%28, 31
   _task_model.py250100% 
   _task_sql_repository.py60100% 
   task.py107595%91, 96, 167–169
   task_id.py30100% 
gui
   __init__.py150100% 
   _default_config.py30100% 
   _gui_cli.py370100% 
   _gui_section.py300100% 
   _init.py10100% 
   _page.py32293%20–21
   _warnings.py110100% 
   config.py1101685%175, 177, 179–180, 184, 193, 201, 203, 207, 209, 211, 275–276, 281–282, 323
   gui.py124123481%41, 362, 364, 410–416, 419–426, 428–429, 431, 433–434, 436–438, 440–441, 443, 445–447, 449–452, 454–455, 457–462, 516, 527, 529, 531, 551, 574, 576, 589–590, 596–597, 618–621, 635, 639, 651, 665, 670, 677–678, 702–704, 714, 743, 752, 772, 789–790, 794–800, 802–807, 840–841, 855, 864, 952, 954, 959–960, 963, 966–967, 969, 971, 975, 977–978, 983, 985, 990, 996–1003, 1005, 1007, 1020–1024, 1026–1029, 1043–1045, 1052, 1054–1055, 1058, 1067, 1069–1071, 1079–1087, 1092, 1124–1126, 1193–1194, 1259, 1261, 1266, 1275–1276, 1287, 1297, 1303, 1331, 1333–1334, 1387, 1477–1479, 1489, 1498–1506, 1530, 1536, 1692–1695, 1741, 1743, 1794, 1810, 1824–1826, 1828, 1831–1832, 1844, 1849–1851, 1853, 1911, 1925–1926, 1955, 1960, 1972–1975, 1981, 1993–1998, 2018, 2025–2027, 2042, 2055, 2067, 2079, 2081, 2085–2086, 2094–2096, 2110, 2226, 2358, 2371, 2381, 2385
   gui_actions.py962771%61, 101, 133, 148, 174, 196–199, 220, 234, 238–239, 261–263, 289, 312–314, 318–319, 371, 380, 382, 387, 393
   gui_types.py91693%147, 152, 157, 164, 175, 177
   icon.py231630%58–63, 66–75
   page.py511864%21, 46, 48, 50, 55–57, 62–67, 98, 109, 112–114
   partial.py27388%21, 66, 73
   server.py1743778%43, 68, 92, 110, 156–162, 164–165, 178–179, 188, 191, 198, 209, 260, 262–263, 286, 295, 297, 302–303, 310–311, 313–315, 325, 327, 329, 332–333
   setup.py26260%14, 16–18, 20–21, 23, 25–30, 32, 51, 53, 63–67, 70–73, 76
   state.py1081883%24, 128, 140, 154, 180, 195, 210–212, 222–223, 235–238, 241–242, 245
gui/_renderers
   __init__.py731086%21–22, 40, 49, 60, 85–87, 91, 98
   builder.py66411382%45, 123, 142–143, 159–161, 163, 176, 203, 235–236, 240, 258–259, 263, 278, 296, 301–302, 306, 317, 334–337, 346, 349–351, 364, 377, 379–380, 383, 387–390, 392, 399, 441, 453, 499–502, 504–508, 510–513, 515–519, 529, 549, 566, 573, 578, 593, 600, 603–604, 606, 609, 613, 628, 635, 661, 673, 675–679, 682–683, 707, 710, 712, 798, 801, 824–825, 829, 861, 866–869, 871–874, 880–881, 891, 944, 961, 966, 973–977, 1000
   factory.py78396%20–21, 578
   json.py30873%26, 33–39
   utils.py66887%38–39, 54, 66–67, 78–79, 90
gui/_renderers/_html
   __init__.py10100% 
   factory.py9188%22
   parser.py981386%41–42, 52–53, 63, 70, 74, 82–83, 108–109, 121, 134
gui/_renderers/_markdown
   __init__.py18194%30
   blocproc.py35488%48–49, 67–68
   control.py110100% 
   factory.py140100% 
   postproc.py190100% 
   preproc.py122695%23, 103, 132, 136, 140, 199
gui/builder
   __init__.py40100% 
   _api_generator.py59689%25, 43, 45, 51, 70, 73
   _context_manager.py13192%17
   _element.py951089%24, 49–50, 65, 73, 78, 160, 171, 189, 192
   _factory.py9188%22
   page.py30970%57, 61–66, 70, 72
gui/custom
   __init__.py10100% 
   _page.py301260%28–32, 45, 48, 52, 63, 70, 79, 86
gui/data
   __init__.py30100% 
   array_dict_data_accessor.py40685%47–48, 50–51, 59, 66
   content_accessor.py961881%51, 58–60, 64, 66, 73, 85–86, 91–92, 109–110, 124–126, 128, 131
   data_accessor.py721579%27, 33, 37, 43, 48, 51, 72, 74, 77, 83, 87–88, 97–99
   data_format.py40100% 
   data_scope.py37294%61–62
   numpy_data_accessor.py21766%30, 33–35, 40–42
   pandas_data_accessor.py2406572%81, 83–92, 94, 96–103, 109, 117, 149–151, 156–159, 182, 204, 207–212, 231, 262–263, 279–280, 283, 292–294, 300–301, 311, 317–319, 353–357, 359, 364–365, 374–376, 406, 409
   utils.py592852%22, 53–54, 57–59, 82, 98, 118–119, 121–125, 127–133, 136, 138, 141–144
gui/data/decimator
   __init__.py40100% 
   lttb.py38294%53, 56
   minmax.py25196%45
   rdp.py64592%85, 112–114, 140
   scatter_decimator.py501374%62–63, 77–83, 87–90
gui/extension
   __init__.py20100% 
   library.py1391887%27–28, 62, 71, 75, 81, 132, 141, 143, 176, 240, 270, 343, 349, 351, 364, 412, 422
gui/utils
   __init__.py220100% 
   _adapter.py1175453%39–40, 44–45, 48–55, 58–70, 82, 87, 92, 98–100, 103–108, 116, 126–133, 141–145, 149, 151, 153
   _attributes.py30776%16, 31–32, 39–40, 56–57
   _bindings.py50198%20
   _evaluator.py2284281%22, 124–126, 149, 151, 161, 169–170, 199–201, 244–248, 250–260, 270, 290–293, 295–298, 301, 313, 326–327, 338
   _locals_context.py48295%54, 60
   _map_dict.py66592%34, 55, 70–71, 73
   _runtime_manager.py13284%17, 30
   _variable_directory.py100892%47–50, 61, 72, 84, 88
   boolean.py9188%27
   chart_config_builder.py1381291%23, 91–92, 107, 154–155, 195, 198–200, 266, 268
   clientvarname.py14285%23, 26
   datatype.py11372%20, 22, 24
   date.py17382%27, 30–31
   expr_var_name.py13192%23
   filename.py120100% 
   filter_locals.py50100% 
   get_imported_var.py110100% 
   get_module_name.py130100% 
   get_page_from_module.py5180%19
   getdatecolstrname.py8187%21
   html.py9188%22
   is_debugging.py3166%17
   is_port_open.py60100% 
   isnotebook.py10100% 
   proxy.py65650%12–17, 19–22, 24, 27, 29, 36–37, 40–46, 49, 52–53, 55–60, 62–63, 70–71, 73–79, 87–88, 91–95, 97–103, 106–109, 111–115
   singleton.py70100% 
   table_col_builder.py461860%21, 29, 38–40, 42, 52–54, 56, 59–61, 63, 66–68, 70
   types.py1493179%49–50, 104–110, 138–141, 144–146, 185–186, 195–199, 201–208
   varnamefromcontent.py6183%17
gui_core
   _GuiCoreLib.py340100% 
   __init__.py10100% 
   _adapters.py823853%59–64, 100–101, 103, 113, 116–129, 133, 146–147, 149, 166–172, 196–197, 199
   _context.py57322161%21–22, 163, 172–173, 199, 203–204, 217, 232–233, 242–248, 250–251, 254–257, 264–265, 268–273, 285, 312–316, 319–325, 327–336, 351, 353–356, 358–359, 368–372, 374, 379, 381–384, 386, 389, 392, 396–407, 412, 423, 435–436, 438–443, 445, 449, 458, 464, 480–487, 492–495, 498–500, 510–514, 521–522, 529–531, 538–539, 547–550, 570–571, 577, 589–590, 601–602, 612, 622–623, 628, 640, 642–643, 650, 656, 659–662, 665–668, 677–679, 681, 685, 691, 698–704, 706–707, 716–717, 722, 725, 728, 735–742, 749–752, 758–761, 775, 795–796, 818–820, 826–827, 846–848, 864, 867–868, 885–886, 890–901
   _init.py50100% 
logger
   __init__.py00100% 
   _taipy_logger.py22290%30–31
rest
   __init__.py40100% 
   _init.py10100% 
   app.py200100% 
   extensions.py30100% 
   rest.py7185%45
   setup.py12120%11–12, 14, 16–17, 19–24, 26
   version.py90100% 
rest/api
   __init__.py20100% 
   error_handler.py49197%88
   views.py64493%165–166, 212–213
rest/api/exceptions
   __init__.py00100% 
   exceptions.py90100% 
rest/api/middlewares
   __init__.py00100% 
   _middleware.py14192%34
rest/api/resources
   __init__.py70100% 
   cycle.py490100% 
   datanode.py87396%465, 584, 603
   job.py70494%201–204
   scenario.py650100% 
   sequence.py66198%293
   task.py65198%202
rest/api/schemas
   __init__.py70100% 
   cycle.py100100% 
   datanode.py630100% 
   job.py120100% 
   scenario.py120100% 
   sequence.py100100% 
   task.py110100% 
rest/commons
   __init__.py00100% 
   apispec.py51982%35, 49, 84, 87, 91, 98–100, 103
   encoder.py13653%22–25, 27–28
   pagination.py14140%12, 14, 16–17, 20–23, 26–29, 36, 44
   to_from_model.py80100% 
templates/default/{{cookiecutter.__root_folder_name}}
   requirements.txt10100% 
   {{cookiecutter.__main_file}}.py00100% 
templates/default/{{cookiecutter.__root_folder_name}}/algorithms
   __init__.py110%12
   algorithms.py10100% 
templates/default/{{cookiecutter.__root_folder_name}}/configuration
   __init__.py110%12
   config.py3233%18, 20
templates/default/{{cookiecutter.__root_folder_name}}/pages
   __init__.py110%12
   root.py3233%19, 21
templates/default/{{cookiecutter.__root_folder_name}}/pages/page_example
   page_example.md00100% 
   page_example.py3233%19, 21
templates/default/{{cookiecutter.__root_folder_name}}/sections
   import.txt10100% 
   page_content.txt00100% 
templates/scenario-management/{{cookiecutter.__root_folder_name}}
   .taipyignore00100% 
templates/scenario-management/{{cookiecutter.__root_folder_name}}/algos
   __init__.py110%12
   algos.py3166%15
templates/scenario-management/{{cookiecutter.__root_folder_name}}/config
   __init__.py00100% 
   config.py9722%17, 22–25, 31, 34
   config.toml18950%3, 15–17, 19–22, 24
   config_with_toml.py440%12, 15–17
templates/scenario-management/{{cookiecutter.__root_folder_name}}/pages/job_page
   __init__.py110%12
   job_page.py2150%12
templates/scenario-management/{{cookiecutter.__root_folder_name}}/pages/scenario_page
   __init__.py110%12
   data_node_management.py141214%20–21, 26, 34–35, 42–43, 46–50
   scenario_page.py11109%12, 17–21, 23, 26–27, 30
TOTAL17603207588% 

Please sign in to comment.