class attributes v instance attributes #1
Loading…
Reference in New Issue
No description provided.
Delete Branch "%!s(<nil>)"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Checking I understand the proposal...one of the standard python gotchas is the difference in behaviour between class attributes and instance attributes.
Looking at this code
it seems like the proposed new artiq python scraps the concept of class attributes (which is fine since we're nowhere near the original python data model anyway) and uses the normal class attribute syntax to define type annotations for instance attributes. Is that about correct? Side note: I actually don't know how the current artiq-python treats class attributes.
How does this play out for classes that have both kernel and non-kernel methods?
Yes, this is used for instance attributes.
I did not thought about this previously, maybe I would have a look tmr to see how this behaves in current artiq-python. (Side note: I'm not an experienced artiq-python user either...)
I personally prefer to only allow kernel functions for class methods (perhaps except constructor), as it does not make much sense to copy all attributes to the host everytime we use RPC, and making the transfer explicit via parameter passing to other RPCs seems not too bad for me.
Attributes in classes that have both kernel and host methods are a major footgun in artiq-python IME. Improvement here would be welcome so long as it doesn't become overly restrictive. I do think we need to be careful to gather use-cases/code examples from the current artiq-python before making some of these calls however.
Yes, but I'm not sure where to find existing use cases apart from the artiq repo.
Just need to make sure we bring the relevant stakeholders into the discussion (which it seems you are doing already...)
One challenge is that it can be nice to write a method as
@portable
and then be able to run it on either the host or the kernel. This makes it easy to debug code by running it on the host, and then you can just plop the debugged code into a kernel. While this hides potential pitfalls, it is also nice to be able to do code reuse in this way.Basically, the warning here is that most people who are used to Python classes and attributes will probably get tripped up by these sorts of restrictions. The point is not that we shouldn't have them, but that we need to figure out ways to make this work nicely for users, and not force them to make copy/paste host and kernel versions of methods if we can help it...
You could still use
@portable
, but outside kernel classes - see the example in #5 (comment)I've checked PEP526#class-and-instance-variable-annotations, it seems that the syntax is valid python type annotation for instance variable.
Is there any comment about the alternative mentioned above?
Thanks for the reference. I don't really understand type annotations in python so that was interesting reading. I agree that your approach does seem to be the correct pythonic one.
The syntax here still makes me a little uncomfortable, but I think that's just python language mess (class attributes always feel like a massive footgun to me and these annotations make the situation more complex if anything) rather than an issue with this proposal.
Another issue that I just thought of: We use
self.setattr_device
all over the place in the current artiq code. Should we require the users to specify these instance attributes?What if we set the attributes automatically when the users add the type annotations? For example:
Yes we can sometimes set attributes automatically. The default
build()
method could call method that attempts to populate the devices from the type annotations and the device database.It could be a simple match on the name of the device and the class described in the device database.
pca006132 referenced this issue2021-06-09 10:01:32 +08:00
I am not the biggest fan of "automatic population of attributes from the device db based on a type annotation". Like, should these devices be typed as
KernelImmutable[TTLOut]
or not, and when do you raise exceptions when there is a type mismatch or the key is not found? I find it a bit implicit, but if this is a desired feature and we can clarify how to type it and how the error-mechanisms work, I am fine with it.Besides, I am wondering, will
setattr_device()
andget_device()
stay? We mainly use the latter and in some cases the former. We find it very important that the key of the device does not have to be the same as the name of the attribute. And in case any of these two functions would be used, I assume they still need to be typed asKernelImmutable[TTLOut]
, so that could then conflict with automatically populating attributes from the device db.Sure, the current DDB functions will stay. And the automatic function is only in the default
build()
and does not get called when you overloadbuild()
, you'd have to call it yourself. It just makes simple experiments simpler.Our inheritance stuctures are often quite complicated and we use
super()
in most of these classes to ensure allbuild()
functions are called independent of the inheritance structure. Now, the last call tosuper()
will result in a call to the emptybuild()
function in ARTIQ, which is safe. With this change it would always call this new default function, which could then have conflicts. It would be great if there is some way to disable this default behavior (i.e. through a class variableAUTO_POPULATE_DEVICES=False
) or to make it explicit (i.e. users need to callself.auto_populate_devices()
to get this behavior.For typing of device attributes, I have the impression that they should be
KernelImmutable[TTLOut]
to make them accessible from host code, but not sure if this automatic system should deal with that in a more flexible way.I think they should be typed as
KernelImmutable[TTLOut]
, I forgot that they should be immutable kernel attributes.I think we could see if they are defined in the constructor. If they are, we silently ignore it (do not automatically populate it), otherwise provide a better error message (Use before definition, not defined automatically because there is no such key in the device database/with a different type).
I'm OK with this.
Calling
self.auto_populate_devices()
looks good to me.