Repository: DataDog/dd-trace-py

Issue:
IndexError in _wrap_get_create
### Which version of dd-trace-py are you using? 0.50.3

### Which version of pip are you using? 20.3.3

### Which version of the libraries are you using? `dogpile.cache==0.7.1`

### How can we reproduce your problem?

Install redis if needed (on macOS):
```
brew install redis
brew services start redis
```

Create  a dogpile region cache:
```
from dogpile.cache import make_region
region = make_region("foo",
            key_mangler=lambda key: '{}:{}:{}'.format("foo", "env", key)) \
            .configure('dogpile.cache.redis', arguments=dict(url="redis://localhost:6379")
```

Call `get_or_create` using only kwargs:

```
        return token_cache.get_or_create(
            key="cache_key", creator=lambda: make_value(foo, bar, buzz))

```

Invoke your script via ddtrace-run:

```
ddtrace-run python3 test_ddtrace_and_dogpile.py
```

### What is the result that you get?

```
[ERROR] IndexError: tuple index out of range
Traceback (most recent call last):
  File "/var/lang/lib/python3.8/site-packages/datadog_lambda/wrapper.py", line 54, in __call__
    return self.func(*args, **kwargs)
  File "/var/lang/lib/python3.8/site-packages/datadog_lambda/wrapper.py", line 124, in __call__
    self.response = self.func(event, context, **kwargs)
  File "/var/task/handler.py", line 105, in execute_task
    executor = executor_getter(event)
  File "/var/task/handler.py", line 43, in get_step_executor
    executor = ETLStepExecutor(Worker.lambda_instance.value, **get_parameters_for_inference_pipeline(event))
  File "/var/lang/lib/python3.8/site-packages/sondes/containers/executors/base.py", line 24, in __init__
    client = get_third_party_client(kwargs["third_party_module_name"])(
  File "/var/task/caiso/client.py", line 17, in __init__
    self.api = get_caiso_api_client(
  File "/var/task/caiso/utility/caiso_api_client.py", line 40, in get_caiso_api_client
    token = get_login_token(service_url, username, password, worker, environment, cache_host)
  File "/var/task/caiso/utility/caiso_api_client.py", line 32, in get_login_token
    return token_cache.get_or_create(
  File "/var/lang/lib/python3.8/site-packages/ddtrace/contrib/dogpile_cache/region.py", line 14, in _wrap_get_create
    key = args[0]
```

### What is the result that you expected?
No error 


Discussion:
Thanks for the report @kharmabum! I'm able to reproduce the issue. Fix will be up shortly!

Resolving patch:
diff --git a/ddtrace/contrib/dogpile_cache/region.py b/ddtrace/contrib/dogpile_cache/region.py
--- a/ddtrace/contrib/dogpile_cache/region.py
+++ b/ddtrace/contrib/dogpile_cache/region.py
@@ -4,6 +4,7 @@
 
 from ...constants import SPAN_MEASURED_KEY
 from ...pin import Pin
+from ...utils import get_argument_value
 
 
 def _wrap_get_create(func, instance, args, kwargs):
@@ -11,7 +12,7 @@ def _wrap_get_create(func, instance, args, kwargs):
     if not pin or not pin.enabled():
         return func(*args, **kwargs)
 
-    key = args[0]
+    key = get_argument_value(args, kwargs, 0, "key")
     with pin.tracer.trace("dogpile.cache", resource="get_or_create", span_type=SpanTypes.CACHE) as span:
         span.set_tag(SPAN_MEASURED_KEY)
         span.set_tag("key", key)
@@ -25,7 +26,7 @@ def _wrap_get_create_multi(func, instance, args, kwargs):
     if not pin or not pin.enabled():
         return func(*args, **kwargs)
 
-    keys = args[0]
+    keys = get_argument_value(args, kwargs, 0, "keys")
     with pin.tracer.trace("dogpile.cache", resource="get_or_create_multi", span_type="cache") as span:
         span.set_tag(SPAN_MEASURED_KEY)
         span.set_tag("keys", keys)


Test patch:
diff --git a/tests/contrib/dogpile_cache/test_tracing.py b/tests/contrib/dogpile_cache/test_tracing.py
--- a/tests/contrib/dogpile_cache/test_tracing.py
+++ b/tests/contrib/dogpile_cache/test_tracing.py
@@ -26,7 +26,7 @@ def region(tracer):
     patch()
     # Setup a simple dogpile cache region for testing.
     # The backend is trivial so we can use memory to simplify test setup.
-    test_region = dogpile.cache.make_region(name="TestRegion")
+    test_region = dogpile.cache.make_region(name="TestRegion", key_mangler=lambda x: x)
     test_region.configure("dogpile.cache.memory")
     Pin.override(dogpile.cache, tracer=tracer)
     return test_region
@@ -205,3 +205,12 @@ def test_calls_inner_functions_correctly(self, region, mocker):
         spy_multi_cache.reset_mock()
         assert [6, 10] == multi_cache(3, 5)
         assert spy_single_cache.call_count == 0
+
+
+def test_get_or_create_kwarg_only(region):
+    """
+    When get_or_create is called with only kwargs
+        The arguments should be handled correctly
+    """
+    assert region.get_or_create(key="key", creator=lambda: 3) == 3
+    assert region.get_or_create_multi(keys="keys", creator=lambda *args: [1, 2])