PYTHON3-ABRT(5)

πŸ“¦
version 2.17.9 Β· 4 versions
, in fedora 43

πŸ’– PYTHON3-ABRT(5) πŸ’–

okay bestie, sit down, hydrate, because we're about to get into python3-abrt β€” yes that's the manual name, PYTHON3-ABRT(5), and it's so much more fun than it sounds, trust.

🌸 NAME 🌸

python3-abrt β€” the python3-abrt documentation. Slay, we love a self-referential queen.

It's the high-level API for querying, creating and manipulating problems handled by ABRT, in Python. So instead of poking at raw bytes like a Victorian orphan, you get nice Python objects. πŸ¦‹

How does it work under the hood, you ask? It sits on top of the low-level DBus or socket API that ABRT provides. And the socket API? Bestie, it's a fallback. It only exists for systems that don't have the new DBus problem API, and it can only handle creating new problems. That's it. That's all it does. It's giving side quest. πŸ™ˆ

This project lives in the abrt repository and is distributed under GPLv2</> license. FOSS royalty, no cap. πŸ‘‘

Contents:


✨ USAGE EXAMPLES ✨

πŸ“ Creating new problem

import problem

prob = problem.Runtime(
        reason='egg_error_message: assertion "error" failed',
    )

prob.add_current_process_data()
prob.add_current_environment()
prob.save()

Look at her. problem.Runtime, a reason, grab the current process data and environment, then save(). Iconic. πŸ’…

πŸ“ Creating problem for different executable

import problem

prob = problem.Selinux(reason='Front fell off')

prob.executable = '/usr/bin/time'

prob.save()

Notice how we just set prob.executable to whatever we want. '/usr/bin/time' in this case. "Front fell off" as a reason is genuinely the most valid bug report I've ever read. 😭

πŸ“ Adding custom data

import problem

prob = problem.Runtime(
        reason='Error getting devices:'
        'GDBus.Error:org.freedesktop.DBus.Error.UnknownMethod: '
        'No such interface `org.gnome.SettingsDaemon.Power` on object at path '
        '/org/gnome/SettingsDaemon/Power'
    )

prob.add_current_process_data()
prob.custom_data = 'any'
prob['dict_access_example'] = 'works'

print(prob)
print('')

for key, value in prob.items():
    print('{0}={1}'.format(key, value))

print('Identifier:', prob.save())

The way that prob.custom_data = 'any' works and prob['dict_access_example'] = 'works' works?? Two whole ways to set stuff. Highkey obsessed. Also peep the multi-line implicit string concat in the reason β€” that's just Python being pretty. ✨

πŸ“ Querying problems

import problem

for prob in problem.list():
    print(prob)
    print(repr(prob.time))
    if hasattr(prob, 'pid'):
        print(prob.pid)

problem.list() gives you the goods, and hasattr(prob, 'pid') is your vibe check before you touch it. Never assume a field exists, bestie. That's how you get an AttributeError in front of everyone. πŸ’€

πŸ“ Querying all problems

The list method used with the auth=True parameter will try to authenticate via polkit</> to gain access to all problems on the system. Queen behavior, going for the whole system.

If there is no authentication agent running, or authentication is unsuccessful, the list of problems which belong to the current user is returned β€” same as returned by the plain list method. So no drama, you just get your own stuff back. 🫢

import problem

for prob in problem.list(auth=True):
    print(prob)
    if hasattr(prob, 'username'):
        print('Problem belongs to {0}'.format(prob.username))

πŸ“ Editing existing problems

import problem

for prob in problem.list():
    if prob.type == problem.JAVA:
        prob.delete()

    if prob.type == problem.CCPP:
        if 'password' in prob.backtrace:
            del prob.backtrace
            prob.save()

    if prob.type == problem.KERNELOOPS:
        prob.backtrace = prob.backtrace.replace(' ?', '')
        prob.save()

Three little branches, three little moods: delete it, scrub the password out and save, tidy the backtrace and save. That's the whole personality of this example and I respect it.

πŸ“ Watching for new problems

import problem
import logging

logging.basicConfig(level=logging.DEBUG)

def monitor(prob):
    print(prob)
    prob.delete()

pwatch = problem.get_problem_watcher()
pwatch.add_callback(monitor)

try:
    pwatch.run()
except KeyboardInterrupt:
    pwatch.quit()

logging.basicConfig(level=logging.DEBUG) so you can see everything, then run() forever until you smash Ctrl+C, at which point KeyboardInterrupt catches it and pwatch.quit() lets you leave gracefully. Manners! πŸŽ€

πŸ“ Watching for new problems in a thread

from __future__ import print_function

import sys
import time
import problem
import threading

