4

Background

(Might be relevant because there might be a simpler way to achieve what I want.)

I want to build a declarative way to define "aspects" that can be analyzed by a static code analysis tool. The whole concept is written down here. Every aspect (say Redundancy) may have sub aspects recursively (say Redundancy.Clone) and every aspect shall have documentation and arbitrary other properties. The user shall be able to choose which aspects to analyze and I have to programmatically find out for the internal representation of an aspect if it's the one choosen by the user (i.e. for a given class Redundancy.Clone I want to validate that it belongs to a given string redundancy.clone but not redundancy.unused_import).

I decided to use classes like this:

class Redundancy(Aspect):
    """
    This meta aspect describes any kind of redundancy in your source code.
    """

    # Can't inherit from Redundancy here because of the recursion
    class Clone(Aspect):
        """
        This redundancy describes a code clone. Code clones are different pieces of
        code in your codebase that are very similar.
        """

        # Stuff...

Problem

For a given Aspect class I want to get the describing string (Redundancy.Clone -> redundancy.clone). For that I have to get the name of the surrounding module/class/whatever it is, check if it's a class (trivial) and construct a string out of it.

Possible Solutions and How They Failed

I did try looking at the dir of my classes to see if there's anything useful in the dunder methods I could use but found nothing except the repr which in the above case is <class 'coalib.bearlib.aspects.Redundancy.Clone'> when living in the aspects module. This shows that it should be possible but I have no idea on how repr gets this information and I would like to avoid using repr and stripping off the unneeded stuff as that's kind of a hack.

I am not able to inherit the nested class from the outer one as it is not completely defined yet. I want them nested for the usability, being able to from ... import Redundancy and in my source code write Redundancy.Clone is a huge plus.

Any suggestions, including changing my approach, would be appreciated.

3
  • 1
    Nested classes are very rarely useful in Python. Does Redundancy do anything itself? If not it should probably just be a module. Commented Oct 21, 2016 at 9:48
  • Having them a class inheriting from Aspect is nice because we can provide abilities for it that are tied to it's "being", e.g. it allows to define settings that we can do operations with. Commented Oct 21, 2016 at 9:50
  • (Ooops, enter sends already...) This still should work recursively, e.g. Redundancy.Clone.FullClone or whatnot. Requirements are: - Aspects itself should be dead easy to write - Should be intuitive to use (like Redundancy.Clone) - I need to be able to get the string representation as described (this may be complex but it's not done by the user) Commented Oct 21, 2016 at 9:52

2 Answers 2

5

You could use __qualname__ (PEP 3155)

>>> class C:
...   def f(): pass
...   class D:
...     def g(): pass
...
>>> C.__qualname__
'C'
>>> C.f.__qualname__
'C.f'
>>> C.D.__qualname__
'C.D'
>>> C.D.g.__qualname__
'C.D.g'
Sign up to request clarification or add additional context in comments.

1 Comment

You learn a new thing every day - thanks so much! This seems exactly what we need.
0

You can construct the class after class Redundancy statement finish its execution with the type class. For example:

class Aspect:
    pass

class Redundancy(Aspect):    

    @classmethod
    def init(cls):
       cls.make_Clone()

    @classmethod
    def make_Clone(cls):
        def __init__(self):
            print('inside Redundancy.Clone')

        methods = {} 
        methods["__init__"] =  __init__

        cls.Clone = type("{0}.Clone".format(cls.__name__), (Redundancy,), methods )

Redundancy.init()

print(Redundancy.Clone)
print(Redundancy.Clone())
# output:
#   <class '__main__.Redundancy.Clone'>
#   inside Redundancy.Clone
#   <__main__.Redundancy.Clone object at 0x01DCA130>

1 Comment

Indeed, thanks for the response! However this complicates our API by far as users should be able to easily define those things in a declarative way. Nested classes allow us - and thanks to the __qualname__ we can do it like this. (Plus we don't support python 3.3 and lower.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.