2016-05-18 18 views
3

Wie kann ich den parallelen Prozess unabhängig von der Hauptsache mit dem Multiprocessing-lib oder anderen Weg laufen lassen? (Der Beispielcode ist jetzt mit Gabel)Wie kann ich eine Funktion parallel ausführen und nachdem das Hauptprogramm beendet wurde, läuft er weiter?

Lange Geschichte

Nun, ich war ein kleines Skript für Back-End meiner conky Konfigurationen zu schreiben und eine davon ist ein rss_parser.py, das eine Reihe von Titeln erhalten/URLs und stdout anziehen. Kürzlich habe ich mich über etwas gefragt, um neue Benachrichtigungen unter Verwendung Notify von GTK zu aktivieren, wenn etwas Neues auf der RSS-Liste erscheint.

Aber ich habe ein Problem hier. Ich habe eine Aktion zum Öffnen des Links eingerichtet. Wenn meine Benachrichtigung aktiv ist, kann ich den Browser anklicken und öffnen. Dieser Teil des Codes muss warten, bis der Benutzer klickt oder die Benachrichtigungen schließen, für die eine ordnungsgemäße Ereignisschleife ausgeführt wird. Dies steht jedoch in Konflikt mit anderen Sache: , um meine Conky zu aktualisieren Ich brauche zu meinem Hauptskript zu beenden, ohne die Benachrichtigungen Teil zu erwarten! Also meine Idee ist die Benachrichtigungen parallel geschaltet.

Der Kodex from Hell

habe ich versucht, dies mit diesem vollständigen Code code: (überprüfe die parallel_notify Funktion)

#!/usr/bin/env python 
# coding=utf-8 
# 
# Python Script 
# 
# Copyleft © Manoel Vilela 
# 
# 

import feedparser 
from argparse import ArgumentParser 
from string import ascii_letters as alphabet 
from os import fork 
import json 
import sys 
import webbrowser 

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


class RssNotifier(GObject.Object): 
    Notify.init("rss_conky") 
    notifications = [] 

    def __init__(self, label): 
     self.label = label 
     self.loop = GObject.MainLoop() 
     super(RssNotifier, self).__init__() 
     GObject.timeout_add(100, self.exit_when_empty) 
     # lets initialise with the application name 

    def send_notification(self, title, text, url, file_path_to_icon=""): 

     n = Notify.Notification.new(title, text, file_path_to_icon) 
     # print('put notification') 
     self.notifications.append(n) 
     n.add_action(url, 'open', self.open_webbrowser) 
     n.connect('closed', self.close_notification, n) 
     n.show() 

    def send_rss(self, rss, url): 
     self.send_notification(self.label, rss, url, 'rss') 

    def open_webbrowser(self, n, url): 
     # print(':: webbrowse opening') 
     webbrowser.open(url) 

    def close_notification(self, n, arg): 
     self.notifications.remove(n) 
     # print(':: remove notification') 
     # print(':: notifications: ', self.notifications) 

    def exit_when_empty(self): 
     # print('exit check') 
     if not any(RssNotifier.notifications): 
      self.loop.quit() 
      return False 
     return True 


CACHE_FILE = '.cache.json' 

parser = ArgumentParser() 
parser.add_argument(
    '-u', '--url', 
    default="http://hackernews.demos.monkeylearn.com/feed.xml?", 
    dest='url', 
    type=str, 
    help='The url to be parsed' 
) 
parser.add_argument(
    '-l', '--lines', 
    default=10, 
    dest='lines', 
    type=int 
) 

parser.add_argument(
    '-w', '--width', 
    default=80, 
    dest='width', 
    type=int, 
    help='The horizontal limit' 
) 
parser.add_argument(
    '-p', '--prefix', 
    default='- ', 
    dest='prefix', 
    type=str, 
    help='A prefix attached each feed' 

) 

parser.add_argument(
    '-i', '--ignore', 
    default='', 
    dest='ignore', 
    type=str, 
    help='Useless string to remove' 
) 

parser.add_argument(
    '-n', '--disable-notifications', 
    default=True, 
    dest='notifications', 
    action='store_false', 
    help='Disable notifications (default True)' 
) 

parser.add_argument(
    '-r', '--rss-label', 
    default='RSS', 
    dest='rss_label', 
    type=str, 
    help='A simple label for what is fetching' 

) 


def get_label(entry): 
    if entry.get('tags'): 
     label = '{}: '.format(entry.get('tags').pop()['term']) 
    else: 
     label = '' 
    return label 


