Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Support for Adding .NET Attributes when Creating .NET Classes #1768

Open
rmadsen-ks opened this issue Apr 13, 2022 · 8 comments · May be fixed by #2032
Open

Add Support for Adding .NET Attributes when Creating .NET Classes #1768

rmadsen-ks opened this issue Apr 13, 2022 · 8 comments · May be fixed by #2032

Comments

@rmadsen-ks
Copy link
Contributor

rmadsen-ks commented Apr 13, 2022

Environment

  • Pythonnet version: 3.x
  • Python version: 3.x
  • Operating System: Any
  • .NET Runtime: .Net Framework 4 / .Net6

Details

I want to be able to specify attributes on my class defined in python. This is essential for my specific use case, but I imagine generally useful. I can come up with 3 different situations where it could be useful to add attributes:

  1. Attributes on types - This can be done with an annotation
  2. Attributes on methods - This can be done with an annotation
  3. Attributes on properties - It seems clr.clrproperty needs to be modified to support it.
### from test_clrmethod.py

# Attaching attributes to types (1)
@clr.attribute(DebuggerDisplayAttribute, "ExampleClrClass: X={X}")
class ExampleClrClass(System.Object):
    __namespace__ = "PyTest"
    def __init__(self):
        self._x = 3

    # Attaching attributes to methods (2)
    @clr.clrmethod(int, [int])
    @clr.attribute(DescriptionAttribute, "Test method")
    def test(self, x):
        return x*2

    def get_X(self):
        return self._x
    def set_X(self, value):
        self._x = value

   # Attaching attributes to properties (3)
    X = clr.clrproperty(int, get_X, set_X)\
        .attribute(DescriptionAttribute, "Gets or sets X")\
        .attribute(BrowsableAttribute, True)

    # Also attaching attributes to properties (3)
    @clr.clrproperty(int)
    @clr.attribute(DescriptionAttribute, "Gets Y")
    def Y(self):
        return self._x * 2

Regarding using clr.attribte(DebuggerDisplayAttribute, "...") vs clr.attribute(DebuggerDisplay("...")), it is exceedingly hard to infer how an attribute is constructed form the attribute value itself. Instead the list of constructors can be iterated and matched with a list of arguments and this way the best matching constructor can be found.

I can probably implement this myself, but before making a PR I want to know if it will be accepted or it is a bad idea.

@rmadsen-ks rmadsen-ks changed the title Add Support for .NET Attributes when Creating .NET Classes Add Support for Adding .NET Attributes when Creating .NET Classes Apr 13, 2022
@lostmsu
Copy link
Member

lostmsu commented Apr 13, 2022

Sounds good to me. But I believe at least your variant of syntax would be very tricky to implement for classes (methods are fine), because Python would first create class ExampleClrClass, and only then apply the attribute to it. But in .NET you can't create a class and then change it by adding attributes - you'd have to create a new class, which in your example would have the same name (which might not be a problem as long as you don't use reflection to find it).

BTW, the code in question is in CreateDerivedType function.

Finally, for the scenario you described, you could just override ToString function.

@rmadsen-ks
Copy link
Contributor Author

@lostmsu, you are right the '@'-attribute won't work for classes.

So maybe something like this then?

 __attributes__ = [(DebuggerDisplayAttribute, "ExampleClrClass: X={X}")]

Your final comment I assume is referring to DebuggerDisplayAttribute, but I just used that as an example.

@lostmsu
Copy link
Member

lostmsu commented Apr 19, 2022

@rmadsen-ks absolutely correct, and __attributes__ would work.

@filmor
Copy link
Member

filmor commented Apr 21, 2022

It could make sense to prefix all of the class members that we use with clr, like __clr_attributes__.

@filmor
Copy link
Member

filmor commented Apr 21, 2022

Also: @rmadsen-ks is there anything that is keeping us from just constructing the instances in Python already, like __clr_attributes__ = [DebuggerDisplayAttribute("ExampleClrClass: X={X}")]?

@rmadsen-ks
Copy link
Contributor Author

Also: @rmadsen-ks is there anything that is keeping us from just constructing the instances in Python already, like __clr_attributes__ = [DebuggerDisplayAttribute("ExampleClrClass: X={X}")]?

I also think that would be the most elegant solution, but in the dynamic type builder(ClassDerived.cs) we have to emit some IL code that calls the attribute constructor. So, from the attribute instance we would have to reverse engineer the arguments to insert into that constructor and I believe that is generally impossible.

@filmor
Copy link
Member

filmor commented Apr 21, 2022

Hmm, we could theoretically special-case System.Attribute derivatives, or automatically introduce facades without the Attribute suffix (so DebuggerDisplay("...") which would not be a real DebuggerDisplayAttribute instance but just contain a reference to the class and the arguments), though that could lead to quite a bit of confusion.

@rmadsen-ks
Copy link
Contributor Author

@filmor, I think this sounds like a great idea. So the list of tasks becomes:

  1. Attribute facades that generates instances of 'AttributeConstructor'. Named the same as the attribute but without 'Attribute'.
  2. Allow the use of __clr_attributes__ to assign attributes to generated types and members.

I have a good idea of how to do 2. For 1 I could use some hint of where to start looking.

Also for 1, for Attributes which are not named with the 'Attribute' suffix or if there is a naming conflict, we could fallback to support the use a tuple as in my example?

@rmadsen-ks rmadsen-ks linked a pull request Nov 30, 2022 that will close this issue
5 tasks
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 a pull request may close this issue.

3 participants