Skip to content

New notifier refactor#1544

Merged
gazpachoking merged 49 commits intonextfrom
new_notifier_api
Jan 8, 2017
Merged

New notifier refactor#1544
gazpachoking merged 49 commits intonextfrom
new_notifier_api

Conversation

@gazpachoking
Copy link
Copy Markdown
Member

@gazpachoking gazpachoking commented Dec 10, 2016

NOTE: Everything here is still a draft, mostly untested, and meant for discussion still. Getting mostly up to snuff at this point, though still open for other changes.

Motivation for changes:

Further separate the different types of notification generation plugins for simplification, and easier customization of each message type. Simplify the framework to not need advanced knowledge about the context of a message.

Detailed changes:

  • The notification framework only sends one message at a time now, and doesn't have knowledge of the 'context' of the message. (also makes a future log notifier easier, which isn't task or entry context)
  • plugins in the 'notifier' group now get params as (title, message, config) with all templates already rendered. title and message will be the message as constructed by one of the notification message generator plugins (notify_abort, notify_task, etc.), and config is the config for the transport plugin (email, prowl, etc.) as specified and validated by its schema (any text fields in the config will have jinja templates rendered against the appropriate context before being passed to transport plugin though)
  • to send a message, you send (title, message, outputs, [template_renderer]) where outputs is a list of configured notifier output plugins. template_renderer should be a function that renders jinja strings or templates against the appropriate context for the message. It will be applied to title, message, and any string keys in the output plugin configs.
  • Remove title and message params from each notifier plugin?
  • A function can be passed in which will be used to render all text fields in the notification config for convenience (so each notification generator doesn't have to do it themselves.)
  • The common base class was removed from the notification generators, and split into multiple separate files which now handle the different contexts of the notifications themselves.
  • Move notifications to a new notify phase. Add a way to suppress task aborts from this phase. (Is the way I did this okay? Is there something nicer?)
  • notify_task now only notifies when there are accepted or failed entries. Removed and tweaked templates so that we don't generate blank emails anymore.
  • Notifications are sent on --test runs now too.
  • Update the notification output plugins to take the title and message (add url as a default message field too?) separate than the output plugin config. (all of them now done, question about url as a default field remains open though)

Breaking changes

I've changed some names that I thought made sense, but we could backtrack if they don't seem worth it to break backwards compatibility in places.

  • notify_entries -> notify_entry ? I feel this has more parity with the singular notify_task, plus it sends a notification per entry like notify_task does per task
  • combined notify_entries and notify_task, and notify_abort back into notify plugin, with 'task', 'entries', and 'abort' subkeys.
  • the template folder 'entries' to 'entry', as that's the rendering context
  • file_template -> template I changed the option in notify_task and notify_entry to just 'template' to specify one of the templates from disk.
  • 'to' -> 'via' any of the plugins that send messages take the notifier configs under the 'via' key now.
  • Backwards compatibility with the notifiers allowed directly in task is gone.
  • The notifier transport plugins no longer allow title, message, or template (or file_template) in their schemas.
  • 'notify' plugin was used as a framework for any other plugins that wanted to send messages before. Put that functionality into 'notify_framework' plugin now.
  • sns is now a regular output plugin again. (I did move it to the new notify phase so that errors don't fail the task, but I'm not sure if that's good or not.)
  • notify_osd was renamed toast, and (beta) Windows support was added.

Things to finish before merging

  • Make the sns notifier a regular output plugin again.
  • Combine notify_task, _entry, _abort, again? I'm still not really certain about that one, I sorta like the split.
  • Notify_task and _entries are combined, should _abort go in there too?
  • The common framework used to send notifications is now named notification_framework so that the task plugin can be named notify. Not great, but it's only used from other plugins, not user facing.
  • Consider the case where there are no other output plugins in a task but a notifier. In this instance it may be desirable to let failures in the notification cause entry failures/task aborts, as there won't be any side effects.
  • Consider whether the notification framework should provide 'url' as a default field. There are several notifiers which all support 'url', but it wouldn't be 100%. 'subject' is already included though, and that isn't used 100% either, so url might be just as appropriate. Leaning to 'no' on this.


def notify(self, to, message, title, smtp_host, smtp_port, smtp_username=None, smtp_password=None, smtp_tls=None,
smtp_ssl=None, html=None, **kwargs):
def notify(self, title, message, config):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is how I initially did it, by passing config dict. @paranoidi advised me to pass it a literal params for clarity's sake. I can't say I disagree

Copy link
Copy Markdown
Member Author

@gazpachoking gazpachoking Dec 10, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But... it wasn't passing in literal params, it was passing in a config dict, with other stuff added to it, which was being keyword argument expanded. This way we are passing in our actual literal params, which are the ones generated by the notification system, title, message, and the config, as specified and validated by the schema of the plugin, like we do for our other plugin methods. The difference is that these parameters are all the discrete ones that the caller knows about, and doesn't mix in the ones the callee is defining in its own schema.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@liiight What you had was different, it was a single dict called "data" and contained message and configuration in same dict. This is what I proposed as first ideal solution but there were some challenges with that at the time. This is fine.


title = template_renderer(title)
except RenderError as e:
log.error('Error rendering notification title: %s', e)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you got rid of falling back to default in case of render error?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It falls back to the non rendered jinja string.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I liked having a fallback dict, but it's no biggie I guess

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I sorta feel like having the unrendered string will be a better indication of error when debugging why their config isn't working rather than getting some string that isn't to do with the title/message they defined.

'{{ title }}'
'{% endif %}'
},
'template': {'type': 'string'},
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used file_templates to differentiate this from template plugin

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I guess we do have a couple different kinds of templates, not sure if file_template really helps clarify things much or not though.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

notification template maybe? seems long and too verbose

return
# If a file template is defined, it overrides message
if config.get('template'):
body = get_template(config['template'], 'entry')
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could raise ValueError

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Need to catch that.

@gazpachoking
Copy link
Copy Markdown
Member Author

gazpachoking commented Dec 10, 2016

I was also wondering if 'via' might be more appropriate for the list of notifier outputs than 'to', e.g.:

notify_task:
  via:
  - email:
      to: blah@blah.com

Eliminates the double nested 'to' option in the email case. I think it's a bit more accurate description as well, i.e. the notification is being sent 'via' one of the transport plugins, and 'to' a recipient defined in that transport plugin config. (BTW, maybe need some more consistent terminology to discuss these plugins. What do we call the message generators, (notify_task, notify_abort, etc.,) vs the transport ones, (email, pushbullet, etc.?) similar to the problem of 'list plugins' (list_match, list_add, etc.) vs 'list_plugins' (imdb_list, trakt_list, etc.))

@liiight
Copy link
Copy Markdown
Member

liiight commented Dec 11, 2016

I like using via but I'm not sure if this warrants ANOTHER upgrade actions in spite all the noise this change already made...
About the semantics, I suggest we lock down the manged list semantics first 😛

@gazpachoking
Copy link
Copy Markdown
Member Author

gazpachoking commented Dec 11, 2016

About the semantics, I suggest we lock down the manged list semantics first

I think the terminology should be the same, and I don't mean necessarily for user facing docs. We have a pattern of defining an api the plugins in a certain group should implement. We then have plugins in that group, and plugins that use plugins in that group. This applies both to the list plugins, and the notification plugins. I just want to figure out what the hell to call things when discussing. 😛

@liiight
Copy link
Copy Markdown
Member

liiight commented Jan 2, 2017

I'd say making notify_abort work like the other two makes sense, no need to have two schema paradigms.
About having no output plugins in the task, I don't know how common of an issue that is, and if it's worth supporting as a real life scenario. maybe handle that problem if and when it arises?

@andocromn
Copy link
Copy Markdown
Contributor

@liiight I wanted to comment on the "no other output plugins" consideration since I was the one to raise this concern. Although I agree its probably not common, the email plugin used to be just a normal output so there was nothing wrong with this configuration. I raised the concern to address the changes in the behavior resulting from introduction of the notify phase and discuss how to best maintain the previous functionality while still implementing the improvements. As a result of the changes associated with the notify_phase, a task that only has a notification output will no longer retry sending the notification if the notification fails. I think that if the notification is the only output, and the notification fails, it should consider that entry to be failed

As an alterantive, I'm not sure if this was considered, but the best way of handling may be to create a notification queue. This way, regardless of the output, any failed notifications could be retried on the next run of the task and would be sent to the user without the program re-downloading

@liiight
Copy link
Copy Markdown
Member

liiight commented Jan 5, 2017

@gazpachoking notifyosd is a crappy name but it's not original name https://wiki.ubuntu.com/NotifyOSD

@gazpachoking
Copy link
Copy Markdown
Member Author

@gazpachoking notifyosd is a crappy name but it's not original name https://wiki.ubuntu.com/NotifyOSD

Oh hmm. It works with this too, right? https://wiki.archlinux.org/index.php/Desktop_notifications 'notify osd' seems to be just one implementation for unity.

@gazpachoking
Copy link
Copy Markdown
Member Author

gazpachoking commented Jan 5, 2017

Speaking of which, should we change it to this https://pypi.python.org/pypi/notify2 , instead of requiring gtk as the dependency?

@liiight
Copy link
Copy Markdown
Member

liiight commented Jan 5, 2017

Yeah, looks better

@liiight
Copy link
Copy Markdown
Member

liiight commented Jan 5, 2017

Sweet, windows osd. Works on 7 and up I suppose?

@gazpachoking
Copy link
Copy Markdown
Member Author

Sweet, windows osd. Works on 7 and up I suppose

Works on 10 at least, (though not optimal integration with the new action center,) not sure how far back it goes, I assume it works fine on 7.

@gazpachoking
Copy link
Copy Markdown
Member Author

What should we call the osd notifier now that it's a bit more generic? Would 'popup' or 'desktop' or something be more appropriate?

@liiight
Copy link
Copy Markdown
Member

liiight commented Jan 5, 2017

I think on_screen_display is generic enough Maybe toast?. Also, I think it's schema should also be a Boolean, so you could just do on_screen_display: yes

@liiight
Copy link
Copy Markdown
Member

liiight commented Jan 6, 2017

Hmm,got this crash while testing it:

4 VERBOSE  task          ser             ACCEPTED: `series.s01e01.720p.hdtv-flexget` by accept_all plugin                           2017-01-06 01:14 VERBOSE  details       ser             Summary - Accepted: 1 (Rejected: 0 Undecided: 0 Failed: 0)                                 2017-01-06 01:14 WARNING  task          ser             Task doesn't have any output plugins, you should add (at least) one!                       2017-01-06 01:14 CRITICAL task          ser             BUG: Unhandled error in plugin notify: Missing parentheses in call to 'print' (__init__.py, line 39)                                        Traceback (most recent call last):
  File "/data/data/com.termux/files/home/Flexget/flexget/task.py", line 490, in __run_plugin          return method(*args, **kwargs)
  File "/data/data/com.termux/files/home/Flexget/flexget/event.py", line 23, in __call__              return self.func(*args, **kwargs)              File "/data/data/com.termux/files/home/Flexget/flexget/plugins/notifiers/notify.py", line 129, in on_task_notify                                     send_notification(config['task']['title'], template, config['task']['via'], template_renderer=task.render)                                       File "/data/data/com.termux/files/home/Flexget/flexget/plugins/notifiers/notification_framework.py", line 120, in send_notification                  notifier.notify(title, message, rendered_config)  # TODO: Update notifiers for new api          File "/data/data/com.termux/files/home/Flexget/flexget/plugins/notifiers/on_screen_display.py", line 28, in linux_notify                             from gi.repository import Notify               File "/data/data/com.termux/files/usr/lib/python3.5/site-packages/gi/__init__.py", line 39          print url                                                ^                                    SyntaxError: Missing parentheses in call to 'print'                      

@liiight
Copy link
Copy Markdown
Member

liiight commented Jan 6, 2017

I'm not so sure anymore about the via keyword now that we have the task and entries levels:

notify:                                                                                            
  task:                                                                                    
    via:                                                                                     
       - on_screen_display: {}

This feels too much to me...

@gazpachoking
Copy link
Copy Markdown
Member Author

Hmm,got this crash while testing it:

Very tough to read that traceback. Looks like maybe the gobject introspection doesn't support python 3? I'm thinking of reverting the changes to osd for now and dealing with that in a separate PR. Maybe include the rename, and allow for bool schema, that way we could upgrade it outside of next branch.

@gazpachoking
Copy link
Copy Markdown
Member Author

I'm not so sure anymore about the via keyword now that we have the task and entries levels:

Dunno what there is to be done about that. We could move the via to directly under notify, but that would mean you couldn't configure e.g. aborts to notify via a different method than entries.

Allow boolean configuration for toast.
Update toast plugin registration to use interfaces

# Conflicts:
#	flexget/plugins/notifiers/notify.py
#	flexget/plugins/notifiers/notify_osd.py
#	flexget/plugins/notifiers/sns.py
@gazpachoking gazpachoking changed the title [WIP] New notifier refactor New notifier refactor Jan 8, 2017
@gazpachoking gazpachoking merged commit 846fd05 into next Jan 8, 2017
@liiight liiight mentioned this pull request Jan 8, 2017
@gazpachoking gazpachoking deleted the new_notifier_api branch January 16, 2017 08:06
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 this pull request may close these issues.

5 participants