Compare commits

...

10 Commits
main ... 0.96.1

Author SHA1 Message Date
Juan Pablo Ugarte
ae11d6485a
Rolling 0.96.1 2025-05-16 08:33:13 -04:00
Juan Pablo Ugarte
a69df48d17
CmbVersionNotificationView: do not show read more button if there is no link 2025-05-15 18:29:26 -04:00
Juan Pablo Ugarte
090ef7f633
CmbObjectDataEditor: fix object data remove. 2025-05-14 17:19:13 -04:00
Juan Pablo Ugarte
4cce14ea3f
CmbProject: fix GResource list model update 2025-05-13 19:29:57 -04:00
Juan Pablo Ugarte
7ebfa1999c
CmbResource: misc improvements
- Notify display-name when underlying props changes
 - Add private functions to update parent data needed to keep
   list model in sync
2025-05-13 19:29:57 -04:00
Juan Pablo Ugarte
d81f8ee7fd
CmbResourceEditor: use open for choosing a file 2025-05-13 19:29:57 -04:00
Juan Pablo Ugarte
aef72f38cc
CmbDB: add update_gresource_children_position() 2025-05-13 19:29:57 -04:00
Juan Pablo Ugarte
3527266b4a
CmbFileButton: add use_open property 2025-05-13 19:29:57 -04:00
Juan Pablo Ugarte
d8f59a9144
run-dev.py: Misc cleanups
- Make tests work without any special env set
 - Remove .local.env bash env
 - Simplify coverage script
2025-05-12 20:37:53 -04:00
Juan Pablo Ugarte
c8a3f36641
CmbNotification: generate UUID locally 2025-05-12 20:35:46 -04:00
21 changed files with 243 additions and 119 deletions

View File

@ -1,13 +0,0 @@
#!/usr/bin/bash
SCRIPT=$(readlink -f $0)
DIRNAME=$(dirname $SCRIPT)
ARCH_TRIPLET=$(cc -dumpmachine)
export LIBDIR=$DIRNAME/.local/lib/$ARCH_TRIPLET
export LD_LIBRARY_PATH=$LIBDIR:$LIBDIR/cambalache:$LIBDIR/cmb_catalog_gen:$LD_LIBRARY_PATH
export GI_TYPELIB_PATH=$LIBDIR/girepository-1.0:$LIBDIR/cambalache:$LIBDIR/cmb_catalog_gen:$GI_TYPELIB_PATH
export PKG_CONFIG_PATH=$LIBDIR/pkgconfig:$PKG_CONFIG_PATH
export GSETTINGS_SCHEMA_DIR=$DIRNAME/.local/share/glib-2.0/schemas:$GSETTINGS_SCHEMA_DIR
export XDG_DATA_DIRS=$DIRNAME/.local/share:$XDG_DATA_DIRS
export PYTHONPATH=$DIRNAME/.local/lib/python3/dist-packages:$PYTHONPATH
export PATH=$DIRNAME/.local/bin:$PATH

View File

@ -9,6 +9,11 @@ Cambalache used even/odd minor numbers to differentiate between stable and
development releases.
## 0.96.1
- Fix/improve GResource list model update
- Fix removing custom class data like styles
## 0.96.0
- Add GResource support

View File

@ -152,7 +152,7 @@ cambalache under .local directoy and set up all environment variables needed to
run the app from the source directory. (Follow manual installation to ensure
you have everything needed)
`./run-dev.sh`
`./run-dev.py`
This is meant for Cambalache development only.

View File

