Skip to content

Commit

Permalink
fix: add normalize_path to utils
Browse files Browse the repository at this point in the history
  • Loading branch information
joaoandre-avaiga committed Nov 25, 2024
1 parent 33fd039 commit cf7bc01
Show file tree
Hide file tree
Showing 11 changed files with 72 additions and 45 deletions.
3 changes: 3 additions & 0 deletions taipy/core/_entity/_properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

from taipy.common.config.common._template_handler import _TemplateHandler as _tpl

from ..common._utils import _normalize_path
from ..notification import EventOperation, Notifier, _make_event


Expand All @@ -26,6 +27,8 @@ def __init__(self, entity_owner, **kwargs):
self._pending_deletions = set()

def __setitem__(self, key, value):
if key == "path":
value = _normalize_path(value)
super(_Properties, self).__setitem__(key, value)

if hasattr(self, "_entity_owner"):
Expand Down
5 changes: 5 additions & 0 deletions taipy/core/common/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
# specific language governing permissions and limitations under the License.

import functools
import re
import time
from collections import namedtuple
from importlib import import_module
Expand Down Expand Up @@ -79,4 +80,8 @@ def _fcts_to_dict(objs):
return [d for obj in objs if (d := _fct_to_dict(obj)) is not None]


def _normalize_path(path: str) -> str:
return re.sub(r"[\\]+", "/", path)


_Subscriber = namedtuple("_Subscriber", "callback params")
8 changes: 2 additions & 6 deletions taipy/core/config/data_node_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
# specific language governing permissions and limitations under the License.

import json
import re
from copy import copy
from datetime import timedelta
from typing import Any, Callable, Dict, List, Optional, Union
Expand All @@ -22,6 +21,7 @@
from taipy.common.config.common.scope import Scope
from taipy.common.config.section import Section

from ..common._utils import _normalize_path
from ..common._warnings import _warn_deprecated
from ..common.mongo_default_document import MongoDefaultDocument