class ProblemWatchThread(threading.Thread):
    def __init__(self):
        super(ProblemWatchThread, self).__init__()
        self.pwatch = problem.get_problem_watcher()
        self.pwatch.add_callback(self.handle)
        self.probcount = 0

    def handle(self, prob):
        self.probcount += 1
        print('{0}: {1}'.format(self.probcount, prob))
        # prob.delete()

    def run(self):
        self.pwatch.run()

    def stop(self):
        self.pwatch.quit()

pwt = ProblemWatchThread()
pwt.start()

i = 0
print('Waiting for new problem to appear')
spinner = ['\\', '|', '/', '-']

try:
    while True:
        time.sleep(0.1)
        print('{0}\r'.format(spinner[i]), end='')
        i += 1
        i = i % len(spinner)
        sys.stdout.flush()
except KeyboardInterrupt:
    pwt.stop()

pwt.stop()

THE SPINNER?? ['\\', '|', '/', '-'] β€” a whole rotating loader in a terminal, with \r and sys.stdout.flush() and everything. It's giving main character energy. πŸ”₯ Note the # prob.delete() is commented out, so nothing gets nuked while you're watching. And yes, from __future__ import print_function is there, it's ancient lore, don't touch it.

πŸ“ Getting bug numbers of problems reported to bugzilla

import problem

bugs = set()

for prob in problem.list():
    if not hasattr(prob, 'reported_to'):
        continue

    for line in prob.reported_to.splitlines():
        if line.startswith('Bugzilla:'):
            bug_num = int(line.split('=')[-1])
            bugs.add(bug_num)

print(bugs)

A set so no dupes, hasattr so no crashes, splitlines() then startswith('Bugzilla:') then split('=')[-1] and int() it. Textbook. You ate that. πŸ’…


πŸ‘‘ STABLE API DOCUMENTATION πŸ‘‘

πŸ’œ class problem.Problem(typ, reason, analyzer=None)

Base class for the other problem types.

No need to use this class directly, bestie β€” use one of the specific problem classes instead. This is the ancestor, not the moment. ✨

  • add_current_environment() β€” Add environment of current process to this problem object
  • add_current_process_data() β€” Add pid, gid and executable of current process to this problem object
  • chown() β€” Assures ownership of a problem for the caller
  • delete() β€” Delete this problem
  • prefetch_data() β€” Prefetch possible data fields of this problem
  • save() β€” Create this problem or update modified data

On that save() β€” here's the tea. It creates or updates the project if some of its fields were modified.

It returns None in case of modification, and the identifier if a new problem was created. So if you get None back, that's not a bug, that's a success, it just means you modified instead of created. Read the return value, bestie. 😭

πŸ’œ problem.get(identifier, auth=False)

Return problem object matching identifier.

Returns None in case the problem does not exist. Use auth=True if authentication should be attempted.

πŸ’œ problem.get_problem_watcher(auth=False)

Return ProblemWatcher object which can be used to attach callbacks called when a new problem is created.

Use auth=True if authentication should be attempted for new problems that don't belong to the current user. If not set, such a problem is ignored. So like, if you're nosy, pass the True. πŸ™ˆ

πŸ’œ problem.list(auth=False)

Return the list of the problems.

Use auth=True if authentication should be attempted.

If authentication via polkit</> fails, the function behaves as if auth=False was specified β€” only users' problems are returned. No exception, no drama, just a quiet downgrade. 🫢


🌈 Specific problem types 🌈

  • class problem.Ccpp(reason) β€” C, C++ problem
  • class problem.Java(reason) β€” Java problem
  • class problem.Kerneloops(reason) β€” Kerneloops problem
  • class problem.Python(reason) β€” Python problem
  • class problem.Python3(reason) β€” Python3 problem
  • class problem.Runtime(reason) β€” Runtime problem
  • class problem.Selinux(reason) β€” Selinux problem
  • class problem.Unknown(reason) β€” Unknown problem
  • class problem.Xorg(reason) β€” Xorg problem

Pick your fighter. They all take a reason. That's the whole bit and it's a good bit. πŸ¦‹


🧚 ProblemWatcher 🧚

class problem.watch.ProblemWatcher(auth)

New problem signal handler attached to DBus signal.

Use auth=True if authentication should be attempted for new problems that don't belong to the current user. If not set, such a problem is ignored. (Same energy as get_problem_watcher, because that's literally where you get one of these from.)

  • add_callback(fun) β€” Add callback to be called when new problem occurs.

Each callback function receives a Problem instance. πŸŽ€

  • quit() β€” Stop event listener loop
  • run() β€” Start event listener loop

run() starts it, quit() stops it, and in between your callbacks get fed Problem instances. That's the whole loop, and it's lowkey perfect. ✨

