次のコードを使用して、環境変数を一時的に変更します。
_@contextmanager
def _setenv(**mapping):
"""``with`` context to temporarily modify the environment variables"""
backup_values = {}
backup_remove = set()
for key, value in mapping.items():
if key in os.environ:
backup_values[key] = os.environ[key]
else:
backup_remove.add(key)
os.environ[key] = value
try:
yield
finally:
# restore old environment
for k, v in backup_values.items():
os.environ[k] = v
for k in backup_remove:
del os.environ[k]
_
このwith
コンテキストは、主にテストケースで使用されます。例えば、
_def test_myapp_respects_this_envvar():
with _setenv(MYAPP_PLUGINS_DIR='testsandbox/plugins'):
myapp.plugins.register()
[...]
_
私の質問:__setenv
_を書くためのシンプルでエレガントな方法はありますか?実際にbackup = os.environ.copy()
を実行してから_os.environ = backup
_ ..を実行することを考えましたが、それがプログラムの動作に影響するかどうかはわかりません(例:_os.environ
_が参照 = Pythonインタプリタ)の他の場所。
_environ = dict(os.environ) # or os.environ.copy()
try:
...
finally:
os.environ.clear()
os.environ.update(_environ)
次の実装をお勧めします。
import contextlib
import os
@contextlib.contextmanager
def set_env(**environ):
"""
Temporarily set the process environment variables.
>>> with set_env(PLUGINS_DIR=u'test/plugins'):
... "PLUGINS_DIR" in os.environ
True
>>> "PLUGINS_DIR" in os.environ
False
:type environ: dict[str, unicode]
:param environ: Environment variables to set
"""
old_environ = dict(os.environ)
os.environ.update(environ)
try:
yield
finally:
os.environ.clear()
os.environ.update(old_environ)
編集:より高度な実装
以下のコンテキストマネージャーを使用して、環境変数を追加/削除/更新できます。
import contextlib
import os
@contextlib.contextmanager
def modified_environ(*remove, **update):
"""
Temporarily updates the ``os.environ`` dictionary in-place.
The ``os.environ`` dictionary is updated in-place so that the modification
is sure to work in all situations.
:param remove: Environment variables to remove.
:param update: Dictionary of environment variables and values to add/update.
"""
env = os.environ
update = update or {}
remove = remove or []
# List of environment variables being updated or removed.
stomped = (set(update.keys()) | set(remove)) & set(env.keys())
# Environment variables and values to restore on exit.
update_after = {k: env[k] for k in stomped}
# Environment variables and values to remove on exit.
remove_after = frozenset(k for k in update if k not in env)
try:
env.update(update)
[env.pop(k, None) for k in remove]
yield
finally:
env.update(update_after)
[env.pop(k) for k in remove_after]
使用例:
>>> with modified_environ('HOME', LD_LIBRARY_PATH='/my/path/to/lib'):
... home = os.environ.get('HOME')
... path = os.environ.get("LD_LIBRARY_PATH")
>>> home is None
True
>>> path
'/my/path/to/lib'
>>> home = os.environ.get('HOME')
>>> path = os.environ.get("LD_LIBRARY_PATH")
>>> home is None
False
>>> path is None
True
EDIT2
このコンテキストマネージャーのデモンストレーションは GitHub で入手できます。
私は同じことをしようとしていましたが、ユニットテストのために、 unittest.mock.patch
関数を使用してそれを行った方法は次のとおりです。
def test_function_with_different_env_variable():
with mock.patch.dict('os.environ', {'hello': 'world'}, clear=True):
self.assertEqual(os.environ.get('hello'), 'world')
self.assertEqual(len(os.environ), 1)
基本的に unittest.mock.patch.dict
とclear=True
を使用して、os.environ
のみを含む辞書として{'hello': 'world'}
を作成しています。
clear=True
を削除すると、元のos.environが許可され、指定されたキーと値のペアが{'hello': 'world'}
内に追加/置換されます。
{'hello': 'world'}
を削除すると、空の辞書が作成されるだけなので、os.envrion
はwith
内で空になります。
単体テストでは、オプションのパラメーターを指定したデコレーター関数を使用することをお勧めします。このようにして、変更された環境値をテスト関数全体に使用できます。以下のデコレータは、関数が例外を発生させた場合に、元の環境値も復元します。
import os
def patch_environ(new_environ=None, clear_orig=False):
if not new_environ:
new_environ = dict()
def actual_decorator(func):
from functools import wraps
@wraps(func)
def wrapper(*args, **kwargs):
original_env = dict(os.environ)
if clear_orig:
os.environ.clear()
os.environ.update(new_environ)
try:
result = func(*args, **kwargs)
except:
raise
finally: # restore even if Exception was raised
os.environ = original_env
return result
return wrapper
return actual_decorator
単体テストでの使用法:
class Something:
@staticmethod
def print_home():
home = os.environ.get('HOME', 'unknown')
print("HOME = {0}".format(home))
class SomethingTest(unittest.TestCase):
@patch_environ({'HOME': '/tmp/test'})
def test_environ_based_something(self):
Something.print_home() # prints: HOME = /tmp/test
unittest.main()
ここで要点を使用すると、ローカル、グローバルスコープ変数および環境変数を保存/復元できます: https://Gist.github.com/earonesty/ac0617a5672ae1a41be1eaf316dd63e4
import os
from varlib import vartemp, envtemp
x = 3
y = 4
with vartemp({'x':93,'y':94}):
print(x)
print(y)
print(x)
print(y)
with envtemp({'foo':'bar'}):
print(os.getenv('foo'))
print(os.getenv('foo'))
この出力:
93
94
3
4
bar
None
pytest
では、monkeypatch
フィクスチャを使用して環境変数を一時的に設定できます。詳細については、 ドキュメント を参照してください。便宜上、ここにスニペットをコピーしました。
import pytest
def test_upper_to_lower(monkeypatch):
"""Set the USER env var to assert the behavior."""
monkeypatch.setenv("USER", "TestingUser")
assert get_os_user_lower() == "testinguser"