link cpython
This commit is contained in:
parent
c93c182953
commit
9f2ac70cc8
|
@ -31,6 +31,7 @@ import logging
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import re
|
import re
|
||||||
|
import site
|
||||||
import shlex
|
import shlex
|
||||||
import shutil
|
import shutil
|
||||||
import socket
|
import socket
|
||||||
|
@ -168,6 +169,8 @@ OBJ_GRAPHIC = 'SwXTextGraphicObject'
|
||||||
OBJ_TEXTS = 'SwXTextRanges'
|
OBJ_TEXTS = 'SwXTextRanges'
|
||||||
OBJ_TEXT = 'SwXTextRange'
|
OBJ_TEXT = 'SwXTextRange'
|
||||||
IS_FLATPAK = bool(os.getenv("FLATPAK_ID", ""))
|
IS_FLATPAK = bool(os.getenv("FLATPAK_ID", ""))
|
||||||
|
IS_APP_IMAGE = bool(os.getenv("APPIMAGE", ""))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ~ from com.sun.star.sheet.FilterOperator import EMPTY, NO_EMPTY, EQUAL, NOT_EQUAL
|
# ~ from com.sun.star.sheet.FilterOperator import EMPTY, NO_EMPTY, EQUAL, NOT_EQUAL
|
||||||
|
@ -636,15 +639,60 @@ def sleep(seconds):
|
||||||
time.sleep(seconds)
|
time.sleep(seconds)
|
||||||
return
|
return
|
||||||
|
|
||||||
def get_flatpak_site_packages_dir() -> str:
|
|
||||||
# should never be all users
|
|
||||||
sand_box = os.getenv("FLATPAK_SANDBOX_DIR", "") or str(
|
def get_site_packages_dir() -> str:
|
||||||
Path.home() / ".var/app/org.libreoffice.LibreOffice/sandbox"
|
"""Gets the site-packages folder for the current user."""
|
||||||
)
|
|
||||||
major_minor = f"{sys.version_info.major}.{sys.version_info.minor}"
|
major_minor = f"{sys.version_info.major}.{sys.version_info.minor}"
|
||||||
site_packages = Path(sand_box) / f"lib/python{major_minor}/site-packages"
|
|
||||||
site_packages.mkdir(parents=True, exist_ok=True)
|
def get_windows_site_packages_dir() -> str:
|
||||||
return str(site_packages)
|
nonlocal major_minor
|
||||||
|
if site.USER_SITE:
|
||||||
|
site_packages = Path(site.USER_SITE).resolve()
|
||||||
|
else:
|
||||||
|
site_packages = (
|
||||||
|
Path.home() / f"'/AppData/Roaming/Python/Python{major_minor}/site-packages'"
|
||||||
|
)
|
||||||
|
site_packages.mkdir(parents=True, exist_ok=True)
|
||||||
|
return str(site_packages)
|
||||||
|
|
||||||
|
def get_flatpak_site_packages_dir() -> str:
|
||||||
|
# should never be all users
|
||||||
|
nonlocal major_minor
|
||||||
|
sand_box = os.getenv("FLATPAK_SANDBOX_DIR", "") or str(
|
||||||
|
Path.home() / ".var/app/org.libreoffice.LibreOffice/sandbox"
|
||||||
|
)
|
||||||
|
site_packages = Path(sand_box) / f"lib/python{major_minor}/site-packages"
|
||||||
|
site_packages.mkdir(parents=True, exist_ok=True)
|
||||||
|
return str(site_packages)
|
||||||
|
|
||||||
|
def get_mac_site_packages_dir() -> str:
|
||||||
|
nonlocal major_minor
|
||||||
|
if site.USER_SITE:
|
||||||
|
site_packages = Path(site.USER_SITE).resolve()
|
||||||
|
else:
|
||||||
|
site_packages = (
|
||||||
|
Path.home() / f"Library/LibreOfficePython/{major_minor}/lib/python/site-packages"
|
||||||
|
)
|
||||||
|
site_packages.mkdir(parents=True, exist_ok=True)
|
||||||
|
return str(site_packages)
|
||||||
|
|
||||||
|
def get_default_site_packages_dir() -> str:
|
||||||
|
nonlocal major_minor
|
||||||
|
if site.USER_SITE:
|
||||||
|
site_packages = Path(site.USER_SITE).resolve()
|
||||||
|
else:
|
||||||
|
site_packages = Path.home() / f".local/lib/python{major_minor}/site-packages"
|
||||||
|
site_packages.mkdir(parents=True, exist_ok=True)
|
||||||
|
return str(site_packages)
|
||||||
|
|
||||||
|
if IS_WIN:
|
||||||
|
return get_windows_site_packages_dir()
|
||||||
|
if IS_MAC:
|
||||||
|
return get_mac_site_packages_dir()
|
||||||
|
if IS_FLATPAK:
|
||||||
|
return get_flatpak_site_packages_dir()
|
||||||
|
return get_default_site_packages_dir()
|
||||||
|
|
||||||
class TimerThread(threading.Thread):
|
class TimerThread(threading.Thread):
|
||||||
|
|
||||||
|
@ -6206,8 +6254,7 @@ class Paths(object):
|
||||||
elif IS_MAC:
|
elif IS_MAC:
|
||||||
path = self.join(self.config('Module'), '..', 'Resources', PYTHON)
|
path = self.join(self.config('Module'), '..', 'Resources', PYTHON)
|
||||||
else:
|
else:
|
||||||
is_app_image = bool(os.getenv("APPIMAGE", ""))
|
if IS_APP_IMAGE:
|
||||||
if is_app_image:
|
|
||||||
path = self.join(self.config("Module"), PYTHON)
|
path = self.join(self.config("Module"), PYTHON)
|
||||||
else:
|
else:
|
||||||
path = sys.executable
|
path = sys.executable
|
||||||
|
|
|
@ -0,0 +1,146 @@
|
||||||
|
"""
|
||||||
|
On some systems such as Mac and AppImage (Linux) the python extension suffix does not match the
|
||||||
|
cpython suffix used by the embedded python interpreter.
|
||||||
|
|
||||||
|
This class creates symlinks for all .so files in dest folder that match the current python embedded suffix.
|
||||||
|
|
||||||
|
For example a file named ``indexers.cpython-38-x86_64-linux-gnu.so`` would be symlinked to ``indexers.cpython-3.8.so``.
|
||||||
|
This renaming allows the python interpreter to find the import.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
from typing import List
|
||||||
|
from pathlib import Path
|
||||||
|
from importlib import machinery
|
||||||
|
import easymacro as app
|
||||||
|
|
||||||
|
|
||||||
|
class LinkCPython:
|
||||||
|
def __init__(self, pth: str) -> None:
|
||||||
|
"""
|
||||||
|
Constructor
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pth (str): Path to site-packages folder.
|
||||||
|
"""
|
||||||
|
self._current_suffix = self._get_current_suffix()
|
||||||
|
app.debug("CPythonLink.__init__")
|
||||||
|
self._link_root = Path(pth)
|
||||||
|
if not self._link_root.exists():
|
||||||
|
raise FileNotFoundError(f"Path does not exist {self._link_root}")
|
||||||
|
app.debug("CPythonLink.__init__ done")
|
||||||
|
|
||||||
|
def _get_current_suffix(self) -> str:
|
||||||
|
"""Gets suffix currently used by the embedded python interpreter such as ``cpython-3.8``"""
|
||||||
|
for suffix in machinery.EXTENSION_SUFFIXES:
|
||||||
|
if suffix.startswith(".cpython-") and suffix.endswith(".so"):
|
||||||
|
# remove leading . and trailing .so
|
||||||
|
return suffix[1:][:-3]
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def _get_all_files(self, path: Path) -> List[Path]:
|
||||||
|
return [p for p in path.glob(f"**/*{self.file_suffix}.so") if p.is_file()]
|
||||||
|
|
||||||
|
def _get_all_links(self, path: Path) -> List[Path]:
|
||||||
|
return [p for p in path.glob(f"**/*{self.current_suffix}.so") if p.is_symlink()]
|
||||||
|
|
||||||
|
def _create_symlink(self, src: Path, dst: Path, overwrite: bool) -> None:
|
||||||
|
if dst.is_symlink():
|
||||||
|
if overwrite:
|
||||||
|
app.debug(f"Removing existing symlink {dst}")
|
||||||
|
dst.unlink()
|
||||||
|
else:
|
||||||
|
app.debug(f"Symlink already exists {dst}")
|
||||||
|
return
|
||||||
|
dst.symlink_to(src)
|
||||||
|
app.debug(f"Created symlink {dst} -> {src}")
|
||||||
|
|
||||||
|
def _find_current_installed_suffix(self, path: Path) -> str:
|
||||||
|
"""
|
||||||
|
Finds the current suffix from the current installed python so files such as ``cpython-38-x86_64-linux-gnu``.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
path (Path): Path to search in. Usually site-packages.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: suffix if found, otherwise empty string.
|
||||||
|
"""
|
||||||
|
return next(
|
||||||
|
(str(p).rsplit(".", 2)[1] for p in path.glob("**/*.cpython-*.so") if not p.is_symlink()),
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
|
||||||
|
def link(self, overwrite:bool = False) -> None:
|
||||||
|
"""
|
||||||
|
Creates symlinks for all .so files in site-packages that match the current suffix.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
overwrite (bool, optional): Override any existing sys links. Defaults to False.
|
||||||
|
"""
|
||||||
|
app.debug("CPythonLink.link starting")
|
||||||
|
if not self._link_root:
|
||||||
|
app.debug("No site-packages found")
|
||||||
|
return
|
||||||
|
if not self.file_suffix:
|
||||||
|
app.debug("No current file suffix found")
|
||||||
|
return
|
||||||
|
if not self._link_root.exists():
|
||||||
|
app.debug(f"Site-packages does not exist {self._link_root}")
|
||||||
|
return
|
||||||
|
app.debug(f"Python current suffix: {self._current_suffix}")
|
||||||
|
app.debug(f"Found file suffix: {self.file_suffix}")
|
||||||
|
files = self._get_all_files(self._link_root)
|
||||||
|
if not files:
|
||||||
|
app.debug(f"No files found in {self._link_root}")
|
||||||
|
return
|
||||||
|
cp_old = self.file_suffix
|
||||||
|
cp_new = self._current_suffix
|
||||||
|
if cp_old == cp_new:
|
||||||
|
app.debug(f"Suffixes match, no need to link: {cp_old} == {cp_new}")
|
||||||
|
return
|
||||||
|
|
||||||
|
for file in files:
|
||||||
|
ln_name = file.name.replace(cp_old, cp_new)
|
||||||
|
src = file
|
||||||
|
if not src.is_absolute():
|
||||||
|
src = file.resolve()
|
||||||
|
dst = src.parent / ln_name
|
||||||
|
self._create_symlink(src, dst, overwrite)
|
||||||
|
app.debug("CPythonLink.link done")
|
||||||
|
|
||||||
|
def unlink(self) -> None:
|
||||||
|
"""Unlinks all broken sys links"""
|
||||||
|
links = self._get_all_links(self._link_root)
|
||||||
|
if not links:
|
||||||
|
app.debug(f"No links found in {self._link_root}")
|
||||||
|
return
|
||||||
|
for link in links:
|
||||||
|
if not link.exists():
|
||||||
|
app.debug(f"Removing broken symlink {link}")
|
||||||
|
link.unlink()
|
||||||
|
|
||||||
|
|
||||||
|
# region Properties
|
||||||
|
@property
|
||||||
|
def cpy_name(self) -> str:
|
||||||
|
"""Gets/Sets CPython name, e.g. cpython-3.8"""
|
||||||
|
return self._current_suffix
|
||||||
|
|
||||||
|
@cpy_name.setter
|
||||||
|
def cpy_name(self, value: str) -> None:
|
||||||
|
self._current_suffix = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def current_suffix(self) -> str:
|
||||||
|
"""Current Suffix such as ``cpython-3.8``"""
|
||||||
|
return self._current_suffix
|
||||||
|
|
||||||
|
@property
|
||||||
|
def file_suffix(self) -> str:
|
||||||
|
"""Current Suffix such as ``cpython-38-x86_64-linux-gnu``"""
|
||||||
|
try:
|
||||||
|
return self._file_suffix
|
||||||
|
except AttributeError:
|
||||||
|
self._file_suffix = self._find_current_installed_suffix(self._link_root)
|
||||||
|
return self._file_suffix
|
||||||
|
|
||||||
|
# endregion Properties
|
|
@ -67,7 +67,6 @@ class Controllers(object):
|
||||||
def __init__(self, dialog):
|
def __init__(self, dialog):
|
||||||
self.d = dialog
|
self.d = dialog
|
||||||
self.path_python = app.paths.python
|
self.path_python = app.paths.python
|
||||||
self.is_flatpak = app.IS_FLATPAK
|
|
||||||
|
|
||||||
def _set_state(self, state):
|
def _set_state(self, state):
|
||||||
self._states = {
|
self._states = {
|
||||||
|
@ -260,8 +259,8 @@ class Controllers(object):
|
||||||
self.d.lst_log.visible = True
|
self.d.lst_log.visible = True
|
||||||
|
|
||||||
line = ''
|
line = ''
|
||||||
if self.is_flatpak:
|
if app.IS_FLATPAK:
|
||||||
cmd = f' install --upgrade --target={app.get_flatpak_site_packages_dir()}'
|
cmd = f' install --upgrade --target={app.get_site_packages_dir()}'
|
||||||
else:
|
else:
|
||||||
cmd = ' install --upgrade --user'
|
cmd = ' install --upgrade --user'
|
||||||
if value:
|
if value:
|
||||||
|
@ -275,6 +274,33 @@ class Controllers(object):
|
||||||
self.d.lst_log.insert(line, 'ok.png')
|
self.d.lst_log.insert(line, 'ok.png')
|
||||||
else:
|
else:
|
||||||
self.d.lst_log.insert(line)
|
self.d.lst_log.insert(line)
|
||||||
|
self._link_cpython()
|
||||||
|
return
|
||||||
|
|
||||||
|
def _link_cpython(self):
|
||||||
|
if not app.IS_MAC and not app.IS_APP_IMAGE:
|
||||||
|
app.debug('Not Mac or not AppImage')
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
app.debug("Linking CPython")
|
||||||
|
from link_cpython import LinkCPython
|
||||||
|
cpy_link = LinkCPython(pth=app.get_site_packages_dir())
|
||||||
|
cpy_link.link()
|
||||||
|
except Exception as err:
|
||||||
|
app.error(err)
|
||||||
|
return
|
||||||
|
|
||||||
|
def _unlink_cpython(self):
|
||||||
|
if not app.IS_MAC and not app.IS_APP_IMAGE:
|
||||||
|
app.debug('Not Mac or not AppImage')
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
app.debug("Unlinking CPython")
|
||||||
|
from link_cpython import LinkCPython
|
||||||
|
cpy_link = LinkCPython(pth=app.get_site_packages_dir())
|
||||||
|
cpy_link.unlink()
|
||||||
|
except Exception as err:
|
||||||
|
app.error(err)
|
||||||
return
|
return
|
||||||
|
|
||||||
def lst_package_double_click(self, event):
|
def lst_package_double_click(self, event):
|
||||||
|
@ -305,6 +331,7 @@ class Controllers(object):
|
||||||
self.d.lst_log.insert(line, 'ok.png')
|
self.d.lst_log.insert(line, 'ok.png')
|
||||||
else:
|
else:
|
||||||
self.d.lst_log.insert(line)
|
self.d.lst_log.insert(line)
|
||||||
|
self._unlink_cpython()
|
||||||
return
|
return
|
||||||
|
|
||||||
def cmd_uninstall_action(self, event):
|
def cmd_uninstall_action(self, event):
|
||||||
|
|
Loading…
Reference in New Issue