Skip to content

Commit

Permalink
Merge branch 'master' into dependabot/pip/zipp-3.21.0
Browse files Browse the repository at this point in the history
  • Loading branch information
JuroOravec authored Dec 2, 2024
2 parents 27985f0 + 025562a commit 5250f51
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 82 deletions.
15 changes: 9 additions & 6 deletions src/django_components/component_media.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,8 @@ def _normalize_media_filepath(filepath: Any) -> Union[str, SafeData]:
return filepath

if isinstance(filepath, (Path, os.PathLike)) or hasattr(filepath, "__fspath__"):
filepath = filepath.__fspath__()
# In case of Windows OS, convert to forward slashes
filepath = Path(filepath.__fspath__()).as_posix()

if isinstance(filepath, bytes):
filepath = filepath.decode("utf-8")
Expand Down Expand Up @@ -293,19 +294,21 @@ def _resolve_component_relative_files(attrs: MutableMapping) -> None:
# If not, don't modify anything.
def resolve_file(filepath: Union[str, SafeData]) -> Union[str, SafeData]:
if isinstance(filepath, str):
maybe_resolved_filepath = os.path.join(comp_dir_abs, filepath)
component_import_filepath = os.path.join(comp_dir_rel, filepath)
filepath_abs = os.path.join(comp_dir_abs, filepath)
# NOTE: The paths to resources need to use POSIX (forward slashes) for Django to wor
# See https://github.com/EmilStenstrom/django-components/issues/796
filepath_rel_to_comp_dir = Path(os.path.join(comp_dir_rel, filepath)).as_posix()

if os.path.isfile(maybe_resolved_filepath):
if os.path.isfile(filepath_abs):
# NOTE: It's important to use `repr`, so we don't trigger __str__ on SafeStrings
logger.debug(
f"Interpreting template '{repr(filepath)}' of component '{module_name}'"
" relatively to component file"
)

return component_import_filepath
return filepath
return filepath_rel_to_comp_dir

# If resolved absolute path does NOT exist or filepath is NOT a string, then return as is
logger.debug(
f"Interpreting template '{repr(filepath)}' of component '{module_name}'"
" relatively to components directory"
Expand Down
12 changes: 5 additions & 7 deletions src/django_components/util/loader.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import glob
import os
from pathlib import Path
from pathlib import Path, PurePosixPath, PureWindowsPath
from typing import List, NamedTuple, Optional, Set, Union

from django.apps import apps
Expand Down Expand Up @@ -211,13 +211,11 @@ def _filepath_to_python_module(
- Then the path relative to project root is `app/components/mycomp.py`
- Which we then turn into python import path `app.components.mycomp`
"""
rel_path = os.path.relpath(file_path, start=root_fs_path)
rel_path_without_suffix = str(Path(rel_path).with_suffix(""))
path_cls = PureWindowsPath if os.name == "nt" else PurePosixPath

# NOTE: `Path` normalizes paths to use `/` as separator, while `os.path`
# uses `os.path.sep`.
sep = os.path.sep if os.path.sep in rel_path_without_suffix else "/"
module_name = rel_path_without_suffix.replace(sep, ".")
rel_path = path_cls(file_path).relative_to(path_cls(root_fs_path))
rel_path_parts = rel_path.with_suffix("").parts
module_name = ".".join(rel_path_parts)

# Combine with the base module path
full_module_name = f"{root_module_path}.{module_name}" if root_module_path else module_name
Expand Down
6 changes: 4 additions & 2 deletions tests/e2e/utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import functools
import subprocess
import sys
import time
from pathlib import Path

Expand Down Expand Up @@ -39,12 +40,13 @@ def run_django_dev_server():
"""Fixture to run Django development server in the background."""
# Get the path where testserver is defined, so the command doesn't depend
# on user's current working directory.
testserver_dir = (Path(__file__).parent / "testserver").absolute()
testserver_dir = (Path(__file__).parent / "testserver").resolve()

# Start the Django dev server in the background
print("Starting Django dev server...")
proc = subprocess.Popen(
["python", "manage.py", "runserver", f"127.0.0.1:{TEST_SERVER_PORT}", "--noreload"],
# NOTE: Use `sys.executable` so this works both for Unix and Windows OS
[sys.executable, "manage.py", "runserver", f"127.0.0.1:{TEST_SERVER_PORT}", "--noreload"],
cwd=testserver_dir,
)

Expand Down
43 changes: 23 additions & 20 deletions tests/test_finders.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ def do_collect():
post_process=True,
)
collected = cmd.collect()

# Convert collected paths from string to Path, so we can run tests on both Unix and Windows
collected = {key: [Path(item) for item in items] for key, items in collected.items()}
return collected


Expand Down Expand Up @@ -86,10 +89,10 @@ def test_python_and_html_included(self):
collected = do_collect()

# Check that the component files are NOT loaded when our finder is NOT added
self.assertNotIn("staticfiles/staticfiles.css", collected["modified"])
self.assertNotIn("staticfiles/staticfiles.js", collected["modified"])
self.assertNotIn("staticfiles/staticfiles.html", collected["modified"])
self.assertNotIn("staticfiles/staticfiles.py", collected["modified"])
self.assertNotIn(Path("staticfiles/staticfiles.css"), collected["modified"])
self.assertNotIn(Path("staticfiles/staticfiles.js"), collected["modified"])
self.assertNotIn(Path("staticfiles/staticfiles.html"), collected["modified"])
self.assertNotIn(Path("staticfiles/staticfiles.py"), collected["modified"])

self.assertListEqual(collected["unmodified"], [])
self.assertListEqual(collected["post_processed"], [])
Expand All @@ -109,10 +112,10 @@ def test_python_and_html_omitted(self):
collected = do_collect()

# Check that our staticfiles_finder finds the files and OMITS .py and .html files
self.assertIn("staticfiles/staticfiles.css", collected["modified"])
self.assertIn("staticfiles/staticfiles.js", collected["modified"])
self.assertNotIn("staticfiles/staticfiles.html", collected["modified"])
self.assertNotIn("staticfiles/staticfiles.py", collected["modified"])
self.assertIn(Path("staticfiles/staticfiles.css"), collected["modified"])
self.assertIn(Path("staticfiles/staticfiles.js"), collected["modified"])
self.assertNotIn(Path("staticfiles/staticfiles.html"), collected["modified"])
self.assertNotIn(Path("staticfiles/staticfiles.py"), collected["modified"])

self.assertListEqual(collected["unmodified"], [])
self.assertListEqual(collected["post_processed"], [])
Expand All @@ -138,10 +141,10 @@ def test_set_static_files_allowed(self):
collected = do_collect()

# Check that our staticfiles_finder finds the files and OMITS .py and .html files
self.assertNotIn("staticfiles/staticfiles.css", collected["modified"])
self.assertIn("staticfiles/staticfiles.js", collected["modified"])
self.assertNotIn("staticfiles/staticfiles.html", collected["modified"])
self.assertNotIn("staticfiles/staticfiles.py", collected["modified"])
self.assertNotIn(Path("staticfiles/staticfiles.css"), collected["modified"])
self.assertIn(Path("staticfiles/staticfiles.js"), collected["modified"])
self.assertNotIn(Path("staticfiles/staticfiles.html"), collected["modified"])
self.assertNotIn(Path("staticfiles/staticfiles.py"), collected["modified"])

self.assertListEqual(collected["unmodified"], [])
self.assertListEqual(collected["post_processed"], [])
Expand Down Expand Up @@ -169,10 +172,10 @@ def test_set_forbidden_files(self):
collected = do_collect()

# Check that our staticfiles_finder finds the files and OMITS .py and .html files
self.assertIn("staticfiles/staticfiles.css", collected["modified"])
self.assertNotIn("staticfiles/staticfiles.js", collected["modified"])
self.assertIn("staticfiles/staticfiles.html", collected["modified"])
self.assertIn("staticfiles/staticfiles.py", collected["modified"])
self.assertIn(Path("staticfiles/staticfiles.css"), collected["modified"])
self.assertNotIn(Path("staticfiles/staticfiles.js"), collected["modified"])
self.assertIn(Path("staticfiles/staticfiles.html"), collected["modified"])
self.assertIn(Path("staticfiles/staticfiles.py"), collected["modified"])

self.assertListEqual(collected["unmodified"], [])
self.assertListEqual(collected["post_processed"], [])
Expand Down Expand Up @@ -201,10 +204,10 @@ def test_set_both_allowed_and_forbidden_files(self):
collected = do_collect()

# Check that our staticfiles_finder finds the files and OMITS .py and .html files
self.assertIn("staticfiles/staticfiles.css", collected["modified"])
self.assertNotIn("staticfiles/staticfiles.js", collected["modified"])
self.assertNotIn("staticfiles/staticfiles.html", collected["modified"])
self.assertNotIn("staticfiles/staticfiles.py", collected["modified"])
self.assertIn(Path("staticfiles/staticfiles.css"), collected["modified"])
self.assertNotIn(Path("staticfiles/staticfiles.js"), collected["modified"])
self.assertNotIn(Path("staticfiles/staticfiles.html"), collected["modified"])
self.assertNotIn(Path("staticfiles/staticfiles.py"), collected["modified"])

self.assertListEqual(collected["unmodified"], [])
self.assertListEqual(collected["post_processed"], [])
116 changes: 69 additions & 47 deletions tests/test_loader.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import os
import re
from pathlib import Path
from unittest.mock import MagicMock, patch

Expand Down Expand Up @@ -35,8 +34,10 @@ def test_get_dirs__base_dir(self):

# Apps with a `components` dir
self.assertEqual(len(apps_dirs), 2)
self.assertRegex(str(apps_dirs[0]), re.compile(r"\/django_components\/components$"))
self.assertRegex(str(apps_dirs[1]), re.compile(r"\/tests\/test_app\/components$"))

# NOTE: Compare parts so that the test works on Windows too
self.assertTupleEqual(apps_dirs[0].parts[-2:], ("django_components", "components"))
self.assertTupleEqual(apps_dirs[1].parts[-3:], ("tests", "test_app", "components"))

@override_settings(
BASE_DIR=Path(__file__).parent.resolve() / "test_structures" / "test_structure_1", # noqa
Expand All @@ -49,8 +50,10 @@ def test_get_dirs__base_dir__complex(self):

# Apps with a `components` dir
self.assertEqual(len(apps_dirs), 2)
self.assertRegex(str(apps_dirs[0]), re.compile(r"\/django_components\/components$"))
self.assertRegex(str(apps_dirs[1]), re.compile(r"\/tests\/test_app\/components$"))

# NOTE: Compare parts so that the test works on Windows too
self.assertTupleEqual(apps_dirs[0].parts[-2:], ("django_components", "components"))
self.assertTupleEqual(apps_dirs[1].parts[-3:], ("tests", "test_app", "components"))

expected = [
Path(__file__).parent.resolve() / "test_structures" / "test_structure_1" / "components",
Expand All @@ -76,8 +79,10 @@ def test_get_dirs__components_dirs(self, mock_warning: MagicMock):

# Apps with a `components` dir
self.assertEqual(len(apps_dirs), 2)
self.assertRegex(str(apps_dirs[0]), re.compile(r"\/django_components\/components$"))
self.assertRegex(str(apps_dirs[1]), re.compile(r"\/tests\/test_app\/components$"))

# NOTE: Compare parts so that the test works on Windows too
self.assertTupleEqual(apps_dirs[0].parts[-2:], ("django_components", "components"))
self.assertTupleEqual(apps_dirs[1].parts[-3:], ("tests", "test_app", "components"))

self.assertEqual(
own_dirs,
Expand All @@ -104,8 +109,10 @@ def test_get_dirs__components_dirs__empty(self):

# Apps with a `components` dir
self.assertEqual(len(apps_dirs), 2)
self.assertRegex(str(apps_dirs[0]), re.compile(r"\/django_components\/components$"))
self.assertRegex(str(apps_dirs[1]), re.compile(r"\/tests\/test_app\/components$"))

# NOTE: Compare parts so that the test works on Windows too
self.assertTupleEqual(apps_dirs[0].parts[-2:], ("django_components", "components"))
self.assertTupleEqual(apps_dirs[1].parts[-3:], ("tests", "test_app", "components"))

@override_settings(
BASE_DIR=Path(__file__).parent.resolve(),
Expand Down Expand Up @@ -141,7 +148,9 @@ def test_get_dirs__app_dirs(self):

# Apps with a `components` dir
self.assertEqual(len(apps_dirs), 1)
self.assertRegex(str(apps_dirs[0]), re.compile(r"\/tests\/test_app\/custom_comps_dir$"))

# NOTE: Compare parts so that the test works on Windows too
self.assertTupleEqual(apps_dirs[0].parts[-3:], ("tests", "test_app", "custom_comps_dir"))

self.assertEqual(
own_dirs,
Expand Down Expand Up @@ -204,8 +213,10 @@ def test_get_dirs__nested_apps(self):

# Apps with a `components` dir
self.assertEqual(len(apps_dirs), 2)
self.assertRegex(str(apps_dirs[0]), re.compile(r"\/django_components\/components$"))
self.assertRegex(str(apps_dirs[1]), re.compile(r"\/tests\/test_app_nested\/app\/components$"))

# NOTE: Compare parts so that the test works on Windows too
self.assertTupleEqual(apps_dirs[0].parts[-2:], ("django_components", "components"))
self.assertTupleEqual(apps_dirs[1].parts[-4:], ("tests", "test_app_nested", "app", "components"))

self.assertEqual(
own_dirs,
Expand All @@ -225,7 +236,7 @@ def test_get_files__py(self):
files = sorted(get_component_files(".py"))

dot_paths = [f.dot_path for f in files]
file_paths = [str(f.filepath) for f in files]
file_paths = [f.filepath for f in files]

self.assertEqual(
dot_paths,
Expand All @@ -243,20 +254,20 @@ def test_get_files__py(self):
],
)

self.assertEqual(
[
file_paths[0].endswith("tests/components/__init__.py"),
file_paths[1].endswith("tests/components/multi_file/multi_file.py"),
file_paths[2].endswith("tests/components/relative_file/relative_file.py"),
file_paths[3].endswith("tests/components/relative_file_pathobj/relative_file_pathobj.py"),
file_paths[4].endswith("tests/components/single_file.py"),
file_paths[5].endswith("tests/components/staticfiles/staticfiles.py"),
file_paths[6].endswith("tests/components/urls.py"),
file_paths[7].endswith("django_components/components/__init__.py"),
file_paths[8].endswith("django_components/components/dynamic.py"),
file_paths[9].endswith("tests/test_app/components/app_lvl_comp/app_lvl_comp.py"),
],
[True for _ in range(len(file_paths))],
# NOTE: Compare parts so that the test works on Windows too
self.assertTupleEqual(file_paths[0].parts[-3:], ("tests", "components", "__init__.py"))
self.assertTupleEqual(file_paths[1].parts[-4:], ("tests", "components", "multi_file", "multi_file.py"))
self.assertTupleEqual(file_paths[2].parts[-4:], ("tests", "components", "relative_file", "relative_file.py"))
self.assertTupleEqual(
file_paths[3].parts[-4:], ("tests", "components", "relative_file_pathobj", "relative_file_pathobj.py")
)
self.assertTupleEqual(file_paths[4].parts[-3:], ("tests", "components", "single_file.py"))
self.assertTupleEqual(file_paths[5].parts[-4:], ("tests", "components", "staticfiles", "staticfiles.py"))
self.assertTupleEqual(file_paths[6].parts[-3:], ("tests", "components", "urls.py"))
self.assertTupleEqual(file_paths[7].parts[-3:], ("django_components", "components", "__init__.py"))
self.assertTupleEqual(file_paths[8].parts[-3:], ("django_components", "components", "dynamic.py"))
self.assertTupleEqual(
file_paths[9].parts[-5:], ("tests", "test_app", "components", "app_lvl_comp", "app_lvl_comp.py")
)

@override_settings(
Expand All @@ -266,9 +277,7 @@ def test_get_files__js(self):
files = sorted(get_component_files(".js"))

dot_paths = [f.dot_path for f in files]
file_paths = [str(f.filepath) for f in files]

print(file_paths)
file_paths = [f.filepath for f in files]

self.assertEqual(
dot_paths,
Expand All @@ -280,14 +289,14 @@ def test_get_files__js(self):
],
)

self.assertEqual(
[
file_paths[0].endswith("tests/components/relative_file/relative_file.js"),
file_paths[1].endswith("tests/components/relative_file_pathobj/relative_file_pathobj.js"),
file_paths[2].endswith("tests/components/staticfiles/staticfiles.js"),
file_paths[3].endswith("tests/test_app/components/app_lvl_comp/app_lvl_comp.js"),
],
[True for _ in range(len(file_paths))],
# NOTE: Compare parts so that the test works on Windows too
self.assertTupleEqual(file_paths[0].parts[-4:], ("tests", "components", "relative_file", "relative_file.js"))
self.assertTupleEqual(
file_paths[1].parts[-4:], ("tests", "components", "relative_file_pathobj", "relative_file_pathobj.js")
)
self.assertTupleEqual(file_paths[2].parts[-4:], ("tests", "components", "staticfiles", "staticfiles.js"))
self.assertTupleEqual(
file_paths[3].parts[-5:], ("tests", "test_app", "components", "app_lvl_comp", "app_lvl_comp.js")
)


Expand All @@ -307,31 +316,44 @@ def test_prepares_path(self):
"tests.components.relative_file.relative_file",
)

def test_handles_nonlinux_paths(self):
base_path = str(settings.BASE_DIR).replace("/", "//")
def test_handles_separators_based_on_os_name(self):
base_path = str(settings.BASE_DIR)

with patch("os.name", new="posix"):
the_path = base_path + "/" + "tests.py"
self.assertEqual(
_filepath_to_python_module(the_path, base_path, None),
"tests",
)

the_path = base_path + "/" + "tests/components/relative_file/relative_file.py"
self.assertEqual(
_filepath_to_python_module(the_path, base_path, None),
"tests.components.relative_file.relative_file",
)

with patch("os.path.sep", new="//"):
the_path = os.path.join(base_path, "tests.py")
base_path = str(settings.BASE_DIR).replace("/", "\\")
with patch("os.name", new="nt"):
the_path = base_path + "\\" + "tests.py"
self.assertEqual(
_filepath_to_python_module(the_path, base_path, None),
"tests",
)

the_path = os.path.join(base_path, "tests//components//relative_file//relative_file.py")
the_path = base_path + "\\" + "tests\\components\\relative_file\\relative_file.py"
self.assertEqual(
_filepath_to_python_module(the_path, base_path, None),
"tests.components.relative_file.relative_file",
)

base_path = str(settings.BASE_DIR).replace("//", "\\")
with patch("os.path.sep", new="\\"):
the_path = os.path.join(base_path, "tests.py")
# NOTE: Windows should handle also POSIX separator
the_path = base_path + "/" + "tests.py"
self.assertEqual(
_filepath_to_python_module(the_path, base_path, None),
"tests",
)

the_path = os.path.join(base_path, "tests\\components\\relative_file\\relative_file.py")
the_path = base_path + "/" + "tests/components/relative_file/relative_file.py"
self.assertEqual(
_filepath_to_python_module(the_path, base_path, None),
"tests.components.relative_file.relative_file",
Expand Down

0 comments on commit 5250f51

Please sign in to comment.