@ -221,7 +221,10 @@
<ui template-class="CmbContextMenu" filename="cmb_context_menu.ui" sha256="81eba3adf715348a5c03ef4cbc151eebd5d9aa8b5a14c5968232f68a61ae573c"/>
<ui template-class="CmbDBInspector" filename="cmb_db_inspector.ui" sha256="4451cdb08d24bd4a802ea692c0ebb4ef46af13152984c0b435d29bf4eb7dab55"/>
<ui filename="app/cmb_shortcuts.ui" sha256="d7ac37fd2430788a9e210ed4bc84dcfeba5609bdcc801afb192bfd900c7a8883"/>
<ui template-class="CmbFileButton" filename="control/cmb_file_button.ui" sha256="f859b4f85d7c80c1fef69b68ebb9129423d9c72fdb38d304132784f7361cbbfd"/>
<ui template-class="CmbFileButton" filename="control/cmb_file_button.ui" sha256="f859b4f85d7c80c1fef69b68ebb9129423d9c72fdb38d304132784f7361cbbfd">
<property id="dialog_title" type-id="gchararray" disable-inline-object="0" required="0" disabled="0"/>
<property id="use_open" type-id="gboolean" disable-inline-object="0" required="0" disabled="0"/>
</ui>
<ui template-class="CmbNotificationListView" filename="cmb_notification_list_view.ui" sha256="13622645038ef2aaa154f74cd300f9c0fa0dccf69d45d6c9376f9034e6ee57fb"/>
<ui template-class="CmbVersionNotificationView" filename="cmb_version_notification_view.ui" sha256="9a3ced46b90eb7e425d1c345853c4e8e908870c61c75475f7e20ce3c9ee8cec6"/>
<ui template-class="CmbMessageNotificationView" filename="cmb_message_notification_view.ui" sha256="debeffd184e225d82ed29ac590654b8160363e8d5606366dc8acb3ff9840fee3"/>

View File

@ -2023,7 +2023,7 @@ class CmbDB(GObject.GObject):
self.__unknown_tag(child, root, child.tag)
continue
prefix, = self.__node_get(child, "prefix")
prefix, = self.__node_get(child, ["prefix"])
resource_id = self.add_gresource("gresource", parent_id=gresource_id, gresource_prefix=prefix)
@ -3034,6 +3034,20 @@ class CmbDB(GObject.GObject):
(ui_id, ) if parent_id is None else (ui_id, parent_id)
)
def update_gresource_children_position(self, gresource_id):
self.execute(
"""
UPDATE gresource SET position=new.position - 1
FROM (
SELECT row_number() OVER (PARTITION BY parent_id ORDER BY position) position, parent_id, gresource_id
FROM gresource
WHERE parent_id=?
) AS new
WHERE gresource.parent_id=new.parent_id AND gresource.gresource_id=new.gresource_id;
""",
(gresource_id, )
)
# Function used in SQLite

View File

@ -36,6 +36,8 @@ class CmbGResource(CmbBaseGResource, Gio.ListModel):
path_parent = GObject.Property(type=CmbPath, flags=GObject.ParamFlags.READWRITE)
def __init__(self, **kwargs):
self._last_known = None
super().__init__(**kwargs)
self.connect("notify", self.__on_notify)
@ -47,6 +49,13 @@ class CmbGResource(CmbBaseGResource, Gio.ListModel):
return f"CmbGResource<{self.resource_type}> id={self.gresource_id}"
def __on_notify(self, obj, pspec):
resource_type = self.resource_type
if (resource_type == "gresources" and pspec.name == "gresources-filename") or \
(resource_type == "gresource" and pspec.name == "gresource-prefix") or \
(resource_type == "file" and pspec.name == "file-filename"):
obj.notify("display-name")
self.project._gresource_changed(self, pspec.name)
@GObject.Property(type=CmbBaseGResource)
@ -84,6 +93,34 @@ class CmbGResource(CmbBaseGResource, Gio.ListModel):
file_filename = self.file_filename
return file_filename if file_filename else _("Unnamed file {id}").format(id=self.gresource_id)
# GListModel helpers
def _save_last_known_parent_and_position(self):
self._last_known = (self.parent, self.position)
def _update_new_parent(self):
parent = self.parent
position = self.position
# Emit GListModel signal to update model
if parent:
parent.items_changed(position, 0, 1)
parent.notify("n-items")
self._last_known = None
def _remove_from_old_parent(self):
if self._last_known is None:
return
parent, position = self._last_known
# Emit GListModel signal to update model
if parent:
parent.items_changed(position, 1, 0)
parent.notify("n-items")
self._last_known = None
# GListModel iface
def do_get_item(self, position):
gresource_id = self.gresource_id

View File

@ -221,6 +221,7 @@
</child>
<child>
<object class="CmbFileButton" id="file_filename">
<property name="use-open">True</property>
<layout>
<property name="column">1</property>
<property name="column-span">1</property>

View File

