diff --git a/CHANGELOG b/CHANGELOG
index 0fe38fe..0fcf0a7 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,12 @@
+v 1.0.0 [05-nov-2023]
+ - Thanks to https://git.cuates.net/AmourSpirit zaz-pip now work in:
+ * Mac Os
+ * Windows
+ * Linux, sudo installed LibreOffice
+ * Linux, Snap installed LibreOffice
+ * Linux, Flatpak installed LibreOffice
+ * Linux, AppImage LibreOffice
+
v 0.10.2 [22-apr-2022]
- Fix - issue #10
diff --git a/README.md b/README.md
index 7bc6a24..65250f3 100644
--- a/README.md
+++ b/README.md
@@ -25,6 +25,11 @@ SWIFT / BIC: TRWIBEB1XXX
* [Mira la wiki](https://git.cuates.net/elmau/zaz-pip/wiki/home_es)
+Thanks for code:
+
+* @AmourSpirit
+
+
Thanks for translations:
* English - @LibreOfficiant
diff --git a/VERSION b/VERSION
index 42624f3..afaf360 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.10.2
\ No newline at end of file
+1.0.0
\ No newline at end of file
diff --git a/conf.py b/conf.py
index 78f0c2b..baef879 100644
--- a/conf.py
+++ b/conf.py
@@ -26,7 +26,7 @@ import logging
TYPE_EXTENSION = 1
# ~ https://semver.org/
-VERSION = '0.10.2'
+VERSION = '1.0.0'
# ~ Your great extension name, not used spaces
NAME = 'ZAZPip'
diff --git a/extension/ZAZPip_v0.10.2.oxt b/extension/ZAZPip_v0.10.2.oxt
deleted file mode 100644
index 897da46..0000000
Binary files a/extension/ZAZPip_v0.10.2.oxt and /dev/null differ
diff --git a/extension/ZAZPip_v1.0.0.oxt b/extension/ZAZPip_v1.0.0.oxt
new file mode 100644
index 0000000..560102c
Binary files /dev/null and b/extension/ZAZPip_v1.0.0.oxt differ
diff --git a/source/META-INF/manifest.xml b/source/META-INF/manifest.xml
index 29ceecf..3fcb2d4 100644
--- a/source/META-INF/manifest.xml
+++ b/source/META-INF/manifest.xml
@@ -1,6 +1,8 @@
+
+
diff --git a/source/description.xml b/source/description.xml
index c87fc3a..87c4097 100644
--- a/source/description.xml
+++ b/source/description.xml
@@ -1,7 +1,7 @@
-
+
diff --git a/source/job.xcu b/source/job.xcu
new file mode 100644
index 0000000..7228846
--- /dev/null
+++ b/source/job.xcu
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+ net.elmau.zaz.pip.PipRunner
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/pip_runner.py b/source/pip_runner.py
new file mode 100644
index 0000000..c7c253e
--- /dev/null
+++ b/source/pip_runner.py
@@ -0,0 +1,57 @@
+# region Imports
+from __future__ import unicode_literals, annotations
+import os
+import sys
+from typing import TYPE_CHECKING, Tuple
+from pathlib import Path
+import uno
+import unohelper
+from com.sun.star.task import XJob
+
+if TYPE_CHECKING:
+ # just for design time
+ from com.sun.star.beans import NamedValue
+
+# endregion Imports
+
+# region Constants
+
+implementation_name = "net.elmau.zaz.pip.PipRunner"
+implementation_services = ("com.sun.star.task.Job",)
+# endregion Constants
+
+
+
+# region XJob
+class PipRunner(unohelper.Base, XJob):
+
+ def __init__(self, ctx):
+ self._is_flatpak = bool(os.getenv("FLATPAK_ID", ""))
+ if self._is_flatpak:
+ site_packages = self.get_flatpak_site_packages_dir()
+ ss = str(site_packages)
+ if site_packages.exists() and ss not in sys.path:
+ sys.path.append(ss)
+
+ def execute(self, *args: Tuple[NamedValue, ...]) -> None:
+ pass
+
+ def get_flatpak_site_packages_dir(self) -> Path:
+ # should never be all users
+ sand_box = os.getenv("FLATPAK_SANDBOX_DIR", "") or str(
+ Path.home() / ".var/app/org.libreoffice.LibreOffice/sandbox"
+ )
+ 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)
+ return site_packages
+
+# endregion XJob
+
+# region Implementation
+
+g_TypeTable = {}
+g_ImplementationHelper = unohelper.ImplementationHelper()
+
+g_ImplementationHelper.addImplementation(PipRunner, implementation_name, implementation_services)
+# endregion Implementation
\ No newline at end of file
diff --git a/source/pythonpath/easymacro.py b/source/pythonpath/easymacro.py
index cbb26a0..14fadd8 100644
--- a/source/pythonpath/easymacro.py
+++ b/source/pythonpath/easymacro.py
@@ -31,6 +31,7 @@ import logging
import os
import platform
import re
+import site
import shlex
import shutil
import socket
@@ -167,6 +168,9 @@ OBJ_GRAPHIC = 'SwXTextGraphicObject'
OBJ_TEXTS = 'SwXTextRanges'
OBJ_TEXT = 'SwXTextRange'
+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
@@ -595,13 +599,24 @@ def call_macro(args, in_thread=False):
result = _call_macro(args)
return result
-
+def get_env():
+ """
+ Gets Environment used for subprocess.
+ """
+ my_env = os.environ.copy()
+ py_path = ""
+ p_sep = ";" if IS_WIN else ":"
+ for d in sys.path:
+ py_path = py_path + d + p_sep
+ my_env["PYTHONPATH"] = py_path
+ return my_env
+
def run(command, capture=False, split=True):
if not split:
- return subprocess.check_output(command, shell=True).decode()
+ return subprocess.check_output(command, shell=True, env=get_env()).decode()
cmd = shlex.split(command)
- result = subprocess.run(cmd, capture_output=capture, text=True, shell=IS_WIN)
+ result = subprocess.run(cmd, capture_output=capture, text=True, shell=IS_WIN, env=get_env())
if capture:
result = result.stdout
else:
@@ -612,7 +627,7 @@ def run(command, capture=False, split=True):
def popen(command):
try:
proc = subprocess.Popen(shlex.split(command), shell=IS_WIN,
- stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=get_env())
for line in proc.stdout:
yield line.decode().rstrip()
except Exception as e:
@@ -625,6 +640,60 @@ def sleep(seconds):
return
+
+def get_site_packages_dir() -> str:
+ """Gets the site-packages folder for the current user."""
+ major_minor = f"{sys.version_info.major}.{sys.version_info.minor}"
+
+ def get_windows_site_packages_dir() -> str:
+ 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):
def __init__(self, event, seconds, macro):
@@ -4146,6 +4215,7 @@ class EventsFocus(EventsListenerBase, XFocusListener):
if service in self.CONTROLS:
obj = event.Source.Model
obj.BackgroundColor = COLOR_ON_FOCUS
+ obj.TextColor = TEXT_COLOR_ON_FOCUS
return
def focusLost(self, event):
@@ -6184,7 +6254,10 @@ class Paths(object):
elif IS_MAC:
path = self.join(self.config('Module'), '..', 'Resources', PYTHON)
else:
- path = sys.executable
+ if IS_APP_IMAGE:
+ path = self.join(self.config("Module"), PYTHON)
+ else:
+ path = sys.executable
return path
@classmethod
@@ -6306,7 +6379,7 @@ class Paths(object):
if IS_WIN:
os.startfile(path)
else:
- pid = subprocess.Popen(['xdg-open', path]).pid
+ pid = subprocess.Popen(['xdg-open', path, ], env=get_env()).pid
return
@classmethod
@@ -6839,6 +6912,7 @@ def get_color(value):
COLOR_ON_FOCUS = get_color('LightYellow')
+TEXT_COLOR_ON_FOCUS = get_color('black')
class LOServer(object):
@@ -6890,7 +6964,7 @@ class LOServer(object):
for i in range(3):
self._server = subprocess.Popen(self.CMD,
- stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=get_env())
time.sleep(3)
if self.is_running:
break
diff --git a/source/pythonpath/install_flatpak.py b/source/pythonpath/install_flatpak.py
new file mode 100644
index 0000000..5f98199
--- /dev/null
+++ b/source/pythonpath/install_flatpak.py
@@ -0,0 +1,214 @@
+from __future__ import annotations
+from typing import cast
+import os
+import sys
+import tempfile
+import subprocess
+from pathlib import Path
+from typing import Any, List, Dict
+import easymacro as app
+from contextlib import contextmanager
+import uno
+
+
+@contextmanager
+def change_dir(directory):
+ """
+ A context manager that changes the current working directory to the specified directory
+ temporarily and then changes it back when the context is exited.
+ """
+ current_dir = os.getcwd()
+ os.chdir(directory)
+ try:
+ yield
+ finally:
+ os.chdir(current_dir)
+
+
+class InstallPipFromWheel:
+ """Download and install PIP from wheel url"""
+
+ def __init__(self, pip_wheel_url: str, lo_identifier: str) -> None:
+ self._pip_url = pip_wheel_url
+ self._lo_identifier = lo_identifier
+
+ def install(self, dst: str | Path = "") -> None:
+ """
+ Install pip from wheel file.
+
+ Downloads the pip wheel file from the url provided in the config file and unzips it to the destination directory.
+
+ Args:
+ dst (str | Path, Optional): The destination directory where the pip wheel file will be installed. If not provided, the ``pythonpath`` location will be used.
+
+ Returns:
+ None:
+
+ Raises:
+ None:
+ """
+ if not self._pip_url:
+ app.error("PIP installation has failed - No wheel url")
+ return
+
+ if not dst:
+ root_pth = Path(app.Paths.from_id(self._lo_identifier))
+ dst = root_pth / "pythonpath"
+
+ with tempfile.TemporaryDirectory() as temp_dir:
+ # temp_dir = tempfile.gettempdir()
+ path_pip = Path(temp_dir)
+
+ filename = path_pip / "pip-wheel.whl"
+
+ data, _, err = app.url_open(self._pip_url, verify=False)
+ if err:
+ app.error("Unable to download PIP installation wheel file")
+ return
+ filename.write_bytes(data)
+
+ if filename.exists():
+ app.info("PIP wheel file has been saved")
+ else:
+ app.error("PIP wheel file has not been saved")
+ return
+
+ try:
+ self._unzip_wheel(filename=filename, dst=dst)
+ except Exception:
+ return
+ # now that pip has been installed from wheel force a reinstall to ensure it is the latest version
+ self._force_install_pip()
+
+ def _unzip_wheel(self, filename: Path, dst: str | Path) -> None:
+ """Unzip the downloaded wheel file"""
+ if isinstance(dst, str):
+ dst = Path(dst)
+ try:
+ # app.zip.unzip(source=str(filename), target=str(dst))
+ self.unzip_file(zip_file=filename, dest_dir=dst)
+ if dst.exists():
+ app.debug(f"PIP wheel file has been unzipped to {dst}")
+ else:
+ app.error("PIP wheel file has not been unzipped")
+ raise Exception("PIP wheel file has not been unzipped")
+ except Exception as err:
+ app.error(f"Unable to unzip wheel file: {err}", exc_info=True)
+ raise
+
+ def _force_install_pip(self) -> None:
+ """Now that pip has been installed, force reinstall it to ensure it is the latest version"""
+ cmd = [app.paths.python, "-m", "pip", "install", "--upgrade", "pip"]
+ app.popen(command=" ".join(cmd))
+
+ def unzip_file(self, zip_file: str | Path, dest_dir: str | Path = "") -> None:
+ """
+ Unzip the given zip file to the specified destination directory.
+
+ Args:
+ zip_file (str | Path): The zip file to unzip.
+ dest_dir (str | Path, optional): The destination directory to unzip to.
+
+ Returns:
+ None:
+ """
+ from zipfile import ZipFile
+
+ zip_file_path = Path(zip_file) if isinstance(zip_file, str) else zip_file
+ if not zip_file_path.is_file():
+ raise ValueError(f"Expected file, got '{zip_file_path}'")
+ if not zip_file_path.is_absolute():
+ zip_file_path = zip_file_path.absolute()
+ if not zip_file_path.exists():
+ raise FileNotFoundError(f"File '{zip_file_path}' not found")
+
+ if isinstance(dest_dir, str):
+ dest_dir = zip_file_path.parent if dest_dir == "" else Path(dest_dir)
+ else:
+ dest_dir = dest_dir.absolute()
+
+ if not dest_dir.is_dir():
+ raise ValueError(f"Expected folder, got '{dest_dir}'")
+ if not dest_dir.exists():
+ try:
+ dest_dir.mkdir(parents=True)
+ except Exception as e:
+ raise FileNotFoundError(
+ f"Folder '{dest_dir}' not found, unable to create folder."
+ ) from e
+ if not dest_dir.exists():
+ raise FileNotFoundError(f"Folder '{dest_dir}' not found")
+
+ with change_dir(dest_dir):
+ with ZipFile(zip_file_path) as f:
+ f.extractall(dest_dir)
+ # with change_dir(dest_dir):
+ # shutil.unpack_archive(zip_file_path, dest_dir)
+
+
+class FlatpakInstaller:
+ """class for the PIP install."""
+
+ def __init__(self, pip_wheel_url: str, lo_identifier: str) -> None:
+ self.path_python = app.Paths.python
+ app.debug(f"Python path: {self.path_python}")
+ self._pip_url = pip_wheel_url
+ self._lo_identifier = lo_identifier
+ self._site_packages = cast(str, app.get_flatpak_site_packages_dir())
+
+ def install_pip(self) -> None:
+ if self.is_pip_installed():
+ app.info("PIP is already installed")
+ return
+ if self._install_wheel():
+ if self.is_pip_installed():
+ app.info("PIP was installed successfully")
+ else:
+ app.error("PIP installation has failed")
+
+ return
+
+ def _get_pip_cmd(self, filename: Path) -> List[str]:
+ return [str(self.path_python), f"{filename}", "--user"]
+
+ def _get_env(self) -> Dict[str, str]:
+ """
+ Gets Environment used for subprocess.
+ """
+ my_env = os.environ.copy()
+ py_path = ""
+ p_sep = ";" if app.IS_WIN else ":"
+ for d in sys.path:
+ py_path = py_path + d + p_sep
+ my_env["PYTHONPATH"] = py_path
+ return my_env
+
+ def _cmd_pip(self, *args: str) -> List[str]:
+ cmd: List[str] = [str(self.path_python), "-m", "pip", *args]
+ return cmd
+
+ def _install_wheel(self) -> bool:
+ result = False
+
+ installer = InstallPipFromWheel(
+ pip_wheel_url=self._pip_url, lo_identifier=self._lo_identifier
+ )
+ try:
+ installer.install(self._site_packages)
+ if self._site_packages not in sys.path:
+ sys.path.append(self._site_packages)
+ result = True
+ except Exception as err:
+ app.error(err)
+ return result
+ return result
+
+ def is_pip_installed(self) -> bool:
+ """Check if PIP is installed."""
+ # cmd = self._cmd_pip("--version")
+ # cmd = '"{}" -m pip -V'.format(self.path_python)
+ cmd = [str(self.path_python), "-m", "pip", "-V"]
+ result = subprocess.run(
+ cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=self._get_env()
+ )
+ return result.returncode == 0
diff --git a/source/pythonpath/link_cpython.py b/source/pythonpath/link_cpython.py
new file mode 100644
index 0000000..e9d2237
--- /dev/null
+++ b/source/pythonpath/link_cpython.py
@@ -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
diff --git a/source/pythonpath/main.py b/source/pythonpath/main.py
index fc8a41a..46fa86a 100644
--- a/source/pythonpath/main.py
+++ b/source/pythonpath/main.py
@@ -10,6 +10,7 @@ _ = None
TITLE = 'ZAZ-PIP'
URL_PIP = 'https://bootstrap.pypa.io/get-pip.py'
+URL_PIP_WHEEL = 'https://files.pythonhosted.org/packages/47/6a/453160888fab7c6a432a6e25f8afe6256d0d9f2cbd25971021da6491d899/pip-23.3.1-py3-none-any.whl'
URL_TEST = 'http://duckduckgo.com'
PIP = 'pip'
URL_GIT = 'https://git.cuates.net/elmau/zaz-pip'
@@ -85,8 +86,7 @@ class Controllers(object):
self._install_pip()
return
- @app.run_in_thread
- def _install_pip(self):
+ def _install_pip_normal(self) -> None:
self.d.link_proyect.visible = False
self.d.lst_log.visible = True
path_pip = app.paths.tmp()
@@ -119,7 +119,7 @@ class Controllers(object):
self.d.lbl_pip.value = label
self.d.cmd_install_pip.visible = False
self.d.cmd_admin_pip.visible = True
- msg = _('PIP was installed sucessfully')
+ msg = _('PIP was installed successfully')
app.msgbox(msg)
else:
msg = _('PIP installation has failed, see log')
@@ -129,6 +129,35 @@ class Controllers(object):
return
+ def _install_pip_flatpak(self) -> None:
+ try:
+ from install_flatpak import FlatpakInstaller
+ installer = FlatpakInstaller(pip_wheel_url=URL_PIP_WHEEL, lo_identifier=ID_EXTENSION)
+ installer.install_pip()
+ msg = _('PIP was installed successfully')
+ # app.msgbox(msg)
+ cmd = self._cmd_pip('-V')
+ label = app.run(cmd, True)
+ if label:
+ self.d.lbl_pip.value = label
+ self.d.cmd_install_pip.visible = False
+ self.d.cmd_admin_pip.visible = True
+ msg = _('PIP was installed successfully')
+ app.msgbox(msg)
+ else:
+ msg = _('PIP installation has failed, see log')
+ app.warning(msg)
+ except Exception as e:
+ app.errorbox(e)
+
+ @app.run_in_thread
+ def _install_pip(self):
+ if app.IS_FLATPAK:
+ self._install_pip_flatpak()
+ else:
+ self._install_pip_normal()
+ return
+
def _cmd_pip(self, args):
cmd = '"{}" -m pip {}'.format(self.path_python, args)
return cmd
@@ -230,7 +259,10 @@ class Controllers(object):
self.d.lst_log.visible = True
line = ''
- cmd = ' install --upgrade --user'
+ if app.IS_FLATPAK:
+ cmd = f' install --upgrade --target={app.get_site_packages_dir()}'
+ else:
+ cmd = ' install --upgrade --user'
if value:
name = value.split(' ')[0].strip()
cmd = self._cmd_pip(f'{cmd} {name}')
@@ -242,6 +274,31 @@ class Controllers(object):
self.d.lst_log.insert(line, 'ok.png')
else:
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:
+ 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:
+ 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
def lst_package_double_click(self, event):
@@ -272,6 +329,7 @@ class Controllers(object):
self.d.lst_log.insert(line, 'ok.png')
else:
self.d.lst_log.insert(line)
+ self._unlink_cpython()
return
def cmd_uninstall_action(self, event):