views:

386

answers:

3

Hi Guys,

I see a variation in output between C and python code in Windows trying to get the same functionality. The c code ( similar to unix shell scripts ) shows the TEST1 environment variable in 'test.bat.output' with its value as empty string whereas the python code removes this environment variable.

Is there a way to ask Python not to remove the environment variable from environ table when it is empty?

C

#include <windows.h>

main()
{

  DWORD dwRet;
  char pszOldVal[1024] = "abc";

  if(! SetEnvironmentVariable("TEST1", ""))  
    puts("Error\n");

  // _putenv("TEST1=");

  // GetEnvironmentVariable("TEST1", pszOldVal, dwRet);
  system("cmd /c test.bat >test.bat.output");
}

Python

import os
os.environ['TEST1'] = ""
os.environ['TEST2'] = "karthik"
os.system("cmd /c test.bat > test.bat.output.python")

-Karthik

A: 

Yes empty values are not being put into environ, but interesting thing is calling SetEnvironmentVariable from win32api or ctypes module has same affect as os.environ though win32api.SetEnvironmentVariable would be calling the same function as in C .

So are you sure you get different result in C code?

import win32api
import os
win32api.SetEnvironmentVariable("TEST1", "")
# or 
# import ctypes
# ctypes.windll.kernel32.SetEnvironmentVariable("TEST1", "")
os.system("echo %TEST1%")
Anurag Uniyal
I could easily see `putenv` and `SetEnvironmentVariable` having completely different semantics under Windows.
Omnifarious
but atleast invoking SetEnvironmentVariable from python has same effect as with os.environ or putenv
Anurag Uniyal
I don't have win32api or ctypes module to try immediately in my python installation. But what I said about the C code is true. Can you please check at your end?
Kartlee
A: 

This is a possible answer. I don't have a Windows system handy to test with, so I don't really know what this code will do, but what happens if you do this:

import os, subprocess
myenv = {}
myenv.update(os.environ)
myenv['TEST1'] = ""
myenv['TEST2'] = "karthik"
subprocess.Popen(('cmd', '/c', 'test.bat'), stdout=file("test.bat.output.python", 'w'),
                 env=myenv).wait()

In my opinion, you might be encountering a Python bug of some kind. Especially if the code I just gave works.

Also, testing your code under Unix does work. The environment ends up with an empty environment variable in it.

Omnifarious
Your suggestion to get the copy of environ and using it for subprocess works. Could the C code also work the same way internally?
Kartlee
My theory on why it works is that there is a difference between the way the API the os.environ variable uses and the API that subprocesses uses. My guess is that the API that os.environ uses is geared towards old programs that expect setenv to delete an environment variable when it is set to empty.
Omnifarious
+1  A: 

Cross-platform compatibility between Windows and "most everybody else" (operating systems derived or inspired from Unix) is often hard to get, especially in the innumerable corner cases that inevitably arise (e.g., as in this question, "does setting an environment variable to empty mean unsetting it"). Sometimes it's just easier to access Windows specific functionality directly rather than trying to stretch the "cross-platform" functionality.

While the traditional way to access Windows-specific functionality from Python is the win32all extension package, in recent Python versions the ctypes standard library module offers an alternative with the advantage of requiring no installation of C coded extensions. An interesting project is jaraco.windows, a set of pure-Python code on top of ctypes to make Windows operations easier and smoother. For example, if you work with the environment and/or the registry, the environ.py module offers a nice set of functions and classes with a more Pythonic feel to them than the bare win32 API as accessed by the underlying ctypes (e.g., get an exception with a readable error message in it in case of errors, rather than having to check return codes &c).

Alex Martelli