Py3status: class Meta - a cleaner future?

Created on 6 Apr 2016  ·  12Comments  ·  Source: ultrabug/py3status

I was having a think the last few days about py3status modules.

currently a module has update methods like this

 def update(self, i3s_output_list, i3s_config):

but I've never seen any that use output_list, and i3s_config only seems to be used to get color states.

 def update(self):

would be much nicer, but has two problems:

1) All the modules would need updating or would break including users custom modules

2) we now can't do color_degraded etc

But we have the current opportunity to solve both these issues and move to a cleaner calling pattern.
We can do this by using the class Meta: Currently employed by the group module. This is what I would like to do.

If a Py3status module contains class Meta: it is taken to signify that it works with the new 'standard'. As such it would have update calls of just update(self) without the parameters. The on_click would likewise be on_click(self, event): So we have the simplified calling but only for self-declaring modules. All existing modules would continue to function as before.

But what about the i3s_config that might be useful to a module? Well, firstly the module can still access that config.

    class Meta:
        include_py3_module = True

gives access the modules Module and we could easily add a helper_get_i3_config() method. So that would solve that issue, although I'm not sure that the config is that useful. Apart from the color_bad type stuff.

With the colors many modules do

 response['color'] = i3s_config['color_bad']

some allow custom degraded colors to be defined in their config (which is nicer)

response['color'] = self.color_bad or i3s_config['color_bad']

But I want to go further, what if my module just did

 response['color'] = 'bad'

and we then automagically change this to a config specific color_bad if it is supplied, else the general color_bad. If an actual color is returned then we honor eg

 response['color'] = '#FF00FF'

I'd also like to change the cache_until so that setting it to -1 would mean cache forever (well until an update is forced eg middle click), but again just for new style modules.

So to convert a module you would update the method arguments etc and then just add

class Meta:
    pass

Once we have this in place we can make more changes easily in the future. eg.

class Meta:
    use_groovy_new_events = True

So this is my suggestion. I'm interested to know what people think of it. I could easily knock up a patch to demonstrate this but I'll wait until either there is some interest in this idea or it has been shot down in flames.

enhancement

Most helpful comment

Another option that would achieve similar results would be.

Just accept that modules may use update(i3s_output_list, i3s_config) or update() and work that out for them using inspect so the user wouldn't have to care.

In many ways that would be nicer.

class Meta: would still have a use for some special situations (none that actually exist now) so it could be removed and then make it's reappearance when needed. I have some other 'container' modules that I've been working on that require this.

self.py3 could be injected into all modules. It is unlikely to clash with existing vars but we could check first that the module does not have that attribute defined.

My fish.py example module would then get simplified to

# -*- coding: utf-8 -*-

class Py3status:

    def fish(self):
        return {
            'full_text': '>(((º>',
            'cached_until': self.py3.CACHE_FOREVER
        }

    def on_click(self, event):
        self.py3.notify_user('くコ:彡')

Thinking about this. I like this more than the original suggestion.

So there would be a small degree of magic (self.py3) but everything else would be simple and clean.

@ultrabug would that make you more relaxed?

All 12 comments

response['color'] = 'bad'

I really like your idea, but I am not in favor of encoding logic in a string. I think, such a magic conversion is difficult to read and it becomes harder to detect errors.

On another note, it would maybe make sense to add the possibility for a module to signal that it was updated. Think of an async module, which gets events and could tell py3status, that it just got a new value and its response should be printed now. What do you think?

On another note, it would maybe make sense to add the possibility for a module to signal that it was updated. Think of an async module, which gets events and could tell py3status, that it just got a new value and its response should be printed now. What do you think?

this is already possible with the recent module changes (via class Meta) An example is here, as it was something I was playing with.
https://github.com/tobes/py3status/blob/window_title_async/py3status/modules/window_title_async.py

Ah, was not aware of this. Very nice!

response['color'] = 'bad'

I really like your idea, but I am not in favor of encoding logic in a string. I think, such a magic conversion is difficult to read and it becomes harder to detect errors.

