diff options
author | Nicky Sielicki <sielicki@yandex.com> | 2021-06-17 16:27:00 -0500 |
---|---|---|
committer | David Lord <davidism@gmail.com> | 2022-02-19 10:33:06 -0800 |
commit | 06b158831f30b3ee986705d143e5264fee9c765e (patch) | |
tree | 76ee50d2164e0b1b52550aef0173a427856f291e | |
parent | 24992ce4606c62cff4df5ceabcb5a4215d991a1d (diff) | |
download | click-06b158831f30b3ee986705d143e5264fee9c765e.tar.gz |
add an ability to check that a Path has an executable bit set
-rw-r--r-- | CHANGES.rst | 3 | ||||
-rw-r--r-- | src/click/types.py | 33 |
2 files changed, 27 insertions, 9 deletions
diff --git a/CHANGES.rst b/CHANGES.rst index 9d30887..8481685 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,7 +3,8 @@ Version 8.1.0 ------------- -Unreleased +- An an argument to ``Path`` which mirrors writable/readable, but for + executable bits. :issue:`1961` - Drop support for Python 3.6. :pr:`2129` - Remove previously deprecated code. :pr:`2130` diff --git a/src/click/types.py b/src/click/types.py index 550c6ff..e8a5c88 100644 --- a/src/click/types.py +++ b/src/click/types.py @@ -753,8 +753,9 @@ class Path(ParamType): exist, then all further checks are silently skipped. :param file_okay: Allow a file as a value. :param dir_okay: Allow a directory as a value. - :param writable: The file or directory must be writable. - :param readable: The file or directory must be readable. + :param readable: if true, a readable check is performed. + :param writable: if true, a writable check is performed. + :param executable: if true, an executable check is performed. :param resolve_path: Make the value absolute and resolve any symlinks. A ``~`` is not expanded, as this is supposed to be done by the shell only. @@ -765,6 +766,9 @@ class Path(ParamType): ``None``, keep Python's default, which is ``str``. Useful to convert to :class:`pathlib.Path`. + .. versionchanged:: 8.1 + Added the ``executable`` parameter. + .. versionchanged:: 8.0 Allow passing ``type=pathlib.Path``. @@ -779,8 +783,9 @@ class Path(ParamType): exists: bool = False, file_okay: bool = True, dir_okay: bool = True, - writable: bool = False, readable: bool = True, + writable: bool = False, + executable: bool = False, resolve_path: bool = False, allow_dash: bool = False, path_type: t.Optional[t.Type] = None, @@ -788,8 +793,9 @@ class Path(ParamType): self.exists = exists self.file_okay = file_okay self.dir_okay = dir_okay - self.writable = writable self.readable = readable + self.writable = writable + self.executable = executable self.resolve_path = resolve_path self.allow_dash = allow_dash self.type = path_type @@ -862,23 +868,34 @@ class Path(ParamType): ) if not self.dir_okay and stat.S_ISDIR(st.st_mode): self.fail( - _("{name} {filename!r} is a directory.").format( + _("{name} '{filename}' is a directory.").format( + name=self.name.title(), filename=os.fsdecode(value) + ), + param, + ctx, + ) + + if self.readable and not os.access(rv, os.R_OK): + self.fail( + _("{name} '{filename}' is not executable.").format( name=self.name.title(), filename=os.fsdecode(value) ), param, ctx, ) + if self.writable and not os.access(rv, os.W_OK): self.fail( - _("{name} {filename!r} is not writable.").format( + _("{name} '{filename}' is not writable.").format( name=self.name.title(), filename=os.fsdecode(value) ), param, ctx, ) - if self.readable and not os.access(rv, os.R_OK): + + if self.executable and not os.access(value, os.X_OK): self.fail( - _("{name} {filename!r} is not readable.").format( + _("{name} {filename!r} is not executable.").format( name=self.name.title(), filename=os.fsdecode(value) ), param, |