feat(runfiles): create pathlib api for runfiles library#3694
feat(runfiles): create pathlib api for runfiles library#3694rickeylev wants to merge 3 commits intobazel-contrib:mainfrom
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a Path class to the runfiles library, which implements the pathlib.PurePath API to allow for idiomatic path manipulation of Bazel runfiles. It also adds a root() method to the Runfiles class to return these new path objects. The review feedback suggests improving the robustness of the Path class by handling potential None values for the internal _runfiles attribute in str and runfiles_root methods, and using a more robust string formatting in repr.
| def __str__(self) -> str: | ||
| path_posix = super().__str__().replace("\\", "/") | ||
| if not path_posix or path_posix == ".": | ||
| # pylint: disable=protected-access | ||
| return self._runfiles._python_runfiles_root # type: ignore | ||
| resolved = self._runfiles.Rlocation(path_posix, source_repo=self._source_repo) # type: ignore | ||
| return resolved if resolved is not None else super().__str__() |
There was a problem hiding this comment.
The __str__ method assumes that self._runfiles is not None. However, since Path is a public class, it can be instantiated directly without a Runfiles instance (e.g., runfiles.Path("foo")), in which case self._runfiles will be None and this method will raise an AttributeError. It should fallback to the default string representation if no Runfiles instance is associated.
| def __str__(self) -> str: | |
| path_posix = super().__str__().replace("\\", "/") | |
| if not path_posix or path_posix == ".": | |
| # pylint: disable=protected-access | |
| return self._runfiles._python_runfiles_root # type: ignore | |
| resolved = self._runfiles.Rlocation(path_posix, source_repo=self._source_repo) # type: ignore | |
| return resolved if resolved is not None else super().__str__() | |
| def __str__(self) -> str: | |
| if self._runfiles is None: | |
| return super().__str__() | |
| path_posix = super().__str__().replace("\\", "/") | |
| if not path_posix or path_posix == ".": | |
| # pylint: disable=protected-access | |
| return self._runfiles._python_runfiles_root # type: ignore | |
| resolved = self._runfiles.Rlocation(path_posix, source_repo=self._source_repo) # type: ignore | |
| return resolved if resolved is not None else super().__str__() |
| def runfiles_root(self) -> "Path": | ||
| """Returns a Path object representing the runfiles root.""" | ||
| return self._runfiles.root(source_repo=self._source_repo) # type: ignore |
There was a problem hiding this comment.
Similar to __str__, runfiles_root will crash if self._runfiles is None. It should check for the presence of the runfiles instance and perhaps raise a more descriptive error if it's missing.
| def runfiles_root(self) -> "Path": | |
| """Returns a Path object representing the runfiles root.""" | |
| return self._runfiles.root(source_repo=self._source_repo) # type: ignore | |
| def runfiles_root(self) -> "Path": | |
| """Returns a Path object representing the runfiles root.""" | |
| if self._runfiles is None: | |
| raise ValueError("Path object is not associated with a Runfiles instance") | |
| return self._runfiles.root(source_repo=self._source_repo) # type: ignore |
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
| def tearDown(self) -> None: | ||
| self.tmpdir.cleanup() | ||
|
|
||
| def testPathApi(self) -> None: |
There was a problem hiding this comment.
nit, maybe it is personal preference but itwould be nice to have the tests named in snake case here except for the special methods.
| END_UNRELEASED_TEMPLATE | ||
| --> | ||
|
|
||
| ## Unreleased |
There was a problem hiding this comment.
Please use the template :)
This adds a pathlib.Path-based API for interacting with runfiles. This
makes it easier to interact with runfiles.
Fixes #3296