True,

would something like

response['color'] = self.py3_COLOR_BAD

make you any happier as a way to do this. My view is that I'd rather our modules are stand alone (ie don't do from py3status import ...) but using class Meta: we can inject things into the module. I'd also have one of the 'rules' being that no methods/vars in the Py3status modules could start with py3_ as they would be reserved for such usages.

Ah, was not aware of this. Very nice!

Yeah, documentation is needed but I'd love to implement some of the ideas here before people start relying on this :)

response['color'] = self.py3_COLOR_BAD

Abstracting away the distinction between the global and local color configuration in a property is a good idea. Maybe makes sense to inject members into its own namespace like self.py3.COLOR_BAD instead of the py3_ prefix?

its own namespace like self.py3.COLOR_BAD

This I like.

Currently Module has some helper functions like helper_notify_user() that I don't really like (a) the name of (b) where they live - from the point of view of the user.

This would help here eg self.py3.notify_user()

I'm having a hard time keeping up with your pace guys :)

I think the overall idea is very good, but I'm wondering why could we not have this Meta class available by default on new modules instead of having to declare it or why not use a class inheritance ?

I mean, something I'm really proud of is that quite a lot of modules are being contributed by users who never contributed on github before. This makes me believe that the current modules are easy and accessible to everyone.

So I'm cautious when introducing magic or _meta_ stuff at this level. (don't read me wrong, I'm in favour of doing it, but I want to stress how understandable it must be)

I'm wondering why could we not have this Meta class available by default on new modules instead of having to declare it or why not use a class inheritance ?

From my point of view I like the fact that py3modules are just self contained. They don't import any py3status files they just declare themselves.

I agree that this is a bit magic especially that self.py3 appears from nowhere, but it does allow some really neat stuff like the helper functions etc.

the class Meta is just a trick to allow the calls etc to change whilst keeping the existing logic, naming etc.

Another option would be something like using a new class name for the new style classes, but then that would also in my mind seem confusing. Using class inheritance means we would need to do an import from py3status in each module and I think is as mystifying to new users.

I think you have a very good point about keeping things simple so people can contribute, and it is nice to see how active things are with contributions of modules etc

Part of why I like the class Meta approach is that it also allows a simple 2 way flow between the modules and py3status without them being too intertwined. By this I mean that we could do something like this

class Meta:
    is_container = True

as a way for a module to notify py3status that is is a container and can hold other modules (like group does now) part of what I want to do is make it such that group is no longer a special case (it has it's own rules in the config parsing).

I think it's good to take time and consider the options and the effects they have on users new and old.

On the understandably front, I think the biggest thing that can be done is the documentation side of things and having clear examples for the users.

Also I suppose there are 3 thing here

  • New functionality via self.py3 theoretically we could just inject it into all modules now
  • New call signatures
  • Ability to pass information about a module to Py3status before the module actually runs.

The current proposal here 'cheats' in some ways as it forces you to either have all or none. It also keeps backwards compatibility.

Another option that would achieve similar results would be.

Just accept that modules may use update(i3s_output_list, i3s_config) or update() and work that out for them using inspect so the user wouldn't have to care.

In many ways that would be nicer.

class Meta: would still have a use for some special situations (none that actually exist now) so it could be removed and then make it's reappearance when needed. I have some other 'container' modules that I've been working on that require this.

self.py3 could be injected into all modules. It is unlikely to clash with existing vars but we could check first that the module does not have that attribute defined.

My fish.py example module would then get simplified to

# -*- coding: utf-8 -*-

class Py3status:

    def fish(self):
        return {
            'full_text': '>(((º>',
            'cached_until': self.py3.CACHE_FOREVER
        }

    def on_click(self, event):
        self.py3.notify_user('くコ:彡')

Thinking about this. I like this more than the original suggestion.

So there would be a small degree of magic (self.py3) but everything else would be simple and clean.

@ultrabug would that make you more relaxed?

Oh yes, I'm perfectly relaxed :+1: thanks

Was this page helpful?
0 / 5 - 0 ratings