@ -30,6 +30,7 @@ import http.client
import time
import platform
from uuid import uuid4
from urllib.parse import urlparse
from .config import VERSION
from gi.repository import GObject, GLib, Gio, Gdk, Gtk, Adw, HarfBuzz
@ -130,7 +131,7 @@ class CmbNotificationCenter(GObject.GObject):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.retry_interval = 1
self.retry_interval = 2
self.user_agent = self.__get_user_agent()
self.store = Gio.ListStore(item_type=CmbNotification)
self.settings = Gio.Settings(schema_id="ar.xjuan.Cambalache.notification")
@ -156,6 +157,10 @@ class CmbNotificationCenter(GObject.GObject):
logger.warning(f"{backend.scheme} is not supported, only HTTPS")
return
# Ensure we have a UUID
if not self.uuid:
self.uuid = str(uuid4())
logger.info(f"User Agent: {self.user_agent}")
logger.info(f"UUID: {self.uuid}")
@ -248,9 +253,6 @@ class CmbNotificationCenter(GObject.GObject):
def __get_notification_idle(self, data):
logger.debug(f"Got notification response {json.dumps(data, indent=2, sort_keys=True)}")
if "uuid" in data:
self.uuid = data["uuid"]
if "notification" in data:
notification = self.__notification_from_dict(data["notification"])
self.store.insert(0, notification)
@ -264,10 +266,10 @@ class CmbNotificationCenter(GObject.GObject):
return GLib.SOURCE_REMOVE
def __get_notification_thread(self):
headers = {"User-Agent": self.user_agent}
if self.uuid:
headers["x-cambalache-uuid"] = self.uuid
headers = {
"User-Agent": self.user_agent,
"x-cambalache-uuid": self.uuid,
}
try:
logger.info(f"GET /notification {headers=}")
@ -277,7 +279,7 @@ class CmbNotificationCenter(GObject.GObject):
assert response.status == 200
# Reset retry interval
self.retry_interval = 1
self.retry_interval = 2
data = response.read().decode()
@ -318,19 +320,19 @@ class CmbNotificationCenter(GObject.GObject):
def __poll_vote_idle(self, data):
logger.debug(f"Got vote response {data}")
uuid = data["uuid"]
poll_uuid = data["uuid"]
results = data["results"]
for notification in self.store:
if isinstance(notification, CmbPollNotification) and notification.poll.id == uuid:
if isinstance(notification, CmbPollNotification) and notification.poll.id == poll_uuid:
notification.results = CmbPollResult(**results)
self.__save_notifications()
break
return GLib.SOURCE_REMOVE
def __poll_vote_exception_idle(self, uuid):
def __poll_vote_exception_idle(self, poll_uuid):
for notification in self.store:
if isinstance(notification, CmbPollNotification) and notification.poll.id == uuid:
if isinstance(notification, CmbPollNotification) and notification.poll.id == poll_uuid:
notification.my_votes = []
break
return GLib.SOURCE_REMOVE

View File

@ -70,7 +70,10 @@ class CmbObjectDataEditor(Gtk.Box):
if self.info:
self.object.remove_data(self.__data)
else:
self.__data.parent.remove_data(self.__data)
if self.__data.parent:
self.__data.parent.remove_data(self.__data)
else:
self.__data.object.remove_data(self.__data)
@GObject.Property(type=GObject.Object)
def object(self):
@ -133,13 +136,11 @@ class CmbObjectDataEditor(Gtk.Box):
self.__update_arg(key)
def __on_data_added(self, obj, data):
if self.info and self.data is None and self.info == data.info:
self.data = data
self.__update_view()
self.data = data
self.__update_view()
def __on_data_removed(self, obj, data):
if self.object and self.info:
self.__remove_data_editor(data)
self.__remove_data_editor(data)
def __ensure_object_data(self, history_message):
if self.data:

View File

