potentially misleading permissions error when incorrectly specifying python path
one of the solutions engineers reported an issue:

```
 rsconnect deploy fastapi -n connect_server -p .venv/ --entrypoint superbowlads:api ./
Checking arguments...                            [OK]
    Deploying ./ to server "https://SERVER.EXAMPLE.COM"
    Warning: Capturing the environment using 'pip freeze'.
             Consider creating a requirements.txt file instead.
Inspecting Python environment...                 Traceback (most recent call last):
  File "/USERHOME/PATH/TO/.venv/lib/python3.9/site-packages/rsconnect/actions.py", line 74, in cli_feedback
    yield
.....
PermissionError: [Errno 13] Permission denied: '.venv/'
[ERROR]
Internal error: [Errno 13] Permission denied: '.venv/'
```

after consulting the docs and some back and forth realized `-p` needed the path to the python executable.

My hypothesis is that the check https://github.com/rstudio/rsconnect-python/blob/master/rsconnect/actions.py#L111 may need to be slightly more restrictive to check if its actually a file.

I'll try to replicate/get a PR over but filing this issue as the starting point.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 89e018f..a02d592 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ### Added
 
 - The `--cacert` option now supports certificate files encoded in the Distinguished Encoding Rules (DER) binary format. Certificate files with DER encoding must end in a `.cer` or `.der` suffix.
+- The `--python` option now provides additional user guidance when an invalid path is provided.
 
 ### Changed
 
diff --git a/rsconnect/bundle.py b/rsconnect/bundle.py
index 3f60205..80b8112 100644
--- a/rsconnect/bundle.py
+++ b/rsconnect/bundle.py
@@ -1074,19 +1074,24 @@ def are_apis_supported_on_server(connect_details):
     return connect_details["python"]["api_enabled"]
 
 
-def which_python(python, env=os.environ):
-    """Determine which python binary should be used.
-
-    In priority order:
-    * --python specified on the command line
-    * the python binary running this script
-    """
-    if python:
-        if not (exists(python) and os.access(python, os.X_OK)):
-            raise RSConnectException('The file, "%s", does not exist or is not executable.' % python)
-        return python
-
-    return sys.executable
+def which_python(python: typing.Optional[str] = None):
+    """Determines which Python executable to use.
+
+    If the :param python: is provided, then validation is performed to check if the path is an executable file. If
+    None, the invoking system Python executable location is returned.
+
+    :param python: (Optional) path to a python executable.
+    :return: :param python: or `sys.executable`.
+    """
+    if python is None:
+        return sys.executable
+    if not exists(python):
+        raise RSConnectException(f"The path '{python}' does not exist. Expected a Python executable.")
+    if isdir(python):
+        raise RSConnectException(f"The path '{python}' is a directory. Expected a Python executable.")
+    if not os.access(python, os.X_OK):
+        raise RSConnectException(f"The path '{python}' is not executable. Expected a Python executable")
+    return python
 
 
 def inspect_environment(
diff --git a/tests/test_bundle.py b/tests/test_bundle.py
index a3276c4..fce50af 100644
--- a/tests/test_bundle.py
+++ b/tests/test_bundle.py
@@ -646,13 +646,6 @@ class TestBundle(TestCase):
         finally:
             shutil.rmtree(directory)
 
-    def test_which_python(self):
-        with self.assertRaises(RSConnectException):
-            which_python("fake.file")
-
-        self.assertEqual(which_python(sys.executable), sys.executable)
-        self.assertEqual(which_python(None), sys.executable)
-
     def test_default_title(self):
         self.assertEqual(_default_title("testing.txt"), "testing")
         self.assertEqual(_default_title("this.is.a.test.ext"), "this.is.a.test")
@@ -781,3 +774,30 @@ def test_get_python_env_info(
 
         assert python == expected_python
         assert environment == expected_environment
+
+
+class WhichPythonTestCase(TestCase):
+    def test_default(self):
+        self.assertEqual(which_python(), sys.executable)
+
+    def test_none(self):
+        self.assertEqual(which_python(None), sys.executable)
+
+    def test_sys(self):
+        self.assertEqual(which_python(sys.executable), sys.executable)
+
+    def test_does_not_exist(self):
+        with tempfile.NamedTemporaryFile() as tmpfile:
+            name = tmpfile.name
+        with self.assertRaises(RSConnectException):
+            which_python(name)
+
+    def test_is_directory(self):
+        with tempfile.TemporaryDirectory() as tmpdir:
+            with self.assertRaises(RSConnectException):
+                which_python(tmpdir)
+
+    def test_is_not_executable(self):
+        with tempfile.NamedTemporaryFile() as tmpfile:
+            with self.assertRaises(RSConnectException):
+                which_python(tmpfile.name)
['rsconnect/bundle.py', 'tests/test_bundle.py', 'CHANGELOG.md']