Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot access imported functions in django shell by pipeline command?

It is a python script tshi3.py :

    import csv
    def li2ho2():
        print(csv)
    li2ho2()

I copied this code and pasted it in python manage.py shell. It works.

But I ran python manage.py shell < tshi3.py. Got NameError: name 'csv' is not defined.

Why?

Enviroment:

    Linux Mint 18.1
    Python 3.4.1 (default, Sep  3 2014, 08:45:22) 
    Django==1.11.4

There is a similar question.

like image 216
sih4sing5hog5 Avatar asked Jan 20 '26 17:01

sih4sing5hog5


2 Answers

I have taken a look at the code of shell command. Here it is:

if sys.platform != 'win32' and select.select([sys.stdin], [], [], 0)[0]:
    exec(sys.stdin.read())
    return

The problem comes from the exec command is called without passing the globals and locals parameters, then by default, the globals() and locals() dictionary of the current scope will be used.

Remember that at module level, globals and locals are the same dictionary, but at the current scope (at django.core.management.commands.shell.Command.handle), globals() and locals() are two different dictionary. And now things become uncontrollable when the code of tshi3.py is executed.

Let's go through each line of code:

import csv

This will import the module csv and put it into the locals() dictionary. So, if the locals() dict is the same as the globals() dict, this will also be in the globals() dict. BUT, in our case, csv is ONLY in the locals() dict, NOT in the globals() dict.

Next:

def li2ho2():
    print(csv)

When the command print(csv) is called, csv will be looked up in the locals() dictionary of the function li2ho2, and it's surely not there, so csv is looked up in the globals() dictionary. But as what I wrote above, csv is not in globals() dictionary, that's why the error: NameError: name 'csv' is not defined is raised.

I try changing the code of tshi3.py as below:

import csv

print('globals() equals to locals(): {}'.format(globals() == locals()))
print('csv is in globals(): {}'.format('csv' in globals()))
print('csv is in locals(): {}'.format('csv' in locals()))

def li2ho2():
    print('inside li2ho2 function:')
    print('    csv is in globals(): {}'.format('csv' in globals()))
    print('    csv is in locals(): {}'.format('csv' in locals()))

li2ho2()

And run it in two different ways:

$ python tshi3.py 
globals() equals to locals(): True
csv is in globals(): True
csv is in locals(): True
inside li2ho2 function:
    csv is in globals(): True
    csv is in locals(): False

$ ./manage.py shell < tshi3.py 
globals() equals to locals(): False
csv is in globals(): False
csv is in locals(): True
inside li2ho2 function:
    csv is in globals(): False
    csv is in locals(): False

So, you can see it's exactly as what I explained above. This problem can be fixed by passing an empty dictionary as globals parameters for the exec command as below:

exec(sys.stdin.read(), {})

Hope this helps you and I am sorry for not be able to explain the problem shorter.

like image 163
Phuong Nguyen Avatar answered Jan 22 '26 06:01

Phuong Nguyen


As said here - manage.py shell command splits globals() and locals(). So, if you need expected behaviour of variables visibility, you should update list of global variables manually with your new imported local variables. Way below is more easily than changing of manage.py shell function:

import csv

globals().update(locals())

def li2ho2():
    print(csv)

li2ho2()
like image 35
Iurii Aleev Avatar answered Jan 22 '26 07:01

Iurii Aleev



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!