Repository: mkorpela/overrides

Issue:
Strange error if using a class decorator with a dynamic parameter in a function
I encountered a strange error that I managed to reproduce with the following example:

```
from typing import Callable, Type
from overrides import overrides


class SuperClass(object):
    def method(self) -> int:
        """This is the doc for a method and will be shown in subclass method too!"""
        return 2


def my_decorator(name: str) -> Callable:
    def func(cls: Type) -> Type:
        return cls

    return func


class MyClass:
    def __init__(self, name: str):
        self.my_name: str = name


def my_func() -> None:
    my_object = MyClass("Slartibartfast")

    @my_decorator(my_object.my_name)
    class SubClass(SuperClass):
        @overrides
        def method(self) -> int:
            return 1


my_func()
```

The resulting error message:

```
Traceback (most recent call last):
  File "repro.py", line 33, in <module>
    my_func()
  File "repro.py", line 27, in my_func
    class SubClass(SuperClass):
  File "repro.py", line 29, in SubClass
    def method(self) -> int:
  File "/home/username/.local/lib/python3.6/site-packages/overrides/overrides.py", line 59, in overrides
    for super_class in _get_base_classes(sys._getframe(2), method.__globals__):
  File "/home/username/.local/lib/python3.6/site-packages/overrides/overrides.py", line 76, in _get_base_classes
    class_name_components in _get_base_class_names(frame)]
  File "/home/username/.local/lib/python3.6/site-packages/overrides/overrides.py", line 76, in <listcomp>
    class_name_components in _get_base_class_names(frame)]
  File "/home/username/.local/lib/python3.6/site-packages/overrides/overrides.py", line 173, in _get_base_class
    obj = getattr(obj, component)
AttributeError: 'function' object has no attribute 'my_name'
```

Discussion:

Patch:
diff --git a/README.rst b/README.rst
index 58fde28..25cc75e 100644
--- a/README.rst
+++ b/README.rst
@@ -1,4 +1,4 @@
-overrides 
+overrides
 =========
 
 .. image:: https://api.travis-ci.org/mkorpela/overrides.svg
@@ -46,7 +46,7 @@ Usage
 
     from overrides import overrides
 
-    class SuperClass(object):
+    class SuperClass:
 
         def method(self):
             """This is the doc for a method and will be shown in subclass method too!"""
@@ -74,12 +74,13 @@ Enforcing usage
         def method(self):
             """This is the doc for a method and will be shown in subclass method too!"""
             return 2
-        
+
         def method2(self):
             """This is the doc for a method and will be shown in subclass method too!"""
             return 2
-        
-        def method3(self):
+
+        @staticmethod
+        def method3():
             """This is the doc for a method and will be shown in subclass method too!"""
             return 2
 
@@ -90,14 +91,14 @@ Enforcing usage
         def method(self): # <-- overriding a final method
             return 1
 
-    
+
     # THIS FAILS
     class SubClass2(SuperClass):
 
         def method2(self): # <-- @overrides decorator missing
             return 1
-            
-            
+
+
     # THIS ONE IS OK
     class SubClass3(SuperClass):
 
@@ -106,4 +107,10 @@ Enforcing usage
             return 1
 
 
+    # ENSURE THAT @classmethod AND @staticmethod ARE PLACED AT THE TOP
+    class SubClass4(SuperClass):
 
+        @staticmethod
+        @overrides
+        def method3():
+            return 1
diff --git a/overrides/overrides.py b/overrides/overrides.py
index 80ad368..48ea57b 100644
--- a/overrides/overrides.py
+++ b/overrides/overrides.py
@@ -142,5 +142,6 @@ def _get_base_class(components, namespace):
         else:
             obj = getattr(namespace["__builtins__"], components[0])
     for component in components[1:]:
-        obj = getattr(obj, component)
+        if hasattr(obj, component):
+            obj = getattr(obj, component)
     return obj


Test patch:
diff --git a/tests/test_decorator_params.py b/tests/test_decorator_params.py
new file mode 100644
index 0000000..0620394
--- /dev/null
+++ b/tests/test_decorator_params.py
@@ -0,0 +1,28 @@
+from typing import Callable, Type
+from overrides import overrides
+
+
+class SuperClass:
+    def method(self) -> int:
+        return 2
+
+
+def my_decorator(name: str) -> Callable:
+    def func(cls: Type) -> Type:
+        return cls
+    return func
+
+
+class MyClass:
+    def __init__(self, name: str):
+        self.my_name: str = name
+
+
+def test_my_func() -> None:
+    my_object = MyClass("Name accessed in decorator")
+
+    @my_decorator(my_object.my_name)
+    class SubClass(SuperClass):
+        @overrides
+        def method(self) -> int:
+            return 1