@ -999,33 +999,41 @@ class CmbProject(GObject.Object, Gio.ListModel):
self.db.commit()
self.history_pop()
except Exception:
logger.warning("Tried to add GResource", exc_info=True)
return None
else:
return self.__add_gresource(True, gresource_id, resource_type)
finally:
gresource = self.__add_gresource(True, gresource_id, resource_type)
gresource._update_new_parent()
return gresource
def __remove_gresource(self, gresource):
if gresource is None:
logger.warning("Tried to remove a None GResource", exc_info=True)
return
print("__remove_gresource", gresource, self.__gresource_id.get(gresource.gresource_id, None))
self.__gresource_id.pop(gresource.gresource_id, None)
self.__selection_remove(gresource)
print("SELECTION", self.__selection)
self.__gresource_id.pop(gresource.gresource_id, None)
self.emit("gresource-removed", gresource)
def remove_gresource(self, gresource):
try:
print("remove_gresource", gresource)
parent_id = gresource.parent_id
gresource._save_last_known_parent_and_position()
self.history_push(_('Remove GResource "{name}"').format(name=gresource.display_name))
self.db.execute("DELETE FROM gresource WHERE gresource_id=?;", (gresource.gresource_id,))
# Update position
if parent_id:
self.db.update_gresource_children_position(parent_id)
self.history_pop()
self.db.commit()
self.__remove_gresource(gresource)
except Exception as e:
logger.warning(f"Error removing gresource {e}", exc_info=True)
finally:
self.__remove_gresource(gresource)
gresource._remove_from_old_parent()
def get_css_providers(self):
return list(self.__css_id.values())
@ -1117,7 +1125,7 @@ class CmbProject(GObject.Object, Gio.ListModel):
except Exception as e:
logger.warning(f"Error adding object {obj_name}: {e}")
return None
else:
finally:
obj = self.__add_object(True, ui_id, object_id, obj_type, name, parent_id, position=position)
obj._update_new_parent()
return obj
@ -1283,10 +1291,17 @@ class CmbProject(GObject.Object, Gio.ListModel):
obj._property_changed(p)
def __undo_redo_do(self, undo, update_objects=None):
def get_object_position(c, row):
ui_id, parent_id, position = row[0], row[4], row[8]
parent = self.get_object_by_id(ui_id, parent_id)
return parent, position
def get_object_position(table, row):
if table == "object":
ui_id, parent_id, position = row[0], row[4], row[8]
parent = self.get_object_by_id(ui_id, parent_id)
return parent, position
elif table == "gresource":
parent_id, position = row[2], row[3]
parent = self.get_gresource_by_id(parent_id)
return parent, position
return None, None
c = self.db.cursor()
@ -1303,8 +1318,8 @@ class CmbProject(GObject.Object, Gio.ListModel):
# Undo or Redo command
if command == "INSERT":
if table == "object":
parent, position = get_object_position(c, new_values)
if table in ["object", "gresource"]:
parent, position = get_object_position(table, new_values)
if undo:
update_objects.append((parent, position, 1, 0))
@ -1318,8 +1333,8 @@ class CmbProject(GObject.Object, Gio.ListModel):
self.__undo_redo_update_insert_delete(c, undo, command, table, columns, table_pk, old_values, new_values)
elif command == "DELETE":
if table == "object":
parent, position = get_object_position(c, old_values)
if table in ["object", "gresource"]:
parent, position = get_object_position(table, old_values)
if undo:
update_objects.append((parent, position, 0, 1))
@ -1335,8 +1350,8 @@ class CmbProject(GObject.Object, Gio.ListModel):
elif command == "UPDATE":
# parent_id and position have to change together because their are part of a unique index
if update_objects is not None and table == "object" and "position" in columns and "parent_id" in columns:
old_parent, old_position = get_object_position(c, old_values)
new_parent, new_position = get_object_position(c, new_values)
old_parent, old_position = get_object_position(table, old_values)
new_parent, new_position = get_object_position(table, new_values)
if undo:
if old_position >= 0:
@ -1348,6 +1363,9 @@ class CmbProject(GObject.Object, Gio.ListModel):
update_objects.append((new_parent, new_position, 0, 1))
if old_position >= 0:
update_objects.append((old_parent, old_position, 1, 0))
elif table == "gresource":
# TODO
pass
if undo:
self.db.history_update(table, columns, table_pk, old_values)
@ -1476,12 +1494,27 @@ class CmbProject(GObject.Object, Gio.ListModel):
else:
obj._remove_data(data)
else:
parent = obj.data_dict.get(f"{row[2]}.{row[6]}", None)
owner_id, data_id, id, parent_id = row[2], row[3], row[4], row[6]
parent = obj.data_dict.get(f"{owner_id}.{parent_id}", None)
if parent:
parent._add_child(row[2], row[3], row[4])
parent._add_child(owner_id, data_id, id)
else:
obj._add_data(row[2], row[3], row[4])
info = self.type_info.get(owner_id)
taginfo = None
if info:
r = self.db.execute(
"SELECT key FROM type_data WHERE owner_id=? AND data_id=?;",
(owner_id, data_id)
).fetchone()
data_key = r[0] if r else None
if data_key:
taginfo = info.get_data_info(data_key)
obj._add_data(owner_id, data_id, id, info=taginfo)
elif table == "object_data_arg":
obj = self.get_object_by_id(pk[0], pk[1])
if obj:

