Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save osantana/45b0b245aa4f5a8cc1009cc387b3e2da to your computer and use it in GitHub Desktop.
Save osantana/45b0b245aa4f5a8cc1009cc387b3e2da to your computer and use it in GitHub Desktop.
bpo-30441
diff --git a/Lib/os.py b/Lib/os.py
index e293ecae7f..4dfa41c712 100644
--- a/Lib/os.py
+++ b/Lib/os.py
@@ -25,6 +25,7 @@ and opendir), and leave all pathname manipulation to os.path
import abc
import sys, errno
import stat as st
+import _thread
_names = sys.builtin_module_names
@@ -672,6 +673,7 @@ class _Environ(MutableMapping):
self.putenv = putenv
self.unsetenv = unsetenv
self._data = data
+ self._lock = _thread.allocate_lock()
def __getitem__(self, key):
try:
@@ -697,7 +699,9 @@ class _Environ(MutableMapping):
raise KeyError(key) from None
def __iter__(self):
- for key in self._data:
+ with self._lock:
+ keys = list(self._data)
+ for key in keys:
yield self.decodekey(key)
def __len__(self):
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
index 746b3f8be8..9cbe4b90d1 100644
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -828,6 +828,21 @@ class EnvironTests(mapping_tests.BasicTestMappingProtocol):
self.assertIs(cm.exception.args[0], missing)
self.assertTrue(cm.exception.__suppress_context__)
+ def test_iter_error_when_os_environ_changes(self):
+ def _iter_environ_change():
+ for key, value in os.environ.items():
+ yield key, value
+
+ iter_environ = _iter_environ_change()
+ key, value = next(iter_environ) # start iteration over os.environ
+
+ # add a new key in os.environ mapping
+ new_key = "__{}".format(key)
+ os.environ[new_key] = value
+
+ next(iter_environ) # force iteration over modified mapping
+ self.assertEqual(os.environ[new_key], value)
+
class WalkTests(unittest.TestCase):
"""Tests for os.walk()."""
diff --git a/Lib/os.py b/Lib/os.py
index e293eca..5d28572 100644
--- a/Lib/os.py
+++ b/Lib/os.py
@@ -697,7 +697,8 @@ class _Environ(MutableMapping):
raise KeyError(key) from None
def __iter__(self):
- for key in self._data:
+ data = dict(self._data)
+ for key in data:
yield self.decodekey(key)
def __len__(self):
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
index 746b3f8..9cbe4b9 100644
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -828,6 +828,21 @@ class EnvironTests(mapping_tests.BasicTestMappingProtocol):
self.assertIs(cm.exception.args[0], missing)
self.assertTrue(cm.exception.__suppress_context__)
+ def test_iter_error_when_os_environ_changes(self):
+ def _iter_environ_change():
+ for key, value in os.environ.items():
+ yield key, value
+
+ iter_environ = _iter_environ_change()
+ key, value = next(iter_environ) # start iteration over os.environ
+
+ # add a new key in os.environ mapping
+ new_key = "__{}".format(key)
+ os.environ[new_key] = value
+
+ next(iter_environ) # force iteration over modified mapping
+ self.assertEqual(os.environ[new_key], value)
+
class WalkTests(unittest.TestCase):
"""Tests for os.walk()."""
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment