From d7595da0080e160ad3a2eccd107bae9248e9d221 Mon Sep 17 00:00:00 2001 From: jasonbrackman Date: Mon, 20 Mar 2023 16:45:55 -0700 Subject: [PATCH 1/6] siplified the as_start_path function and remove the #todo as it it can still be used by the callers if required. --- src/pyprojroot/root.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/pyprojroot/root.py b/src/pyprojroot/root.py index d2a5377..60f1eec 100644 --- a/src/pyprojroot/root.py +++ b/src/pyprojroot/root.py @@ -6,7 +6,7 @@ """ from pathlib import Path -from typing import Union, Tuple +from typing import Union, Tuple, Optional from .criterion import ( as_root_criterion as _as_root_criterion, @@ -15,13 +15,9 @@ ) -def as_start_path(start: Union[None, _PathType]) -> Path: - if start is None: - return Path.cwd() - if not isinstance(start, Path): - start = Path(start) - # TODO: consider `start = start.resolve()` - return start +def as_start_path(start: Optional[_PathType]) -> Path: + """A pathlib.Path object based on the common working directory or the optional input provided.""" + return Path.cwd() if start is None else Path(start) def find_root_with_reason( From 02e742dae562051364335d9463da761f90e0e0f7 Mon Sep 17 00:00:00 2001 From: jasonbrackman Date: Wed, 22 Mar 2023 06:06:59 -0700 Subject: [PATCH 2/6] Added an expanduser() before returning path to ensure that any input containing the home directory is expanded. --- src/pyprojroot/root.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/pyprojroot/root.py b/src/pyprojroot/root.py index 60f1eec..931c00f 100644 --- a/src/pyprojroot/root.py +++ b/src/pyprojroot/root.py @@ -16,8 +16,18 @@ def as_start_path(start: Optional[_PathType]) -> Path: - """A pathlib.Path object based on the common working directory or the optional input provided.""" - return Path.cwd() if start is None else Path(start) + """ + Returns a Path object based on the current working directory or the optional input + provided. If the input `start` parameter contains the '~' character, it will be + expanded to the home directory before being returned. + + :param start: Optional[str or Path], the path to start from. Defaults to None + which sets the starting path to the current working directory. + :return: Path, the Path object based on the starting path. + """ + if start is not None: + return Path(start).expanduser() + return Path.cwd() def find_root_with_reason( @@ -35,7 +45,6 @@ def find_root_with_reason( # Prepare inputs criterion = _as_root_criterion(criterion) start = as_start_path(start) - # Check start if start.is_dir() and criterion(start): return start, "Pass" From 71cb1f094a8b294752729edeec27b5905aeb1b98 Mon Sep 17 00:00:00 2001 From: jasonbrackman Date: Wed, 22 Mar 2023 06:14:30 -0700 Subject: [PATCH 3/6] removed space from end of lines. --- src/pyprojroot/root.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pyprojroot/root.py b/src/pyprojroot/root.py index 931c00f..7d75cf9 100644 --- a/src/pyprojroot/root.py +++ b/src/pyprojroot/root.py @@ -17,11 +17,11 @@ def as_start_path(start: Optional[_PathType]) -> Path: """ - Returns a Path object based on the current working directory or the optional input - provided. If the input `start` parameter contains the '~' character, it will be + Returns a Path object based on the current working directory or the optional input + provided. If the input `start` parameter contains the '~' character, it will be expanded to the home directory before being returned. - - :param start: Optional[str or Path], the path to start from. Defaults to None + + :param start: Optional[str or Path], the path to start from. Defaults to None which sets the starting path to the current working directory. :return: Path, the Path object based on the starting path. """ From decaf66b345d5d52d901dfb8de1271bdc72ed298 Mon Sep 17 00:00:00 2001 From: jasonbrackman Date: Wed, 22 Mar 2023 08:25:35 -0700 Subject: [PATCH 4/6] - add .resolve() to handle the '.' and '..' normalization for the Path return. - added test_root.py --- src/pyprojroot/root.py | 14 ++------- tests/test_here.py | 2 +- tests/test_root.py | 65 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 12 deletions(-) create mode 100644 tests/test_root.py diff --git a/src/pyprojroot/root.py b/src/pyprojroot/root.py index 7d75cf9..2260764 100644 --- a/src/pyprojroot/root.py +++ b/src/pyprojroot/root.py @@ -16,18 +16,10 @@ def as_start_path(start: Optional[_PathType]) -> Path: - """ - Returns a Path object based on the current working directory or the optional input - provided. If the input `start` parameter contains the '~' character, it will be - expanded to the home directory before being returned. - - :param start: Optional[str or Path], the path to start from. Defaults to None - which sets the starting path to the current working directory. - :return: Path, the Path object based on the starting path. - """ + """Convert path argument into normalised Path object.""" if start is not None: - return Path(start).expanduser() - return Path.cwd() + return Path(start).expanduser().resolve() + return Path.cwd().resolve() def find_root_with_reason( diff --git a/tests/test_here.py b/tests/test_here.py index d9a8afb..3883eba 100644 --- a/tests/test_here.py +++ b/tests/test_here.py @@ -2,7 +2,7 @@ import pytest -from pyprojroot.here import here +from src.pyprojroot.here import here @pytest.mark.parametrize( diff --git a/tests/test_root.py b/tests/test_root.py new file mode 100644 index 0000000..c8275aa --- /dev/null +++ b/tests/test_root.py @@ -0,0 +1,65 @@ + + +import os + +import pytest + +from src.pyprojroot.root import as_start_path, find_root + +MARKER = '.here' + + +@pytest.fixture +def temp_dir_structure(tmp_path): + """ + Create a pytest temp path for testing: + + tmp_path/ + └── dir1/ + ├── .here <-- marker file + └── dir2/ + """ + dir1 = tmp_path / 'dir1' + dir2 = dir1 / 'dir2' + os.makedirs(dir1 / MARKER) + os.makedirs(dir2) + return dir1, dir2 + + +def test_as_start_path_normalized_path(): + result01 = as_start_path('~') # Home + assert result01.is_dir() + + result02 = as_start_path("~/.") # Still at Home + assert result02.is_dir() + assert result01 == result02 + + result03 = as_start_path("~/..") # One directory below Home + assert result03.is_dir() + assert result03 != result02 + + +def test_find_root_marker_in_child(temp_dir_structure): + """Marker is in child folder, checking the parent should raise.""" + dir1, _ = temp_dir_structure + + os.chdir(dir1) + result = find_root(MARKER, start='.') + assert result.is_dir() + + with pytest.raises(RuntimeError, match="Project root not found."): + find_root(MARKER, start='..') + + +def test_find_root_marker_in_parent(temp_dir_structure): + """Marker in parent - child and parent should successfully find a root.""" + _, dir2 = temp_dir_structure + os.chdir(dir2) + result01 = find_root(MARKER, start='.') + assert result01.is_dir() + + result02 = find_root(MARKER, start='..') + assert result02.is_dir() + + + From 84ad46ae40dd07cab22e9d965a75482dc6ff9031 Mon Sep 17 00:00:00 2001 From: jasonbrackman Date: Wed, 22 Mar 2023 08:31:40 -0700 Subject: [PATCH 5/6] removed white space --- tests/test_root.py | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/tests/test_root.py b/tests/test_root.py index c8275aa..20092e0 100644 --- a/tests/test_root.py +++ b/tests/test_root.py @@ -1,65 +1,60 @@ - - import os import pytest from src.pyprojroot.root import as_start_path, find_root -MARKER = '.here' +MARKER = ".here" @pytest.fixture def temp_dir_structure(tmp_path): """ Create a pytest temp path for testing: - + tmp_path/ └── dir1/ ├── .here <-- marker file - └── dir2/ + └── dir2/ """ - dir1 = tmp_path / 'dir1' - dir2 = dir1 / 'dir2' + dir1 = tmp_path / "dir1" + dir2 = dir1 / "dir2" os.makedirs(dir1 / MARKER) os.makedirs(dir2) return dir1, dir2 def test_as_start_path_normalized_path(): - result01 = as_start_path('~') # Home + result01 = as_start_path("~") # Home assert result01.is_dir() - + result02 = as_start_path("~/.") # Still at Home assert result02.is_dir() assert result01 == result02 - + result03 = as_start_path("~/..") # One directory below Home assert result03.is_dir() assert result03 != result02 - + def test_find_root_marker_in_child(temp_dir_structure): """Marker is in child folder, checking the parent should raise.""" dir1, _ = temp_dir_structure - + os.chdir(dir1) - result = find_root(MARKER, start='.') + result = find_root(MARKER, start=".") assert result.is_dir() with pytest.raises(RuntimeError, match="Project root not found."): - find_root(MARKER, start='..') + find_root(MARKER, start="..") def test_find_root_marker_in_parent(temp_dir_structure): """Marker in parent - child and parent should successfully find a root.""" _, dir2 = temp_dir_structure os.chdir(dir2) - result01 = find_root(MARKER, start='.') + result01 = find_root(MARKER, start=".") assert result01.is_dir() - - result02 = find_root(MARKER, start='..') + + result02 = find_root(MARKER, start="..") assert result02.is_dir() - - - From 428b331a0668ad1fa334ee55b952cf6721857e0a Mon Sep 17 00:00:00 2001 From: jasonbrackman Date: Wed, 22 Mar 2023 08:34:41 -0700 Subject: [PATCH 6/6] Looks like I need to have the tests run from pyprojroot. I'll need to figure out how to do this locally. --- tests/test_here.py | 2 +- tests/test_root.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_here.py b/tests/test_here.py index 3883eba..d9a8afb 100644 --- a/tests/test_here.py +++ b/tests/test_here.py @@ -2,7 +2,7 @@ import pytest -from src.pyprojroot.here import here +from pyprojroot.here import here @pytest.mark.parametrize( diff --git a/tests/test_root.py b/tests/test_root.py index 20092e0..17b0e58 100644 --- a/tests/test_root.py +++ b/tests/test_root.py @@ -2,7 +2,7 @@ import pytest -from src.pyprojroot.root import as_start_path, find_root +from pyprojroot.root import as_start_path, find_root MARKER = ".here"