Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using PyGObject with poetry2nix crashes with ImportError: libpthread.so.0: undefined symbol: __libc_siglongjmp, version GLIBC_PRIVATE #1309

Closed
hagen-eriksen opened this issue Sep 13, 2023 · 2 comments · Fixed by #1370

Comments

@hagen-eriksen
Copy link

hagen-eriksen commented Sep 13, 2023

Describe the issue

My Python script uses PyGObject:

import gi
gi.require_version('Notify', '0.7')
from gi.repository import Notify

When I run my code packaged using mkPoetryApplication, I get the following error:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/nix/store/vagb0sjv83ybi435i6iiv10hjrdghph9-python3-3.10.12/lib/python3.10/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 688, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 883, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/home/yves/scripts/musnify-mpd/musnify_mpd/__main__.py", line 7, in <module>
    import gi
  File "/home/yves/.cache/pypoetry/virtualenvs/musnify-mpd-Ql-lE92A-py3.10/lib/python3.10/site-packages/gi/__init__.py", line 40, in <module>
    from . import _gi
ImportError: /nix/store/0xxjx37fcy2nl3yz6igmv4mag2a7giq6-glibc-2.33-123/lib/libpthread.so.0: undefined symbol: __libc_siglongjmp, version GLIBC_PRIVATE

Additional context

I use i3 on NixOS. I am trying to write a little script that sends notifications when the song played by MusicPlayerDeamon (MPD) changes. I use a modified version of musnify-mpd.

The problem has to do with using the Python package PyGObject which requires the GI_TYPELIB_PATH environment variable to be set in order for the package to find bindings with GTK-related libraries (I need this to send notifications).

If I activate my developer environment and run nix run '.#default', the script executes fine. If I run nix run '/path/to/flake#default' outside of the flake devshell or if I try poetry run musnify-mpd from the devshell, the code crashes with the error above.

I think that I am doing something wrong when setting up the poetry2nix derivations (for the application and the development environment) and the package nixpkgs.gobject-introspection which sets the environment variable, yet I have not been able to find a solution to my problem as of yet.

flake.nix:

{
  inputs = {
    flake-utils.url = "github:numtide/flake-utils";
    poetry2nix = {
      url = "github:nix-community/poetry2nix";
      inputs = {
        nixpkgs.follows = "nixpkgs";
      };
    };
    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
  };

  outputs = { self, flake-utils, poetry2nix, nixpkgs }:
    flake-utils.lib.eachDefaultSystem (
      system:
      let
        pkgs = import nixpkgs { inherit system; };

        overrides = pkgs.poetry2nix.defaultPoetryOverrides.extend (self: super: {
            python-mpd2 = super.python-mpd2.overridePythonAttrs (
              old: {
                buildInputs = (old.buildInputs or [ ]) ++ [ super.setuptools ];
              }
            );
            pycairo = super.pycairo.overridePythonAttrs (
              old: {
                nativeBuildInputs = [
                  self.meson
                  pkgs.buildPackages.ninja
                  pkgs.buildPackages.pkg-config
                  pkgs.ninja
                ];
                propogatedBuildInputs = [ pkgs.cairo ];
              }
            );
            pygobject = super.pygobject.overridePythonAttrs (
              old: {
                buildInputs = (old.buildInputs or [ ]) ++ [
                  super.setuptools
                ];
                propagatedBuildInputs = [
                  pkgs.cairo
                  self.pycairo
                ];
              }
            );
          });

        runtimeDeps = with pkgs; [
          gobject-introspection
          gtk3
          libnotify
        ];

      in {
        packages = {
          app = pkgs.poetry2nix.mkPoetryApplication {
            inherit overrides;
            projectDir = self;
            buildInputs = runtimeDeps;
          };
          default = self.packages.${system}.app;
        };

        devShells.default = pkgs.mkShell {
          buildInputs = with pkgs; [
            (pkgs.poetry2nix.mkPoetryEnv {
              inherit overrides;
              projectDir = ./.;
            })
          ] ++ runtimeDeps;
        };
      }
    );
}