def long_title_clean(title): 
    if len(title) > options.width: 
     return (title[:options.width] + '\n' + 
       ' ' * (len(options.prefix)) + 
       long_title_clean(title[options.width:].strip())) 
    return title 


def translate_name(url): 
    return '.' + ''.join([x for x in url if x in alphabet]) + '.cache' 


def save_cache(new_cache, key): 
    cache_file = get_cache_file() 
    cache_file[key] = new_cache 
    with open(CACHE_FILE, 'w') as f: 
     json.dump(cache_file, f) 


def get_cache(key): 
    return get_cache_file()[key] 


def get_cache_text(key): 
    return '\n'.join((x for x, _ in get_cache(key))) 


def get_cache_file(): 
    try: 
     with open(CACHE_FILE, 'r') as f: 
      return json.load(f) 
    except: 
     return {} 


def notify(new_rss): 
    notifier = RssNotifier(options.rss_label) 
    for rss, url in new_rss: 
     notifier.send_rss(rss, url) 
    notifier.loop.run() 


def ignore_pattern(title): 
    return title.replace(options.ignore, '') 


def parallel_notifications(new_rss): 
    if any(new_rss) and options.notifications: 
     if fork() == 0: 
      notify(new_rss) 


def parse_print_rss(feed): 
    new_cache = [] 
    for entry in feed['entries']: 
     if len(new_cache) >= options.lines: 
      break 
     label = get_label(entry) 
     output = '{}{}{!s}'.format(options.prefix, label, entry.title) 
     title = long_title_clean(ignore_pattern(output)) 
     if title not in new_cache: 
      new_cache.append([title, entry['link']]) 
      print(title) 

    return new_cache 


if __name__ == '__main__': 
    loop = GObject.MainLoop() 
    options = parser.parse_args() 
    feed = feedparser.parse(options.url) 
    keyname = translate_name(options.url) 
    if not any(feed['entries']): 
     cache = get_cache_text(keyname) 
     print(cache) 
     sys.exit(0) 

    new_cache = parse_print_rss(feed) 
    old_cache = get_cache(keyname) 
    new_rss = [x for x in new_cache if x not in old_cache] 
    new_rss = new_cache # force use the new_cache 
    # the paralell part going here 
    parallel_notifications(new_rss) 
    save_cache(new_cache, keyname) 

Der Code für faule Personen

import os 
if os.fork() == 0: 
    os.setsid() 
    while True: 
     pass 
print('shorter') 

ps .: auf Conky, nie 'kürzer' wird gesendet, denn das passiert nie, er wartet auf das Kind! (Oder so ähnlich)

Erlittene Versuch

Beim ersten Versuch, benutzen ich multprocessing lib Setup einen neuen Prozess als Daemon (zum Hauptprogramm nicht erwarten Finish), aber dies nicht funktioniert. Übrigens, das schafft jetzt ein anderes Problem: wenn das Hauptprogramm fertig ist, das Process parallel Finish auch und jetzt habe ich nicht mehr Benachrichtigungen (oder einfach die Routine für den Klick funktioniert nicht, weil der Prozess bereits beendet ist) !! ! D:

EDIT-1

fork Mit funktioniert gut, wenn ich versuche, im Terminal läuft! Aber ich habe ein echtes Problem in der Conky läuft! Warum das? In sublime habe ich außerdem das gleiche Verhalten: der Elternprozess wird nur beendet, wenn das Kind verlässt.

+0

Verwenden Sie diese Bibliothek: https://pypi.python.org/pypi/python-daemon/ –

+0

Sie können mir ein Beispiel für diese Verwendung für mein Problem geben? Vielen Dank –

Antwort

0

Kurze Antwort: daemonize den Faden

Das Snippet ich in anderen Thread ausgeführt verwendet, ich dies nur setzen (wie Valentin Lorentz vorschlagen)

from daemon import DaemonContext 
with DaemonContext(): 
     paralell_notifications(new_rss) 

Und immer gut funktioniert, jedoch nur im Terminal und erhaben. Mein Hauptproblem, dessen ist die Conky, habe ich immer noch das gleiche Problem. Erscheint ein Fehler auf der Conky sein.

Warum das?Becase als er said:

ein Daemon ist ein Hintergrund, nicht interaktives Programm. Es ist frei von von der Tastatur und dem Display eines interaktiven Benutzers. Der Wortdämon zur Bezeichnung eines Hintergrundprogramms stammt aus der Unix-Kultur; es ist nicht universal.