View File

@ -49,4 +49,9 @@ class CmbVersionNotificationView(Gtk.Box):
notification = self.notification
self.version_label.props.label = _("<b>Version {version} is available</b>").format(version=notification.version)
self.release_notes_label.props.label = notification.release_notes
self.read_more_button.props.uri = notification.read_more_url
if notification.read_more_url:
self.read_more_button.props.uri = notification.read_more_url
self.read_more_button.show()
else:
self.read_more_button.hide()

View File

@ -35,6 +35,7 @@ class CmbFileButton(Gtk.Button):
dirname = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE)
dialog_title = GObject.Property(type=str, default=_("Select filename"), flags=GObject.ParamFlags.READWRITE)
use_open = GObject.Property(type=bool, default=False, flags=GObject.ParamFlags.READWRITE)
label = Gtk.Template.Child()
@ -59,12 +60,15 @@ class CmbFileButton(Gtk.Button):
def dialog_callback(dialog, res):
try:
file = dialog.save_finish(res)
file = dialog.open_finish(res) if self.use_open else dialog.save_finish(res)
self.cmb_value = os.path.relpath(file.get_path(), start=self.dirname)
except Exception:
pass
dialog.save(self.get_root(), None, dialog_callback)
if self.use_open:
dialog.open(self.get_root(), None, dialog_callback)
else:
dialog.save(self.get_root(), None, dialog_callback)
@GObject.Property(type=str)
def cmb_value(self):

View File

@ -1,11 +1,7 @@
#!/bin/bash
source .local.env
SCRIPT=$(readlink -f $0)
DIRNAME=$(dirname $SCRIPT)
export GSETTINGS_BACKEND=memory
export HOME=$DIRNAME/.local/home
mkdir -p $HOME/Projects
export COVERAGE_PROCESS_START=$DIRNAME/pyproject.toml

View File

@ -14,6 +14,15 @@
</p>
</description>
<releases>
<release date="2025-05-16" version="0.96.1">
<description>
<p>GResource First Bugfix Release!</p>
<ul>
<li>Fix/improve GResource list model update</li>
<li>Fix removing custom class data like styles</li>
</ul>
</description>
</release>
<release date="2025-04-20" version="0.96.0">
<description>
<p>GResource Release!</p>

View File