pyproject.toml:

[tool.poetry]
name = "musnify-mpd"
version = "0.1.0"
description = ""
authors = ["Hagen Eriksen <[email protected]>"]
readme = "README.md"
include = [
  { path = "musnify_mpd" },
]

[tool.poetry.dependencies]
python = "^3.10"
python-mpd2 = "^3.1.0"
PyGObject = "^3.46.0"

[tool.poetry.scripts]
musnify-mpd = "musnify_mpd.__main__:main"


[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

poetry.lock:

# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand.

[[package]]
name = "pycairo"
version = "1.24.0"
description = "Python interface for cairo"
optional = false
python-versions = ">=3.8"
files = [
    {file = "pycairo-1.24.0-cp310-cp310-win32.whl", hash = "sha256:031f5ef2c80792673f2c54ee285f2a31779a44d7521a27a7f82e4a5ecfafc26e"},
    {file = "pycairo-1.24.0-cp310-cp310-win_amd64.whl", hash = "sha256:afccac552386ab628e8ae658185fa363e8d15a5afe96d1de43f97027dd78bdd6"},
    {file = "pycairo-1.24.0-cp311-cp311-win32.whl", hash = "sha256:4631ed794a3376ec314ce47826c3e51940b54695f4ef7d5b3245b203037ae760"},
    {file = "pycairo-1.24.0-cp311-cp311-win_amd64.whl", hash = "sha256:ed0622a0ccffb873ffe7fee1699d60779f1260fba143390e5366d55f1d1739f5"},
    {file = "pycairo-1.24.0-cp312-cp312-win32.whl", hash = "sha256:220742f187d3940d695c1af1a0c1646e26dc2199d65b7bafaa527e15c3520fd3"},
    {file = "pycairo-1.24.0-cp312-cp312-win_amd64.whl", hash = "sha256:3199d6a0538d6482c71efb816bd330515e98bb06f182e23572c77d92be98f536"},
    {file = "pycairo-1.24.0-cp38-cp38-win32.whl", hash = "sha256:c7c79e748ec849811241d29553184c3ad93c857558dbe9954a49327680b8d356"},
    {file = "pycairo-1.24.0-cp38-cp38-win_amd64.whl", hash = "sha256:6ad5c3425408ebb0dfaad2cca4a80e7524d7a309305acdb2569638b5cc988fe7"},
    {file = "pycairo-1.24.0-cp39-cp39-win32.whl", hash = "sha256:c5f6efdd86fe13d36a6b004c64d8e97cde9854d599cf6cca962afb1966fe533d"},
    {file = "pycairo-1.24.0-cp39-cp39-win_amd64.whl", hash = "sha256:26fe2c32ba24caae524d855f26f3ee1a0c1ea3291da2a19604264ed29f64d834"},
    {file = "pycairo-1.24.0.tar.gz", hash = "sha256:1444d52f1bb4cc79a4a0c0fe2ccec4bd78ff885ab01ebe1c0f637d8392bcafb6"},
]

[[package]]
name = "pygobject"
version = "3.46.0"
description = "Python bindings for GObject Introspection"
optional = false
python-versions = ">=3.8, <4"
files = [
    {file = "PyGObject-3.46.0.tar.gz", hash = "sha256:481437b05af0a66b7c366ea052710eb3aacbb979d22d30b797f7ec29347ab1e6"},
]

[package.dependencies]
pycairo = ">=1.16,<2.0"

[[package]]
name = "python-mpd2"
version = "3.1.0"
description = "A Python MPD client library"
optional = false
python-versions = ">=3.6"
files = [
    {file = "python-mpd2-3.1.0.tar.gz", hash = "sha256:f33c2cdb0d6baa74a36724f38c1c4a099a7ce2c8ec4a2bb7192150a5855df476"},
    {file = "python_mpd2-3.1.0-py2.py3-none-any.whl", hash = "sha256:c4d44a54e88a675f7301fdb11a1bd31165a6f51a664dd41e8137e92f7b02ebfb"},
]

[package.extras]
twisted = ["Twisted"]

[metadata]
lock-version = "2.0"
python-versions = "^3.10"
content-hash = "ffc58c5ce1725e8af5358125f8c993e3c85f90445c5f8568a48b5e68bda3382f"

musnify_mpd/__main__.py

#!/usr/bin/env python3

import os
import sys
import re

import gi

gi.require_version('Notify', '0.7')

from gi.repository import Notify
from mpd import MPDClient


gi.require_version('Notify', '0.7')

MUSIC_LIBRARY = os.path.expanduser("~/Music/")
COVER_REGEX = re.compile(
    r'(album|cover|\.?folder|front).*\.(gif|jpeg|jpg|png)$',
    re.I | re.X)

debug = False


def find_cover_path(path):
    for e in os.listdir(path):
        if COVER_REGEX.match(e) is not None:
            cover_path = path + e
            if debug:
                print("Cover found at " + cover_path)
            return cover_path
    if debug:
        print(f"No cover found in folder {path}")
    return None


def help():
    print("""musnify-mpd\n\nOptions:
  --help\tShow this help and exit
  -d\t\tRun with debug mode enabled
         """)


class MPDWrapper:
    def __init__(self, host="localhost", port="6600"):
        self.client = MPDClient()
        self.client.timeout = 1
        self.client.idletimeout = None
        self.client.connect(host, port)

    def get_current_song(self):
        song = self.client.currentsong()

        if "artist" not in song:
            song["artist"] = "Unknown Artist"

        if "album" not in song:
            song["album"] = "Unknown Album"

        if "title" not in song:
            try:
                song["title"] = song["file"].split("/")[-1]
            except KeyError:
                song["title"] = "Unknown Title"

        return song

    def get_status(self):
        return self.client.status()["state"]

    def wait_for_change(self):
        return self.client.idle("player")


class Musnify(object):
    def __init__(self):
        self.mpd = MPDWrapper()

        self.status = None
        self.song = None

        Notify.init("musnify-mpd")
        self.notification_previous_song = Notify.Notification.new(
                "Previous Song")

    def start(self):
        while True:
            current_song = self.mpd.get_current_song()

            if (self.song != current_song):
                self.update_and_notify_song(current_song)
                if debug:
                    print(self.song)

            self.mpd.wait_for_change()

    def update_and_notify_song(self, current_song):
        previous_song = self.song
        if previous_song is not None:
            previous_song_folder = os.path.dirname(
                    MUSIC_LIBRARY + previous_song["file"]
                    ) + "/"
            self.notification_previous_song.update(
                    f"heard: {previous_song['title']}".replace("&", "&amp;"),
                    (f"by {previous_song['artist']}").replace("&", "&amp;"),
                    find_cover_path(previous_song_folder) or "music-note-16th"
            )
            self.notification_previous_song.show()

        self.song = current_song

    def stop(self):
        Notify.uninit()


def main():
    for i in range(len(sys.argv)):
        if sys.argv[i] == "-d":
            debug = True
        if sys.argv[i] == "--help":
            help()
            exit()

    musnify = Musnify()

    try:
        musnify.start()
    except KeyboardInterrupt:
        pass
    finally:
        musnify.stop()


if __name__ == "__main__":
    main()

What should I change to my flake or to my poetry configuration to make my script work?

@hagen-eriksen
Copy link
Author

I read the following threads (and many others but those were the most relevant) without being able to solve my issue:

By removing the line gi.require_version('Notify', '0.7') in my code, I got the error typelib not found that the topics above describe.

@cpcloud
Copy link
Collaborator

cpcloud commented Oct 26, 2023

Can you try with #1370?

The test case there may also be informative regarding what to include in buildInputs/nativeBuildInputs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants