Repository: conan-io/conan

Issue:
Provide a way to unset environment variables from conan
I haven't found any way to unset environment variables in profiles and conan recipes.
Tools module has `environment_append`, but it can only add or replace something, not delete. Empty value is not the same as unbound variable, so `environment_append({"VAR": ""})` does not work in some cases.

`environment_append` could accept `None` values in a dictionary and unset the corresponding variables for them.
For profiles I have no idea yet.



Discussion:
We could provide a `tools.environment_uset([var1, var2])`. For the profiles wouldn't be that easy. Could it work?
Yep. I don't know what's better though (`environment_append` + `None` or such `environment_unset`).
`environment_unset` is clearer and self-describing, but requires two context managers when some variables should be changed and some should be unset.
If `environment_unset` is chosen, then for profiles it would be logical to use
```
[unset_env]
VAR1
VAR2, VAR3
some_package*: VAR4, VAR5
```
Yeah, probably your first choice of `varname: None` is better using the current `environment_append` context manager. Thanks. 
About the profiles, maybe we could use the same syntax, without "=" means unset, but I would consider it a different feature.

Resolving patch:
diff --git a/conans/client/tools/env.py b/conans/client/tools/env.py
--- a/conans/client/tools/env.py
+++ b/conans/client/tools/env.py
@@ -32,20 +32,29 @@ def run_environment(conanfile):
 @contextmanager
 def environment_append(env_vars):
     """
-    :param env_vars: List of simple environment vars. {name: value, name2: value2} => e.g.: MYVAR=1
+    :param env_vars: List (dict) of simple environment vars. {name: value, name2: value2} => e.g.: MYVAR=1
                      The values can also be lists of appendable environment vars. {name: [value, value2]}
                       => e.g. PATH=/path/1:/path/2
+                     If the value is set to None, then that environment variable is unset.
     :return: None
     """
+    unset_vars = []
+    for key in env_vars.keys():
+        if env_vars[key] is None:
+            unset_vars.append(key)
+    for var in unset_vars:
+        env_vars.pop(var, None)
     for name, value in env_vars.items():
         if isinstance(value, list):
             env_vars[name] = os.pathsep.join(value)
             old = os.environ.get(name)
             if old:
                 env_vars[name] += os.pathsep + old
-    if env_vars:
+    if env_vars or unset_vars:
         old_env = dict(os.environ)
         os.environ.update(env_vars)
+        for var in unset_vars:
+            os.environ.pop(var)
         try:
             yield
         finally:


Test patch:
diff --git a/conans/test/unittests/client/tools/test_env.py b/conans/test/unittests/client/tools/test_env.py
new file mode 100644
--- /dev/null
+++ b/conans/test/unittests/client/tools/test_env.py
@@ -0,0 +1,48 @@
+# coding=utf-8
+
+import os
+import unittest
+import mock
+
+from conans.client.tools import env
+
+
+class ToolsEnvTest(unittest.TestCase):
+    def test_environment_append_variables(self):
+        with mock.patch.dict('os.environ', {}),\
+             env.environment_append({'env_var1': 'value',
+                                     'env_var2': 'value2'}):
+                self.assertEqual(os.environ['env_var1'], 'value')
+                self.assertEqual(os.environ['env_var2'], 'value2')
+
+    def test_environment_append_variables_without_values(self):
+        with mock.patch.dict('os.environ',
+                             {'env_var1': 'value',
+                              'env_var2': 'value2'}),\
+             env.environment_append({}):
+                self.assertEqual(os.environ['env_var1'], 'value')
+                self.assertEqual(os.environ['env_var2'], 'value2')
+
+    def test_environment_append_overwriting(self):
+        with mock.patch.dict('os.environ', {'env_var1': 'value'}),\
+             env.environment_append({'env_var1': 'new_value'}):
+                self.assertEqual(os.environ['env_var1'], 'new_value')
+
+    def test_environment_append_list(self):
+        with mock.patch.dict('os.environ', {}),\
+             env.environment_append({'env_var1': ['value1', 'value2']}):
+                self.assertEqual(os.environ['env_var1'], 'value1' +
+                                 os.pathsep + 'value2')
+
+    def test_environment_append_unsetting_some_variables(self):
+        with mock.patch.dict('os.environ', {'env_var1': 'value'}),\
+             env.environment_append({'env_var1': None, 'env_var2': 'value2'}):
+                self.assertNotIn('env_var1', os.environ)
+                self.assertEqual(os.environ['env_var2'], 'value2')
+
+    def test_environment_append_unsetting_all_variables(self):
+        with mock.patch.dict('os.environ',
+                             {'env_var1': 'value',
+                              'env_var2': 'value2'}),\
+             env.environment_append({'env_var1': None}):
+                self.assertNotIn('env_var1', os.environ)