Michael Washburn Jr's Blog

Why You Shouldn’t Import Functions and Classes Directly in Python

Written by Michael Washburn Jr | Sep 23, 2017 4:52:24 PM

Today I was doing some programming, trying to finish up an old project that was 80% done, but I never finished. The project is an API for sharing beer ingredient data, being the avid homebrewer that I am. I was in the middle of writing some pretty simple authentication logic, when I found a weird bug in my code. The code, featured below, was pretty straightforward, or so I thought.

from django.shortcuts import render
from rest_framework.authentication import SessionAuthentication
from rest_framework.response import Response
from rest_framework.renderers import JSONRenderer
from django.contrib.auth import authenticate, login
from rest_framework import status
from rest_framework.decorators import api_view

@api_view(['POST'])
def login(request):
    username = request.data['username']
    password = request.data['password']
    user = authenticate(username=username, password=password)
    if user is not None:
        login(request, user)
        # I haven't gotten this far yet
    else:
        content = {'error': 'Invalid Credentials'}
        return Response(content, status=status.HTTP_401_UNAUTHORIZED)

I was reminded of a lesson I’ve learned before the hard way today when I got a mysterious error in my terminal.

TypeError: login() takes 1 positional argument but 2 were given.

The Django docs (which are awesome, by the way) clearly state the login function takes two positional arguments. So what the heck is wrong with this code?

Well, this is a friendly reminder from me to never directly import functions and classes from packages. Import the parent package, not the function. Otherwise, you might end up importing a function with the same name as a function you’ve already defined…

To be clear, the error I was getting was because I had already defined a “login” function. Which means when I tried calling Django’s login function, I was actually making a recursive call to my own function, unsuccessfully albeit.

Had I only imported django.contrib.auth, I would have saved myself 10 minutes of time that I spent staring dumbfounded at my screen.