Info_timer return value in AppDaemon

What does info_timer return if the handle doesn’t exist anymore???

It will raise a ValueError exception.

There isn’t an easy way to make that one return a valid error value considering the multiple return values is it. I hate trapping errors as part of normally functional code.

An if/then vs a try/catch ? Little difference really.

I don’t feel particularly strongly either way,

try/except is a normal, and encouraged, part of flow control in Python.

EAFP
Easier to ask for forgiveness than permission. This common Python coding style assumes the existence of valid keys or attributes and catches exceptions if the assumption proves false. This clean and fast style is characterized by the presence of many try and except statements. The technique contrasts with the LBYL style common to many other languages such as C.

Maybe I’m old fashioned but to me, it just promotes sloppy coding. It just pushes off the evaluation of whether something is really an error or just a condition you didn’t code for that can be cleaned up and resumed to the error handler. It’s one step removed from a goto statement.

Languages should deal with each normal state in code through straight forward flow control (if-then-else/elseif or case/switch statements). Error handling is just that, for errors that should not have happened. Files system errors, lost communication those kinds of things represent errors.

Yes you hit a nerve. :slight_smile: LOL

I see your reasoning and line of though and saying that EAFP is perfectly normal, and oftentimes encouraged & expected behavior from a proficient python developer. It’s a paradigm in Python that is unlike other c-based languages.

I’m not saying EAFP or LBYL is more correct over the other. It’s just different. :slight_smile:

Understood. And sorry if I came off overly testy in my response.

It’s a good discussion, and to be honest, I’ve been a little inconsistent in my implementation sometimes returning values like None and other times raising an exception, I should really take a more detailed look!

I also note that with a name like @turboc, we can probably make some assumptions as toi your previous programming experience :wink:

I actually learned Pascal as my first official CS language (although I had done a lot of BASIC and Assembler before then) but jumped to C as soon as I could get away with it. Still working out the subtleties of these new fangled scripting languages with their fancy high level data structures :slight_smile:

I started with basic in high school on a dec PDP-11. All together I’ve been in IT in one form or another for around 37 years (dam, that makes me feel old). Learned fortran on punch cards in college, then went to work for the university as a programmer, wandering between assembly, C, fortran and even a little cobol (very little). I spent time writing NLM’s (Network Loadable Modules) for the old Novell servers, wrote warehouse management systems in C, and a heavy amount of Oracle PL/SQL. I’ve learned several of the scripting languages like Moca and Abap. I’ve done my share of web development in ASP and Java. I have a degree in Electrical Engineering and a Masters in Computer Information Systems, but with all of that my passion is programming.

Through all of that the changes we have seen, the ones that I felt were the best advances we have made from a programming language perspective, is structure (both flow, and data), and Objects. In Basic and Cobol, goto’s were a nightmare. Pascal and C gave us the ability to structure our code so that it was easier to follow and maintain. Yes, types were a nightmare so were linked lists. The new structures we have today, have brought us a long way from those.

Python is more like the wild west, variable types are fluid and easily converted (which is a good thing), argument passing is what it is, any problems, and just throw self. in front of it and problem solved. I just think that if we are going to have something that looks up a value, it should either just look up the value and return it or return a dictionary or list with the value(s) in it, or have an additional function that tests to see if the subject of the query exists first so you can avoid the call if it’s going to fail.

The nice thing about returning a dictionary from functions like info_timer is that it makes the return values easy to extend if you want to bring by additional information in the future without having to re-code downstream apps immediately. They only have to re-code if they want to consume that data. It also allows you to return “None” if the timer doesn’t exist. When functions return multiple values, do you have to account for all of them to store return values when they are called? If so, then adding an additional return value to a function like that would require immediate recoding of any apps that use that function.

This is really a conversation better had over a beer and some burgers. It’s conceptual and very dependent on the style of the developer. For me, I’ll just take info_timer, override it and deal with the error in the override function. That way you don’t have to change anything which would impact anyone else using the app, and my code stays clean the way I like it. :slight_smile:

to bad i missed this discussion when it was going on, but i am with @turboc as it comes to avoiding try/except.

guess turbo is becoming the 4th musketier. :wink:

i started with programming about the same time as turbo, around 34 years ago.
my path was a little like @aimc in the first few years, from basic to pascal.

but i never got to C

i stepped over to programming in visual basic as soon as that was possible.

