Repository: xonsh/xonsh

Issue:
xpip fails to correctly install on fedora
```
The following xontribs are enabled but not installed: 
   ['vox', 'autovox', 'avox']
Please make sure that they are installed correctly by checking https://xonsh.github.io/awesome-xontribs/
/usr/lib/python3.12/site-packages/prompt_toolkit/application/application.py:955: DeprecationWarning: There is no current event loop
  loop = asyncio.get_event_loop()
astraluma@nandi ~ @ which xpip
/usr/bin/python3 -m pip @(['install', '--user'] + $args[1:] if $args and $args[0] == 'install' else $args)
astraluma@nandi ~ @ xontrib list
coreutils  not-loaded
astraluma@nandi ~ @ xpip install xontrib-avox xontrib-vox
Requirement already satisfied: xontrib-avox in ./.local/lib/python3.12/site-packages (0.8)
Requirement already satisfied: xontrib-vox in ./.local/lib/python3.12/site-packages (0.0.1)
Requirement already satisfied: xonsh>=0.12.5 in /usr/lib/python3.12/site-packages (from xontrib-vox) (0.14.1)
astraluma@nandi ~ @ source ~/.xonshrc
The following xontribs are enabled but not installed: 
   ['vox', 'autovox', 'avox']
Please make sure that they are installed correctly by checking https://xonsh.github.io/awesome-xontribs/
astraluma@nandi ~ @ xonsh --version
xonsh/0.14.1
astraluma@nandi ~ @ which xonsh
/usr/bin/xonsh
```

Installed on fedora via dnf

## Expected Behavior
xpip should be able to install packages that are available to xonsh

## Current Behavior
Packages installed by xpip are not available to xonsh

## For community
⬇️  **Please click the 👍 reaction instead of leaving a `+1` or 👍  comment**



Discussion:
```
✔ 12:00:43 [yennefer] @ import sys
~ 
✔ 12:01:12 [yennefer] @ sys.path
['/usr/lib64/python312.zip',
 '/usr/lib64/python3.12',
 '/usr/lib64/python3.12/lib-dynload',
 '/usr/lib64/python3.12/site-packages',
 '/usr/lib/python3.12/site-packages']
~ 
✔ 12:01:13 [yennefer] @ python
Python 3.12.0 (main, Oct  2 2023, 00:00:00) [GCC 13.2.1 20230918 (Red Hat 13.2.1-3)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.path
['', '/usr/lib64/python312.zip', '/usr/lib64/python3.12', '/usr/lib64/python3.12/lib-dynload', '/home/tcaswell/.local/lib/python3.12/site-packages', '/usr/lib64/python3.12/site-packages', '/usr/lib/python3.12/site-packages']
>>> 
~ 
✔ 12:03:14 [yennefer] @ sys.executable
'/usr/bin/python3'
~ 

```

I think the problem is that the home-directory `user` path is non it `sys.path` in xonsh.
Adding this

```python
import sys
from site import getusersitepackages
sys.path.append(getusersitepackages())

```

to the top of my `.xonshrc` fixes the problem.
xpip was designed to just work--it should always point to the correct environment to install xonsh modules into.

If that's no longer the case, it should at least print a warning that it won't work.

> I think the problem is that the home-directory `user` path is non it `sys.path` in xonsh.

Is the home-directory `user` path set in that distribution of python?

> xpip was designed to just work--it should always point to the correct environment to install xonsh modules into.

That's right. PR with fix is welcome!
> > xpip was designed to just work--it should always point to the correct environment to install xonsh modules into.
> 
> That's right. PR with fix is welcome!

I no longer remember what the ideal handling of xonsh installation environments is, and it might vary by installation method.

I fixed it for my own zipapp version by making a xonsh-specific site directory--see https://gitlab.com/doyouevenclick/xonsh/-/blob/master/main.py?ref_type=heads

I would much rather a current xore make a design decision in this regard--share a site dir with python? have xonsh manage its own? does that apply if xonsh is installed with pipx or conda?

(for the record, managing the user's site directory is a big reason for #3275 )

Resolving patch:
diff --git a/xonsh/xontribs.py b/xonsh/xontribs.py
--- a/xonsh/xontribs.py
+++ b/xonsh/xontribs.py
@@ -3,6 +3,7 @@
 import importlib
 import importlib.util
 import json
+import os
 import sys
 import typing as tp
 from enum import IntEnum
@@ -81,8 +82,22 @@ def get_xontribs() -> dict[str, Xontrib]:
     return dict(_get_installed_xontribs())
 
 
+def _patch_in_userdir():
+    """
+    Patch in user site packages directory.
+
+    If xonsh is installed in non-writeable location, then xontribs will end up
+    there, so we make them accessible."""
+    if not os.access(os.path.dirname(sys.executable), os.W_OK):
+        from site import getusersitepackages
+
+        if (user_site_packages := getusersitepackages()) not in set(sys.path):
+            sys.path.append(user_site_packages)
+
+
 def _get_installed_xontribs(pkg_name="xontrib"):
     """List all core packages + newly installed xontribs"""
+    _patch_in_userdir()
     spec = importlib.util.find_spec(pkg_name)
 
     def iter_paths():
@@ -111,6 +126,7 @@ def iter_modules():
 
 def find_xontrib(name, full_module=False):
     """Finds a xontribution from its name."""
+    _patch_in_userdir()
 
     # here the order is important. We try to run the correct cases first and then later trial cases
     # that will likely fail


Test patch: