Repository: fabfuel/circuitbreaker

Issue:
Non obvious circuit breaker "name" property behavior
If you don't pass a "name" argument you can end up with a TypeError:

```python
cb = CircuitBreaker()
str(cb)  #TypeError: __str__ returned non-string (type NoneType)
```

Of course you have no problem if you first decorate something:

```python
cb = CircuitBreaker()

@cb
def some_func(): ...

str(cb)  # no error
```

But is it really obvious that it ends up with the name of a function?

```
print(cb.name)  # some_func
```

And if you use the same circuit breaker to decorate something else, it's name won't change:

```python
@cb
def another_func(): ...

print(cb)  # still "some_func"
```

I suggest that if there is no name we should generate one on instance initialization instead:

```python
cb = CircuitBreaker()
str(cb)  # unnamed_CircuitBreaker_1
```

Discussion:
@yarjor: Aside from the TypeError, I think that this behavior might be the best approach. The name is used for registering with `CircuitBreakerMonitor`, and so should be something intuitive -
- for a Circuitbreaker instance that is used to decorate a single function (especially if it's instantiated in the decorator like the example in the README), it's easiest to name the circuitbreaker after the function. It might have been nicer for the repr to be something like `CircuitBreaker(func_name),` but it's not that important (and would break backward compatibility)
- for a CircuitBreaker instance that is used to decorate multiple functions (with the same instance, not class) - I'm not sure there's an intuitive automatic naming scheme and it should probably be named explicitly (for example, if it's used to decorate multiple functions using the same external service, name it after the service).

In any way, default names seem to be more confusing. WDYT?@yarjor: I have opened a small PR with a fix to the `TypeError` issue.
If another behavior is decided on, I'd like to work on it if that's okay :)

Solution patch:
diff --git a/circuitbreaker.py b/circuitbreaker.py
index ff15b48..2d317fb 100644
--- a/circuitbreaker.py
+++ b/circuitbreaker.py
@@ -281,7 +281,7 @@ def fallback_function(self):
         return self._fallback_function
 
     def __str__(self, *args, **kwargs):
-        return self._name
+        return self.name or 'unnamed_CircuitBreaker'
 
 
 class CircuitBreakerError(Exception):


Test patch:
diff --git a/tests/test_unit.py b/tests/test_unit.py
index 37a51e7..ac99871 100644
--- a/tests/test_unit.py
+++ b/tests/test_unit.py
@@ -34,6 +34,11 @@ def test_circuitbreaker__str__():
     assert str(cb) == 'Foobar'
 
 
+def test_circuitbreaker_unnamed__str__():
+    cb = CircuitBreaker()
+    assert str(cb) == 'unnamed_CircuitBreaker'
+
+
 def test_circuitbreaker_error__str__():
     cb = CircuitBreaker(name='Foobar')
     cb._last_failure = Exception()