Compare commits

...

25 Commits

Author SHA1 Message Date
El Mau c2470ddd9d Add support for Flatplak and AppImage versions 2023-11-05 21:35:58 -06:00
El Mau 4b66d38e32 Make new OXT 2023-11-05 21:32:36 -06:00
El Mau d2d60e7281 Update version 2023-11-05 21:30:39 -06:00
Mauricio 0e750c0af0 Merge pull request 'cpython' (#14) from AmourSpirit/zaz-pip:cpython into develop
Reviewed-on: #14
2023-11-03 11:23:41 -06:00
Barry-Thomas-Paul: Moss 8dadb7e62e fix for debug error on Windows 2023-11-02 22:45:55 -04:00
Barry-Thomas-Paul: Moss 9f2ac70cc8 link cpython 2023-11-02 22:05:56 -04:00
Mauricio c93c182953 Merge pull request 'flatpak' (#13) from AmourSpirit/zaz-pip:flatpak into develop
Reviewed-on: #13
2023-11-01 19:18:49 -06:00
Barry-Thomas-Paul: Moss db3466b716 minor correcton for site_packages.exists() 2023-10-31 20:13:43 -04:00
Barry-Thomas-Paul: Moss 0453092831 fix for white text on bright yellow edit box in dark theme 2023-10-31 19:39:45 -04:00
Barry-Thomas-Paul: Moss 7b9ac8eee0 working with flatpak 2023-10-31 19:28:22 -04:00
Barry-Thomas-Paul: Moss 4aa0ca68c8 added pip_runner that add site_packages to sys.path 2023-10-31 16:19:18 -04:00
Barry-Thomas-Paul: Moss 923b39c7f2 pip installing in flatpak 2023-10-31 15:57:41 -04:00
Mauricio 90aa12adf2 Merge pull request 'develop' (#11) from AmourSpirit/zaz-pip:develop into develop
Reviewed-on: #11
2023-10-29 16:51:44 -06:00
AmourSpirit d52472dad7 Merge pull request 'app_image' (#1) from app_image into develop
Reviewed-on: AmourSpirit/zaz-pip#1
2023-10-19 17:09:26 -06:00
Barry-Thomas-Paul: Moss 6ec45c1bb4 Update to work with AppImage
Linxu ppImage Works with this minor fix.
Linux FlatPak still not working yet.
2023-10-19 19:06:50 -04:00
Barry-Thomas-Paul: Moss 910e5f54b1 update for Linxu AppImage 2023-10-19 15:29:02 -04:00
El Mau c9b9bfdf0a Fix issue #10 2022-04-23 21:49:30 -05:00
El Mau 587148553e Fix - Issue #10 2022-04-23 21:48:50 -05:00
El Mau 4b7b0c359f Fix issue #9 2022-03-26 23:03:54 -06:00
El Mau 269f9adc75 Fix - Issue #9 2022-03-26 16:07:22 -06:00
El Mau 668e23353b Update README 2022-03-26 11:46:41 -06:00
El Mau d7d65bb8ba Fix - Issue #9 2022-03-26 11:45:03 -06:00
El Mau b3c783c1fe Add Proxy dialog title to translate 2022-03-25 09:29:59 -06:00
El Mau 2452480457 Add french translation. Issue #8 2022-03-17 12:39:49 -06:00
El Mau afd94fa5fe Add french translation 2022-03-17 12:38:07 -06:00
25 changed files with 901 additions and 41 deletions

View File

@ -1,7 +1,25 @@
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
v 0.10.1 [26-mar-2022]
- Fix - issue #9
v 0.10.0 [19-mar-2022]
- Add french translation
v 0.9.0 [04-feb-2022]
- Fix - Add minimal version required, issue #5
- Fix - Corrections to english messages, issue #6
- Fix - Update and complete spanish translation, thanks to me, :)
- Fix - Update and complete Spanish translation, thanks to me, :)
v 0.8.0 [13-jun-2021]
- Update easymacro.py
@ -25,7 +43,7 @@ v 0.3.0 [12-nov-2019]
- Test install numpy
v 0.2.0 [18-oct-2019]
- Add spanish
- Add Spanish translation
v 0.1.0 [18-oct-2019]
- Initial version

View File

@ -25,8 +25,12 @@ 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
* Help Us
* English - @LibreOfficiant
* French - @LibreOfficiant

View File

@ -1 +1 @@
0.9.0
1.0.0

14
conf.py
View File

@ -26,7 +26,7 @@ import logging
TYPE_EXTENSION = 1
# ~ https://semver.org/
VERSION = '0.9.0'
VERSION = '1.0.0'
# ~ Your great extension name, not used spaces
NAME = 'ZAZPip'
@ -50,6 +50,7 @@ URL_GIT = 'https://git.cuates.net/elmau/zaz-pip'
PUBLISHER = {
'en': {'text': 'El Mau', 'link': URL_GIT},
'es': {'text': 'El Mau', 'link': URL_GIT},
'fr': {'text': 'El Mau', 'link': URL_GIT},
}
# ~ Name in this folder for copy
@ -75,7 +76,7 @@ LICENSE_EN = f"""This file is part of {NAME}.
You should have received a copy of the GNU General Public License
along with {NAME}. If not, see <https://www.gnu.org/licenses/>.
"""
LICENSE_ES = LICENSE_EN
LICENSE_FR = LICENSE_ES = LICENSE_EN
INFO = {
'en': {
@ -88,6 +89,11 @@ INFO = {
'description': 'Pip para LibreOffice',
'license': LICENSE_ES,
},
'fr': {
'display_name': 'ZAZ Pip',
'description': 'Pip pour LibreOffice',
'license': LICENSE_FR,
},
}
@ -110,7 +116,7 @@ PARENT = 'AddonMenu'
MENU_MAIN = {}
MENUS = (
{
'title': {'en': 'Open Pip', 'es': 'Abrir Pip'},
'title': {'en': 'Open Zaz-Pip', 'es': 'Abrir Zaz-Pip', 'fr': 'Ouvert Zaz-Pip'},
'argument': 'open_dialog_pip',
'context': '',
'icon': 'icon',
@ -152,7 +158,7 @@ DIRS = {
'description': 'description',
'images': 'images',
'registration': 'registration',
'files': 'files',
'files': 'extension',
'office': 'Office',
'locales': PATH_LOCALES,
'pythonpath': True,

BIN
extension/ZAZPip_v1.0.0.oxt Normal file

Binary file not shown.

Binary file not shown.

View File

@ -4,8 +4,9 @@
<node oor:name="AddonMenu">
<node oor:name="net.elmau.zaz.pip" oor:op="fuse">
<prop oor:name="Title" oor:type="xs:string">
<value xml:lang="en">Open Pip</value>
<value xml:lang="es">Abrir Pip</value>
<value xml:lang="en">Open Zaz-Pip</value>
<value xml:lang="es">Abrir Zaz-Pip</value>
<value xml:lang="fr">Ouvert Zaz-Pip</value>
</prop>
<prop oor:name="Context" oor:type="xs:string">
<value/>

View File

@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" manifest:version="1.2">
<manifest:file-entry manifest:full-path="job.xcu" manifest:media-type="application/vnd.sun.star.configuration-data" />
<manifest:file-entry manifest:full-path="ZAZPip.py" manifest:media-type="application/vnd.sun.star.uno-component;type=Python"/>
<manifest:file-entry manifest:full-path="pip_runner.py" manifest:media-type="application/vnd.sun.star.uno-component;type=Python" />
<manifest:file-entry manifest:full-path="Office/Accelerators.xcu" manifest:media-type="application/vnd.sun.star.configuration-data"/>
<manifest:file-entry manifest:full-path="Addons.xcu" manifest:media-type="application/vnd.sun.star.configuration-data"/>
</manifest:manifest>

View File

@ -1,17 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<description xmlns="http://openoffice.org/extensions/description/2006" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:d="http://openoffice.org/extensions/description/2006" xmlns:l="http://libreoffice.org/extensions/description/2011">
<identifier value="net.elmau.zaz.pip"/>
<version value="0.9.0"/>
<version value="1.0.0"/>
<dependencies>
<l:LibreOffice-minimal-version value="7.0"/>
</dependencies>
<display-name>
<name lang="en">ZAZ Pip</name>
<name lang="es">ZAZ Pip</name>
<name lang="fr">ZAZ Pip</name>
</display-name>
<extension-description>
<src lang="en" xlink:href="description/desc_en.txt"/>
<src lang="es" xlink:href="description/desc_es.txt"/>
<src lang="fr" xlink:href="description/desc_fr.txt"/>
</extension-description>
<icon>
<default xlink:href="images/zazpip.png"/>
@ -19,11 +21,13 @@
<publisher>
<name xlink:href="https://git.cuates.net/elmau/zaz-pip" lang="en">El Mau</name>
<name xlink:href="https://git.cuates.net/elmau/zaz-pip" lang="es">El Mau</name>
<name xlink:href="https://git.cuates.net/elmau/zaz-pip" lang="fr">El Mau</name>
</publisher>
<registration>
<simple-license accept-by="user" suppress-on-update="true">
<license-text xlink:href="registration/license_en.txt" lang="en"/>
<license-text xlink:href="registration/license_es.txt" lang="es"/>
<license-text xlink:href="registration/license_fr.txt" lang="fr"/>
</simple-license>
</registration>
</description>

View File

@ -0,0 +1 @@
Pip pour LibreOffice

20
source/job.xcu Normal file
View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE oor:component-data SYSTEM "../../../../component-update.dtd">
<oor:component-data oor:name="Jobs" oor:package="org.openoffice.Office"
xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<node oor:name="Jobs">
<node oor:name="PipRunnerJob" oor:op="fuse">
<prop oor:name="Service">
<value>net.elmau.zaz.pip.PipRunner</value>
</prop>
</node>
</node>
<node oor:name="Events">
<node oor:name="OnStartApp" oor:op="fuse">
<node oor:name="JobList">
<node oor:name="PipRunnerJob" oor:op="fuse" />
</node>
</node>
</node>
</oor:component-data>

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2022-01-28 13:40-0600\n"
"POT-Creation-Date: 2022-03-26 11:23-0600\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -55,6 +55,18 @@ msgstr ""
msgid "Enter the package name to install"
msgstr ""
#: source/pythonpath/main.py:218
msgid "Do you really want to install package: {}?"
msgstr ""
#: source/pythonpath/main.py:247
msgid "install"
msgstr ""
#: source/pythonpath/main.py:249
msgid "upgrade"
msgstr ""
#: source/pythonpath/main.py:252
msgid ""
"Do you want to {}:\n"
@ -73,6 +85,13 @@ msgid ""
"{}?"
msgstr ""
#: source/pythonpath/main.py:310
msgid ""
"Do you want to install from:\n"
"\n"
"{}"
msgstr ""
#: source/pythonpath/main.py:337
msgid "Do you want to save this Proxy settings?"
msgstr ""
@ -109,6 +128,10 @@ msgstr ""
msgid "~Close"
msgstr ""
#: source/pythonpath/main.py:635
msgid "Zaz-Pip - Proxy settings"
msgstr ""
#: source/pythonpath/main.py:646
msgid "Host: "
msgstr ""

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: 2022-01-28 13:40-0600\n"
"POT-Creation-Date: 2022-03-26 11:23-0600\n"
"PO-Revision-Date: 2021-01-21 16:32-0500\n"
"Last-Translator: \n"
"Language-Team: \n"
@ -57,6 +57,18 @@ msgstr ""
msgid "Enter the package name to install"
msgstr ""
#: source/pythonpath/main.py:218
msgid "Do you really want to install package: {}?"
msgstr ""
#: source/pythonpath/main.py:247
msgid "install"
msgstr ""
#: source/pythonpath/main.py:249
msgid "upgrade"
msgstr ""
#: source/pythonpath/main.py:252
msgid ""
"Do you want to {}:\n"
@ -75,6 +87,13 @@ msgid ""
"{}?"
msgstr ""
#: source/pythonpath/main.py:310
msgid ""
"Do you want to install from:\n"
"\n"
"{}"
msgstr ""
#: source/pythonpath/main.py:337
msgid "Do you want to save this Proxy settings?"
msgstr ""
@ -111,6 +130,10 @@ msgstr ""
msgid "~Close"
msgstr ""
#: source/pythonpath/main.py:635
msgid "Zaz-Pip - Proxy settings"
msgstr ""
#: source/pythonpath/main.py:646
msgid "Host: "
msgstr ""

View File

@ -5,17 +5,17 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: 2022-01-28 13:40-0600\n"
"PO-Revision-Date: 2022-01-28 13:43-0600\n"
"POT-Creation-Date: 2022-03-26 11:23-0600\n"
"PO-Revision-Date: 2022-03-26 11:28-0600\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.5\n"
"X-Generator: Poedit 3.0\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Generated-By: pygettext.py 1.5\n"
"X-Generator: Poedit 3.0.1\n"
#: source/pythonpath/main.py:81
msgid "Do you want to install 'Python installer package' (PIP)?"
@ -23,7 +23,7 @@ msgstr "¿Desea instalar el instalador de paquetes Python (PIP)?"
#: source/pythonpath/main.py:93
msgid "Downloading PIP…"
msgstr "Descargando PIP..."
msgstr "Descargando PIP"
#: source/pythonpath/main.py:96
msgid "Unable to connect to internet"
@ -39,7 +39,7 @@ msgstr "Se ha guardado el archivo de instalación de PIP"
#: source/pythonpath/main.py:108
msgid "Starting PIP installation…"
msgstr "Iniciando la instalación de PIP..."
msgstr "Iniciando la instalación de PIP"
#: source/pythonpath/main.py:122
msgid "PIP was installed sucessfully"
@ -51,12 +51,24 @@ msgstr "Ha fallado la instalación de PIP, vea el registro"
#: source/pythonpath/main.py:198
msgid "Package not found…"
msgstr "Paquete no encontrado..."
msgstr "Paquete no encontrado"
#: source/pythonpath/main.py:213
msgid "Enter the package name to install"
msgstr "Introduzca el nombre del paquete a instalar"
#: source/pythonpath/main.py:218
msgid "Do you really want to install package: {}?"
msgstr "¿Desea instalar el paquete: {}?"
#: source/pythonpath/main.py:247
msgid "install"
msgstr "instalar"
#: source/pythonpath/main.py:249
msgid "upgrade"
msgstr "actualizar"
#: source/pythonpath/main.py:252
msgid ""
"Do you want to {}:\n"
@ -81,6 +93,16 @@ msgstr ""
"\n"
"{} ?"
#: source/pythonpath/main.py:310
msgid ""
"Do you want to install from:\n"
"\n"
"{}"
msgstr ""
"¿Desea desinstalar desde:\n"
"\n"
"{} ?"
#: source/pythonpath/main.py:337
msgid "Do you want to save this Proxy settings?"
msgstr "¿Desea guardar la configuración del Proxy?"
@ -95,7 +117,7 @@ msgstr "¿Desea eliminar la configuración del Proxy?"
#: source/pythonpath/main.py:355
msgid "Proxy settings deleted sucessfully"
msgstr "PIP instalado correctamente"
msgstr "Configuración del Proxy borrada correctamente"
#: source/pythonpath/main.py:413
msgid "'Python installer package' (PIP) is not installed"
@ -117,6 +139,10 @@ msgstr "Paquetes"
msgid "~Close"
msgstr "~Cerrar"
#: source/pythonpath/main.py:635
msgid "Zaz-Pip - Proxy settings"
msgstr "Zaz-Pip - Configuración del Proxy"
#: source/pythonpath/main.py:646
msgid "Host: "
msgstr "Host: "

Binary file not shown.

View File

@ -0,0 +1,168 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: 2022-03-26 11:23-0600\n"
"PO-Revision-Date: 2022-03-26 11:41-0600\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: fr_FR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"Generated-By: pygettext.py 1.5\n"
"X-Generator: Poedit 3.0.1\n"
#: source/pythonpath/main.py:81
msgid "Do you want to install 'Python installer package' (PIP)?"
msgstr "Souhaitez-vous installer 'Python installer package' (PIP) ?"
#: source/pythonpath/main.py:93
msgid "Downloading PIP…"
msgstr "Téléchargement de PIP…"
#: source/pythonpath/main.py:96
msgid "Unable to connect to internet"
msgstr "Pas de connection à internet"
#: source/pythonpath/main.py:102
msgid "Unable to copy PIP installation file"
msgstr "La réception du fichier d'installation de PIP a échoué"
#: source/pythonpath/main.py:105
msgid "PIP installation file has been saved"
msgstr "Fichier d'installation de PIP reçu"
#: source/pythonpath/main.py:108
msgid "Starting PIP installation…"
msgstr "Démarrage de l'installation de PIP…"
#: source/pythonpath/main.py:122
msgid "PIP was installed sucessfully"
msgstr "PIP est maintenant installé"
#: source/pythonpath/main.py:125
msgid "PIP installation has failed, see log"
msgstr "Echec à l'installation de PIP, consultez le fichier Log'"
#: source/pythonpath/main.py:198
msgid "Package not found…"
msgstr "Paquet non trouvé…"
#: source/pythonpath/main.py:213
msgid "Enter the package name to install"
msgstr "Nom du paquet à installer"
#: source/pythonpath/main.py:218
msgid "Do you really want to install package: {}?"
msgstr "Souhaitez-vous désinstaller :{} ?"
#: source/pythonpath/main.py:247
msgid "install"
msgstr "installer"
#: source/pythonpath/main.py:249
msgid "upgrade"
msgstr "mise à niveau"
#: source/pythonpath/main.py:252
msgid ""
"Do you want to {}:\n"
"\n"
"{}?"
msgstr ""
"Souhaitez-vous {}\n"
"\n"
"\"{}"
#: source/pythonpath/main.py:278
msgid "Select installed package"
msgstr "Choisissez un paquet déjà installé"
#: source/pythonpath/main.py:283
msgid ""
"Do you want to uninstall:\n"
"\n"
"{}?"
msgstr ""
"Souhaitez-vous désinstaller :\n"
"\n"
"{} ?"
#: source/pythonpath/main.py:310
msgid ""
"Do you want to install from:\n"
"\n"
"{}"
msgstr ""
"Souhaitez-vous installer :\n"
"\n"
"{} ?"
#: source/pythonpath/main.py:337
msgid "Do you want to save this Proxy settings?"
msgstr "Enregistrer les données du mandataire (Proxy) ?"
#: source/pythonpath/main.py:343
msgid "Proxy settings saved sucessfully"
msgstr "Les données mandataire (Proxy) sont enregistrées"
#: source/pythonpath/main.py:349
msgid "Do you want to remove Proxy settings?"
msgstr "Supprimer les données du mandataire (Proxy)?"
#: source/pythonpath/main.py:355
msgid "Proxy settings deleted sucessfully"
msgstr "Les données mandataire (Proxy) sont supprimées"
#: source/pythonpath/main.py:413
msgid "'Python installer package' (PIP) is not installed"
msgstr "'Python installer package' (PIP) absent"
#: source/pythonpath/main.py:432
msgid "Admin PIP"
msgstr "Administrer PIP"
#: source/pythonpath/main.py:445
msgid "Install PIP"
msgstr "Installer PIP"
#: source/pythonpath/main.py:494
msgid "Packages"
msgstr "Paquets"
#: source/pythonpath/main.py:536
msgid "~Close"
msgstr "~Fermer"
#: source/pythonpath/main.py:635
msgid "Zaz-Pip - Proxy settings"
msgstr "Zaz-PIP Données mandataire (Proxy)"
#: source/pythonpath/main.py:646
msgid "Host: "
msgstr "Hôte: "
#: source/pythonpath/main.py:669
msgid "Port: "
msgstr "Port: "
#: source/pythonpath/main.py:690
msgid "User: "
msgstr "Utilisateur: "
#: source/pythonpath/main.py:711
msgid "Password: "
msgstr "Mot de passe: "
#: source/pythonpath/main.py:733
msgid "Save"
msgstr "Enregistrer"
#: source/pythonpath/main.py:745
msgid "Delete"
msgstr "Supprimer"

57
source/pip_runner.py Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
@ -151,7 +180,8 @@ class Controllers(object):
def txt_search_key_released(self, event):
if event.KeyCode == app.KEY['enter']:
self.cmd_search_action(None)
# ~ self.cmd_search_action(None)
self.cmd_install_action(None)
return
if not self.d.txt_search.value.strip():
@ -215,7 +245,7 @@ class Controllers(object):
self.d.txt_search.set_focus()
return
msg = _(f'Do you really want to install package: {name}?')
msg = _('Do you really want to install package: {}?').format(name)
if not app.question(msg):
return
@ -229,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}')
@ -241,12 +274,37 @@ 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):
opt = 'install'
opt = _('install')
if self._states['list']:
opt = 'upgrade'
opt = _('upgrade')
name = self.d.lst_package.value
msg = _('Do you want to {}:\n\n{}?').format(opt, name)
@ -271,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):
@ -307,7 +366,7 @@ class Controllers(object):
if not path:
return
msg = _(f'Do you want to install from:\n\n{path}')
msg = _('Do you want to install from:\n\n{}').format(path)
if not app.question(msg):
return
@ -632,7 +691,7 @@ def _create_dialog():
def _create_dialog_proxy():
args= {
'Name': 'dlg_proxy',
'Title': 'Zaz-Pip - Proxy settings',
'Title': _('Zaz-Pip - Proxy settings'),
'Width': 150,
'Height': 100,
}

View File

@ -0,0 +1,14 @@
This file is part of ZAZPip.
ZAZPip is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ZAZPip is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with ZAZPip. If not, see <https://www.gnu.org/licenses/>.

2
zaz.py
View File

@ -755,7 +755,7 @@ def _new(args):
path = _join(args.target, args.name)
_mkdir(path)
_mkdir(_join(path, 'files'))
_mkdir(_join(path, 'ext'))
_mkdir(_join(path, 'images'))
path_logo = 'images/pymacros.png'
copyfile(path_logo, _join(path, 'images/logo.png'))