so andrew, if you are going to make it consistent then prefer that you change the part where exceptions are raised then that you change the parts where none is returned.

i rather have code like this:

if a==c:
  do something
else:
  log something

then this:

try:
  if a==c:
    do something
except:
  log something

Cool,
Dartanian got Raquel Welch. :slight_smile:

1 Like

i rather have code like this:

if a==c:
  do something
else:
  log something

then this:

try:
  if a==c:
    do something
except:
  log something

To be nitpicky here, that’s not exactly how you’d want to use try/except. :slight_smile:

Take for example the code I have in one of my apps…

class SomeApp(appapi.Appdeamon):
    def initialize(self):
        # a way to keep track of individual users' timer-callbacks
        self.user_timer_library = {}

    def person_ishome(self, event_name, data, kwargs):
        mac = data['mac']

        try:
            # cancel any previous timers, effectively resetting the TTL clock
            self.cancel_timer(self.user_timer_library[mac])
        except KeyError:
            self.log('No timers currently set for {}'.format(mac))
        except Exception as e:
            self.log('Something went wrong...\n{}'.format(e))

        # set a timer on the user, running the event trigger for person_isout if it expires
        # add it to the users' timer callback library
        self.user_timer_library[mac] = self.run_in(self.trigger_person_isout, 
                                                   seconds=int(self.args['ttl'])*60, 
                                                   mac=mac)

The function person_ishome() is meant to be able to be called multiple times per user (mac). It is entirely possible it will be called multiple times before the callback in run_in() is able to fire. You’ll see in the middle, a try/except block that will cancel a timer if one exists for the user (mac). You could certainly rewrite the few lines to …

        if mac in self.user_timer_library:
            # cancel any previous timers, effectively resetting the TTL clock
            self.cancel_timer(self.user_timer_library[mac])
        else:
            self.log('No timers currently set for {}'.format(mac))

It’s really just a matter of preference. :slight_smile:

yeah youre right, i dont use the key error. most of the time.
in most cases i dont care why something went wrong, i just want to know THAT something went wrong.
and a keyerror is just what it says, an error.
and i dont like to have errors at all. and if there is a return zero (or any value at all), there would not be an (key)error :wink:

i hope this app you showed is a general app and you call the code from other apps?

and wont the run_in at the end Always be called?

I do find that

        try:
            self.trv_id = self.args["trv_id"]
            self.sensor_temp = self.args["sensor_temp"]
            self.slider_set_temp = self.args["slider_set_temp"]
            self.select_state = self.args["select_state"]
            self.away_temp = self.args["away_temp"]
            self.power_mode_switch = self.args["power_mode_switch"]
        except KeyError as e:
            self.log("Argument not found : {}".format(e), level="ERROR")
            return

Is a much simpler way of detecting configuration errors than checking individually.

but what if you want the different args to be optional and you want to take different actions if the args arnt there?
then you get:

try:
  self.trv_id = self.args["trv_id"]
except Keyerror as e:
  log that
if self.trv_id == somevalue:
  do something

for all your keys instead of

self.trv_id = self.args["trv_id"]
if self.trv_id == somevalue:
  do something

i wouldnt mind the try/except that much if it would bring me the same as the if then.
but i always seem to need both to examine 1 value if a keyerror can be raised.

In the same initialization I have

        try:
            self.overshoot = float(self.args["overshoot"])
        except KeyError:
            self.overshoot = 1.3
        except ValueError:
            self.log("Argument overshoot must be a float.  Using default 1.3",
                    level = "WARNING")
            self.overshoot = 1.3

Which has not greater merits than checking, other than it is the same style as the previous code.

without errors that would be:

self.overshoot = 1.3
if isfloat(self.args["overshoot"]):
  self.overshoot = float(self.args["overshoot"])
else:
  self.log("Argument overshoot must be a float.  Using default 1.3",
                    level = "WARNING")

its still a matter of preference, where i prefer not to use errors as a part of my code. :wink:

Exceptions are not necessarily errors.

Your code also needs an extra check to see if self.args["overshoot"] exists before you use it.

I completely agree.

not if it return Null if it isnt there :wink:

you say exceptions arent necessarily errors but you use:
exept keyERROR or except valueERROR

the whole principle is
try something and if it doesnt work(except) try something else.

“in the old days” that was considered bad programming.