@ -1,6 +1,6 @@
project(
'cambalache', 'c',
version: '0.96.0',
version: '0.96.1',
meson_version: '>= 1.1.0',
default_options: [
'c_std=c11',

39
run-dev.py Executable file
View File

@ -0,0 +1,39 @@
#!/bin/python3
#
# run-dev - Script to run Cambalache from sources
#
# Copyright (C) 2025 Juan Pablo Ugarte
#
# This program 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; version 2 of the License.
#
# This program 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 this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# Authors:
# Juan Pablo Ugarte <juanpablougarte@gmail.com>
#
import os
import sys
import locale
from tools.cmb_init_dev import cmb_init_dev
# Compile deps and install things in .local
cmb_init_dev()
basedir = os.path.join(os.path.split(os.path.dirname(__file__))[0])
locale.bindtextdomain("cambalache", os.path.join(basedir, ".local", "share", "locale"))
locale.textdomain("cambalache")
from cambalache.app import CmbApplication # noqa E402
CmbApplication().run(sys.argv)

View File

@ -1,16 +0,0 @@
#!/usr/bin/bash
source .local.env
if python3 $DIRNAME/tools/cmb_init_dev.py; then
python3 - $@ << EOF
import sys
import locale
locale.bindtextdomain("cambalache", "$DIRNAME/.local/share/locale")
locale.textdomain("cambalache")
from cambalache.app import CmbApplication
CmbApplication().run(sys.argv)
EOF
else
echo Could not initialize dev environment
fi

View File

@ -1,10 +0,0 @@
#!/usr/bin/bash
source .local.env
SCRIPT=$(readlink -f $0)
DIRNAME=$(dirname $SCRIPT)
export GSETTINGS_BACKEND=memory
export HOME=$DIRNAME/.local/home
mkdir -p $HOME/Projects
pytest $@

View File

@ -1,5 +1,15 @@
import os
import gi
basedir = os.path.join(os.path.split(os.path.dirname(__file__))[0])
# Ensure home directory
homedir = os.path.join(basedir, ".local", "home")
os.makedirs(os.path.join(homedir, "Projects"), exist_ok=True)
os.environ["GSETTINGS_BACKEND"] = "memory"
os.environ["HOME"] = homedir
gi.require_version("Gtk", "4.0")
from gi.repository import Gtk # noqa E402
from tools.cmb_init_dev import cmb_init_dev # noqa E402

View File

@ -13,7 +13,7 @@ from . import utils as test_utils
DAY = 3600 * 24
now = utils.utcnow()
CMB_UUID = str(uuid4())
CMB_UUID = notification_center.uuid
POLL_UUID = str(uuid4())
POLL_NOTIFICATION_BASE = {
@ -44,16 +44,14 @@ def wait_for_all_threads():
def test_cmb_notification_disabled():
assert not notification_center.uuid
assert notification_center.enabled is False
assert len(notification_center.store) == 0
notification_center.enabled = True
@pytest.mark.parametrize("response, headers, n", [
@pytest.mark.parametrize("response, n", [
(
{
"uuid": CMB_UUID,
"notification": {
"type": "version",
"start_date": now - DAY,
@ -63,14 +61,10 @@ def test_cmb_notification_disabled():
"read_more_url": "http://localhost"
}
},
{
'User-Agent': notification_center.user_agent
},
1
),
(
{
"uuid": CMB_UUID,
"notification": {
"type": "message",
"start_date": now - DAY,
@ -79,26 +73,16 @@ def test_cmb_notification_disabled():
"message": "This is a message notification"
}
},
{
'User-Agent': notification_center.user_agent,
'x-cambalache-uuid': CMB_UUID
},
2
),
(
{
"uuid": CMB_UUID,
"notification": POLL_NOTIFICATION_BASE
},
{
'User-Agent': notification_center.user_agent,
'x-cambalache-uuid': CMB_UUID
},
3
),
(
{
"uuid": CMB_UUID,
"notification": {
**POLL_NOTIFICATION_BASE,
"results": {
@ -107,14 +91,10 @@ def test_cmb_notification_disabled():
}
}
},
{
'User-Agent': notification_center.user_agent,
'x-cambalache-uuid': CMB_UUID
},
4
)
])
def test_cmb_notification_get(mocker, response, headers, n):
def test_cmb_notification_get(mocker, response, n):
wait_for_all_threads()
mocker.patch(
@ -137,7 +117,14 @@ def test_cmb_notification_get(mocker, response, headers, n):
test_utils.process_all_pending_gtk_events()
request_mock.assert_called_with("GET", "/notification", headers=headers)
request_mock.assert_called_with(
"GET",
"/notification",
headers={
'User-Agent': notification_center.user_agent,
'x-cambalache-uuid': CMB_UUID
}
)
assert notification_center.uuid == CMB_UUID
on_new_notification.assert_called()

View File

@ -28,12 +28,29 @@ import signal
import subprocess
basedir = os.path.join(os.path.split(os.path.dirname(__file__))[0])
sys.path.insert(1, basedir)
cambalachedir = os.path.join(basedir, "cambalache")
localdir = os.path.join(basedir, ".local")
locallibdir = os.path.join(localdir, "lib", sys.implementation._multiarch)
cambalachedir = os.path.join(basedir, "cambalache")
localpkgdatadir = os.path.join(localdir, "share", "cambalache")
catalogsdir = os.path.join(localpkgdatadir, "catalogs")
localbindir = os.path.join(localdir, "bin")
for var, value in [
("LD_LIBRARY_PATH", f"{locallibdir}:{locallibdir}/cambalache:{locallibdir}/cmb_catalog_gen"),
("GI_TYPELIB_PATH", f"{locallibdir}/girepository-1.0:{locallibdir}/cambalache:{locallibdir}/cmb_catalog_gen"),
("PKG_CONFIG_PATH", os.path.join(locallibdir, "pkgconfig")),
("GSETTINGS_SCHEMA_DIR", os.path.join(localdir, "share", "glib-2.0", "schemas")),
("XDG_DATA_DIRS", os.path.join(localdir, "share")),
("PYTHONPATH", os.path.join(localdir, "lib", "python3", "dist-packages"))
]:
if var in os.environ:
old_value = os.environ[var]
os.environ[var] = f"{value}:{old_value}"
else:
os.environ[var] = value
sys.path.insert(1, basedir)
sys.path.insert(1, localbindir)
from gi.repository import GLib # noqa: E402