1

My package currently has a function that's something like:

def do_something(customer): customer_id = customer.id if isinstance(customer, Customer) else customer ...

I'd like to be more strict about the argument type, and replace that with:

def do_something(customer_id): ...

However, I want to be very careful not to break users' code without going through a deprecation cycle. Adding a deprecation warning in the conditional (and then later removing the whole conditional) would suffice for most uses, but some users might be passing my customer argument as a keyword argument, in which case my change will break their code.

Is there any way to transition my argument name from customer to customer_id, while not breaking any code, except through deprecation cycles where the old code still works?

DavidC
  • 1,409
  • 10
  • 25
  • 1
    On mobile so no format answer, but you could add an optional `**kwargs` argument to the function and check that dictionary for a `customer` key. – Jared Goguen Mar 11 '16 at 00:32

1 Answers1

2

I think a deprecation and major version change is fine. You could potentially support both using *args and **kwargs.

def do_something(*args, **kwargs):
    if args:
        customer_id = args[0]
    elif 'customer' in kwargs:
        customer_id = kwargs['customer']
    elif 'customer_id' in kwargs:
        customer_id = kwargs['customer_id']
    else:
        raise TypeError('Expected customer_id')

It's not entirely backwards compatible because the function signature is different and introspection will be different, but it would support both keyword arguments and positional arguments. If you have a lot of arguments (especially with defaults), it can get kind of messy.

You can include extra checks to make sure people don't provide both keyword arguments, or provide an argument positionally and as a keyword. A lot of libraries don't even include deprecation warnings, they just increment the major version and indicate the changes in the release notes. I think it's far better to keep your code clean and easy to understand and tell your users to just suck it up and update their code than make your code harder to read and increase the chance of bugs.

Brendan Abel
  • 35,343
  • 14
  • 88
  • 118
  • 1
    Riiiight, it's necessary to use both args and kwargs for the case in which the function was called in the past with only a keyword argument. – Jared Goguen Mar 11 '16 at 01:46
  • 1
    As a slight improvement, you can offer `customer=None` as a default argument and then any other named arguments as normal. – Jared Goguen Mar 11 '16 at 01:49