πŸ’– PROBLEM OBJECT PROPERTIES πŸ’–

okay so here's the tea on problem properties: right now there is literally no strict specification of what a problem property has to be. You are free to add your own data as you see fit β€” log files, process data, whatever β€” as long as you're planning to use them for reporting. Slay, it's your house 🏠✨


πŸŽ€ Mandatory properties required prior saving πŸŽ€

These ones you MUST have before you save, no cap:

  • executable β€” the executable path of the component which caused the problem. Example: '/usr/bin/time'. The server uses this to determine the component and package data. So like, it's giving main character energy for a reason πŸ’…

πŸ¦‹ Properties the server adds when a new problem is created πŸ¦‹

Not you doing everything yourself β€” the server pulls up and drops these in for you, bestie:

  • component β€” the component which caused this problem. Example: 'time'
  • hostname β€” hostname of the affected machine. Example: 'fiasco'
  • os_release β€” operating system release string. Example: 'Fedora release 17 (Beefy Miracle)'
  • uid β€” user ID. Example: 1000
  • username β€” example: 'jeff'
  • architecture β€” machine architecture string. Example: 'x86_64'
  • kernel β€” kernel version string. Example: '3.6.6-1.fc17.x86_64'
  • package β€” package string. Example: 'time-1.7-40.fc17.x86_64'
  • time β€” time of the occurrence (unixtime). Example: datetime.datetime(2012, 12, 2, 16, 18, 41)
  • count β€” number of times this problem occurred. Example: 1

🌈 Parsed package data 🌈

and there's also the parsed package data just sitting there, ready for you, periodt:

  • pkg_name β€” package name. Example: 'time'
  • pkg_epoch β€” package epoch. Example: 0
  • pkg_version β€” package version. Example: '1.7'
  • pkg_release β€” package release. Example: '40.fc17'
  • pkg_arch β€” package architecture. Example: 'x86_64'

✨ Other common properties ✨

these are the other common ones, and lowkey their presence differs based on problem type β€” so don't come for me if one's missing, it's just the vibe check of whichever type you got 🫢

  • abrt_version β€” ABRT version string. Example: '2.0.18.84.g211c'. Applicable to: crashes caught by ABRT.
  • cgroup β€” cgroup (control group) information for crashed process. Example: '9:perf_event:/\n8:blkio:/\n...'. Applicable to: C/C++.
  • core_backtrace β€” machine readable backtrace with no private data. Applicable to: C/C++, Python, Ruby, Kerneloops.
  • backtrace β€” original backtrace or backtrace produced by retracing process. Applicable to: C/C++ (after retracing), Python, Ruby, Xorg, Kerneloops.
  • dso_list β€” list of dynamic libraries loaded at the time of crash. Applicable to: C/C++, Python.
  • maps β€” a whole copy of the /proc/<pid>/maps file for the problem executable πŸ’… produced by C/C++.
  • cmdline β€” copy of the /proc/<pid>/cmdline file, defaulting to '/usr/bin/gtk-builder-convert'. Comes from C/C++, Python, Ruby and Kerneloops. 🌸
  • coredump β€” the actual coredump of the crashing process. C/C++ only, bestie, it's giving main character energy 😭
  • environ β€” the runtime environment of the process, captured as it was. C/C++ and Python. ✨
  • open_fds β€” the list of file descriptors that were open at the exact time of the crash. C/C++. 🫢
  • pid β€” the Process ID, defaulting to '42'. Brought to you by C/C++, Python and Ruby. πŸ”₯
  • proc_pid_status β€” a copy of the /proc/<pid>/status file. C/C++. πŸ’–
  • limits β€” a copy of the /proc/<pid>/limits file. C/C++. πŸŽ€
  • var_log_messages β€” the part of the /var/log/messages file that holds the crash information. C/C++. πŸ¦‹
  • suspend_stats β€” a copy of /sys/kernel/debug/suspend_stats. Kerneloops energy. πŸ’œ
  • reported_to β€” if the problem was already reported, this item holds the URLs of the services where it got reported. That's Reported problems coded. πŸ‘‘
  • event_log β€” the ABRT event log. Also Reported problems. ⭐
  • dmesg β€” a copy of dmesg. Kerneloops coded. 🌈

✨ Index ✨

  • Index
  • Module Index
  • Search Page
ngl bestie, those three are just the standard navigation links from the docs build β€” click away, they're not deep lore πŸ’€

🌸 AUTHOR 🌸

Richard Marko

2012, Richard Marko


okay that's a wrap, bestie!! 🫢 this whole thing was served up as version 0.1, dated Aug 03, 2026, and it's the PYTHON3-ABRT(5) page. go forth and debug slayfully πŸ’…βœ¨

πŸ“¦ where this page comes from