Expand Down Expand Up @@ -277,7 +277,7 @@ def __init__(
self._scope = scope
self._validity_period = validity_period
if "path" in properties:
properties["path"] = self._normalize_path(properties["path"])
properties["path"] = _normalize_path(properties["path"])
super().__init__(id, **properties)

# modin exposed type is deprecated since taipy 3.1.0
Expand All @@ -295,10 +295,6 @@ def __copy__(self):
def __getattr__(self, item: str) -> Optional[Any]:
return _tpl._replace_templates(self._properties.get(item))

@staticmethod
def _normalize_path(path: str) -> str:
return re.sub(r"[\\]+", "/", path)

@property
def storage_type(self) -> str:
"""Storage type of the data nodes created from the data node config.
Expand Down
10 changes: 3 additions & 7 deletions taipy/core/data/_file_datanode_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

import os
import pathlib
import re
import shutil
from datetime import datetime
from os.path import isfile
Expand All @@ -21,6 +20,7 @@
from taipy.common.logger._taipy_logger import _TaipyLogger

from .._entity._reload import _self_reload
from ..common._utils import _normalize_path
from ..reason import InvalidUploadFile, NoFileToDownload, NotAFile, ReasonCollection, UploadFileCanNotBeRead
from .data_node import DataNode
from .data_node_id import Edit
Expand Down Expand Up @@ -61,11 +61,11 @@ def is_generated(self) -> bool:
@_self_reload(DataNode._MANAGER_NAME)
def path(self) -> str:
"""The path to the file data of the data node."""
return self._path
return _normalize_path(self._path)

@path.setter
def path(self, value) -> None:
_path = self._normalize_path(value)
_path = _normalize_path(value)
self._path = _path
self.properties[self._PATH_KEY] = _path # type: ignore[attr-defined]
self.properties[self._IS_GENERATED_KEY] = False # type: ignore[attr-defined]
Expand Down Expand Up @@ -182,7 +182,3 @@ def _migrate_path(self, storage_type, old_path) -> str:
if os.path.exists(old_path):
shutil.move(old_path, new_path)
return new_path

@staticmethod
def _normalize_path(path: str) -> str:
return re.sub(r"[\\]+", "/", path)
5 changes: 5 additions & 0 deletions tests/core/config/test_data_node_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -405,3 +405,8 @@ def test_clean_config():
assert dn1_config.validity_period is dn2_config.validity_period is None
assert dn1_config.default_path is dn2_config.default_path is None
assert dn1_config.properties == dn2_config.properties == {}


def test_normalize_path():
data_node_config = Config.configure_data_node(id="data_nodes1", storage_type="csv", path=r"data\file.csv")
assert data_node_config.path == "data/file.csv"
16 changes: 9 additions & 7 deletions tests/core/data/test_csv_data_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import dataclasses
import os
import pathlib
import re
import uuid
from datetime import datetime, timedelta
from time import sleep
Expand All @@ -25,6 +26,7 @@
from taipy.common.config import Config
from taipy.common.config.common.scope import Scope
from taipy.common.config.exceptions.exceptions import InvalidConfigurationId
from taipy.core.common._utils import _normalize_path
from taipy.core.data._data_manager import _DataManager
from taipy.core.data._data_manager_factory import _DataManagerFactory
from taipy.core.data.csv import CSVDataNode
Expand Down Expand Up @@ -129,7 +131,7 @@ def test_new_csv_data_node_with_existing_file_is_ready_for_reading(self):
)
def test_create_with_default_data(self, properties, exists):
dn = CSVDataNode("foo", Scope.SCENARIO, DataNodeId(f"dn_id_{uuid.uuid4()}"), properties=properties)
assert dn.path == os.path.join(Config.core.storage_folder.strip("/"), "csvs", dn.id + ".csv")
assert dn.path == f"{Config.core.storage_folder}/csvs/dn.id.csv"
assert os.path.exists(dn.path) is exists

def test_set_path(self):
Expand Down Expand Up @@ -208,20 +210,20 @@ def test_is_not_downloadable_no_file(self):
reasons = dn.is_downloadable()
assert not reasons
assert len(reasons._reasons) == 1
assert str(NoFileToDownload(path, dn.id)) in reasons.reasons
assert str(NoFileToDownload(_normalize_path(path), dn.id)) in reasons.reasons

def test_is_not_downloadable_not_a_file(self):
path = os.path.join(pathlib.Path(__file__).parent.resolve(), "data_sample")
dn = CSVDataNode("foo", Scope.SCENARIO, properties={"path": path, "exposed_type": "pandas"})
reasons = dn.is_downloadable()
assert not reasons
assert len(reasons._reasons) == 1
assert str(NotAFile(path, dn.id)) in reasons.reasons
assert str(NotAFile(_normalize_path(path), dn.id)) in reasons.reasons

def test_get_downloadable_path(self):
path = os.path.join(pathlib.Path(__file__).parent.resolve(), "data_sample/example.csv")
dn = CSVDataNode("foo", Scope.SCENARIO, properties={"path": path, "exposed_type": "pandas"})
assert dn._get_downloadable_path() == path
assert re.split(r"[\\/]", dn._get_downloadable_path()) == re.split(r"[\\/]", path)

def test_get_downloadable_path_with_not_existing_file(self):
dn = CSVDataNode("foo", Scope.SCENARIO, properties={"path": "NOT_EXISTING.csv", "exposed_type": "pandas"})
Expand All @@ -247,7 +249,7 @@ def test_upload(self, csv_file, tmpdir_factory):

assert_frame_equal(dn.read(), upload_content) # The content of the dn should change to the uploaded content
assert dn.last_edit_date > old_last_edit_date
assert dn.path == old_csv_path # The path of the dn should not change
assert dn.path == _normalize_path(old_csv_path) # The path of the dn should not change

def test_upload_with_upload_check_with_exception(self, csv_file, tmpdir_factory, caplog):
old_csv_path = tmpdir_factory.mktemp("data").join("df.csv").strpath
Expand Down Expand Up @@ -304,7 +306,7 @@ def check_data_column(upload_path, upload_data):

assert_frame_equal(dn.read(), old_data) # The content of the dn should not change when upload fails
assert dn.last_edit_date == old_last_edit_date # The last edit date should not change when upload fails
assert dn.path == old_csv_path # The path of the dn should not change
assert dn.path == _normalize_path(old_csv_path) # The path of the dn should not change

# The upload should succeed when check_data_column() return True
assert dn._upload(csv_file, upload_checker=check_data_column)
Expand Down Expand Up @@ -354,7 +356,7 @@ def check_data_is_positive(upload_path, upload_data):

np.array_equal(dn.read(), old_data) # The content of the dn should not change when upload fails
assert dn.last_edit_date == old_last_edit_date # The last edit date should not change when upload fails
assert dn.path == old_csv_path # The path of the dn should not change
assert dn.path == _normalize_path(old_csv_path) # The path of the dn should not change

# The upload should succeed when check_data_is_positive() return True
assert dn._upload(new_csv_path, upload_checker=check_data_is_positive)
12 changes: 12 additions & 0 deletions tests/core/data/test_data_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -807,3 +807,15 @@ def test_track_edit(self):
edit_5 = data_node.edits[5]
assert len(edit_5) == 1
assert edit_5[EDIT_TIMESTAMP_KEY] == timestamp

def test_normalize_path(self):
dn = DataNode(
config_id="foo_bar",
scope=Scope.SCENARIO,
id=DataNodeId("an_id"),
path=r"data\foo\bar.csv",
)
assert dn.config_id == "foo_bar"
assert dn.scope == Scope.SCENARIO
assert dn.id == "an_id"
assert dn.properties["path"] == "data/foo/bar.csv"
16 changes: 9 additions & 7 deletions tests/core/data/test_excel_data_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import os
import pathlib
import re
import uuid
from datetime import datetime, timedelta
from time import sleep
Expand All @@ -24,6 +25,7 @@

from taipy.common.config import Config
from taipy.common.config.common.scope import Scope
from taipy.core.common._utils import _normalize_path
from taipy.core.data._data_manager import _DataManager
from taipy.core.data._data_manager_factory import _DataManagerFactory
from taipy.core.data.data_node_id import DataNodeId
Expand Down Expand Up @@ -183,7 +185,7 @@ def test_set_path(self):
)
def test_create_with_default_data(self, properties, exists):
dn = ExcelDataNode("foo", Scope.SCENARIO, DataNodeId(f"dn_id_{uuid.uuid4()}"), properties=properties)
assert dn.path == os.path.join(Config.core.storage_folder.strip("/"), "excels", dn.id + ".xlsx")
assert dn.path == f"{Config.core.storage_folder}/excels/dn.id.xlsx"
assert os.path.exists(dn.path) is exists

def test_read_write_after_modify_path(self):
Expand Down Expand Up @@ -423,20 +425,20 @@ def test_is_not_downloadable_no_file(self):
reasons = dn.is_downloadable()
assert not reasons
assert len(reasons._reasons) == 1
assert str(NoFileToDownload(path, dn.id)) in reasons.reasons
assert str(NoFileToDownload(_normalize_path(path), dn.id)) in reasons.reasons

def test_is_not_downloadable_not_a_file(self):
path = os.path.join(pathlib.Path(__file__).parent.resolve(), "data_sample")
dn = ExcelDataNode("foo", Scope.SCENARIO, properties={"path": path, "exposed_type": "pandas"})
reasons = dn.is_downloadable()
assert not reasons
assert len(reasons._reasons) == 1
assert str(NotAFile(path, dn.id)) in reasons.reasons
assert str(NotAFile(_normalize_path(path), dn.id)) in reasons.reasons

def test_get_download_path(self):
path = os.path.join(pathlib.Path(__file__).parent.resolve(), "data_sample/example.xlsx")
dn = ExcelDataNode("foo", Scope.SCENARIO, properties={"path": path, "exposed_type": "pandas"})
assert dn._get_downloadable_path() == path
assert re.split(r"[\\/]", dn._get_downloadable_path()) == re.split(r"[\\/]", path)

def test_get_downloadable_path_with_not_existing_file(self):
dn = ExcelDataNode("foo", Scope.SCENARIO, properties={"path": "NOT_EXISTING.xlsx", "exposed_type": "pandas"})
Expand All @@ -457,7 +459,7 @@ def test_upload(self, excel_file, tmpdir_factory):

assert_frame_equal(dn.read()["Sheet1"], upload_content) # The data of dn should change to the uploaded content
assert dn.last_edit_date > old_last_edit_date
assert dn.path == old_xlsx_path # The path of the dn should not change
assert dn.path == _normalize_path(old_xlsx_path) # The path of the dn should not change

def test_upload_with_upload_check_pandas(self, excel_file, tmpdir_factory):
old_xlsx_path = tmpdir_factory.mktemp("data").join("df.xlsx").strpath
Expand Down Expand Up @@ -503,7 +505,7 @@ def check_data_column(upload_path, upload_data):

assert_frame_equal(dn.read()["Sheet1"], old_data) # The content of the dn should not change when upload fails
assert dn.last_edit_date == old_last_edit_date # The last edit date should not change when upload fails
assert dn.path == old_xlsx_path # The path of the dn should not change
assert dn.path == _normalize_path(old_xlsx_path) # The path of the dn should not change

# The upload should succeed when check_data_column() return True
assert dn._upload(excel_file, upload_checker=check_data_column)
Expand Down Expand Up @@ -552,7 +554,7 @@ def check_data_is_positive(upload_path, upload_data):

np.array_equal(dn.read()["Sheet1"], old_data) # The content of the dn should not change when upload fails
assert dn.last_edit_date == old_last_edit_date # The last edit date should not change when upload fails
assert dn.path == old_excel_path # The path of the dn should not change
assert dn.path == _normalize_path(old_excel_path) # The path of the dn should not change

# The upload should succeed when check_data_is_positive() return True
assert dn._upload(new_excel_path, upload_checker=check_data_is_positive)
14 changes: 8 additions & 6 deletions tests/core/data/test_json_data_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import json
import os
import pathlib
import re
import uuid
from dataclasses import dataclass
from enum import Enum
Expand All @@ -26,6 +27,7 @@
from taipy.common.config import Config
from taipy.common.config.common.scope import Scope
from taipy.common.config.exceptions.exceptions import InvalidConfigurationId
from taipy.core.common._utils import _normalize_path
from taipy.core.data._data_manager import _DataManager
from taipy.core.data._data_manager_factory import _DataManagerFactory
from taipy.core.data.data_node_id import DataNodeId
Expand Down Expand Up @@ -336,7 +338,7 @@ def test_filter(self, json_file):
)
def test_create_with_default_data(self, properties, exists):
dn = JSONDataNode("foo", Scope.SCENARIO, DataNodeId(f"dn_id_{uuid.uuid4()}"), properties=properties)
assert dn.path == os.path.join(Config.core.storage_folder.strip("/"), "jsons", dn.id + ".json")
assert dn.path == f"{Config.core.storage_folder}/jsons/dn.id.json"
assert os.path.exists(dn.path) is exists

def test_set_path(self):
Expand Down Expand Up @@ -405,20 +407,20 @@ def test_is_not_downloadable_no_file(self):
reasons = dn.is_downloadable()
assert not reasons
assert len(reasons._reasons) == 1
assert str(NoFileToDownload(path, dn.id)) in reasons.reasons
assert str(NoFileToDownload(_normalize_path(path), dn.id)) in reasons.reasons

def is_not_downloadable_not_a_file(self):
path = os.path.join(pathlib.Path(__file__).parent.resolve(), "data_sample/json")
dn = JSONDataNode("foo", Scope.SCENARIO, properties={"path": path})
reasons = dn.is_downloadable()
assert not reasons
assert len(reasons._reasons) == 1
assert str(NotAFile(path, dn.id)) in reasons.reasons
assert str(NotAFile(_normalize_path(path), dn.id)) in reasons.reasons

def test_get_download_path(self):
path = os.path.join(pathlib.Path(__file__).parent.resolve(), "data_sample/json/example_dict.json")
dn = JSONDataNode("foo", Scope.SCENARIO, properties={"path": path})
assert dn._get_downloadable_path() == path
assert re.split(r"[\\/]", dn._get_downloadable_path()) == re.split(r"[\\/]", path)

def test_get_download_path_with_not_existed_file(self):
dn = JSONDataNode("foo", Scope.SCENARIO, properties={"path": "NOT_EXISTED.json"})
Expand All @@ -440,7 +442,7 @@ def test_upload(self, json_file, tmpdir_factory):

assert dn.read() == upload_content # The content of the dn should change to the uploaded content
assert dn.last_edit_date > old_last_edit_date
assert dn.path == old_json_path # The path of the dn should not change
assert dn.path == _normalize_path(old_json_path) # The path of the dn should not change

def test_upload_with_upload_check(self, json_file, tmpdir_factory):
old_json_path = tmpdir_factory.mktemp("data").join("df.json").strpath
Expand Down Expand Up @@ -486,7 +488,7 @@ def check_data_keys(upload_path, upload_data):

assert dn.read() == old_data # The content of the dn should not change when upload fails
assert dn.last_edit_date == old_last_edit_date # The last edit date should not change when upload fails
assert dn.path == old_json_path # The path of the dn should not change
assert dn.path == _normalize_path(old_json_path) # The path of the dn should not change

# The upload should succeed when check_data_keys() return True
assert dn._upload(json_file, upload_checker=check_data_keys)
Loading

0 comments on commit cf7bc01

Please sign in to comment.