|
import contextlib |
|
import os |
|
import sys |
|
from typing import Union |
|
|
|
from more_itertools import unique_everseen |
|
|
|
|
|
if sys.version_info >= (3, 9): |
|
StrPath = Union[str, os.PathLike[str]] |
|
else: |
|
StrPath = Union[str, os.PathLike] |
|
|
|
|
|
def ensure_directory(path): |
|
"""Ensure that the parent directory of `path` exists""" |
|
dirname = os.path.dirname(path) |
|
os.makedirs(dirname, exist_ok=True) |
|
|
|
|
|
def same_path(p1: StrPath, p2: StrPath) -> bool: |
|
"""Differs from os.path.samefile because it does not require paths to exist. |
|
Purely string based (no comparison between i-nodes). |
|
>>> same_path("a/b", "./a/b") |
|
True |
|
>>> same_path("a/b", "a/./b") |
|
True |
|
>>> same_path("a/b", "././a/b") |
|
True |
|
>>> same_path("a/b", "./a/b/c/..") |
|
True |
|
>>> same_path("a/b", "../a/b/c") |
|
False |
|
>>> same_path("a", "a/b") |
|
False |
|
""" |
|
return normpath(p1) == normpath(p2) |
|
|
|
|
|
def normpath(filename: StrPath) -> str: |
|
"""Normalize a file/dir name for comparison purposes.""" |
|
|
|
file = os.path.abspath(filename) if sys.platform == 'cygwin' else filename |
|
return os.path.normcase(os.path.realpath(os.path.normpath(file))) |
|
|
|
|
|
@contextlib.contextmanager |
|
def paths_on_pythonpath(paths): |
|
""" |
|
Add the indicated paths to the head of the PYTHONPATH environment |
|
variable so that subprocesses will also see the packages at |
|
these paths. |
|
|
|
Do this in a context that restores the value on exit. |
|
|
|
>>> getfixture('monkeypatch').setenv('PYTHONPATH', 'anything') |
|
>>> with paths_on_pythonpath(['foo', 'bar']): |
|
... assert 'foo' in os.environ['PYTHONPATH'] |
|
... assert 'anything' in os.environ['PYTHONPATH'] |
|
>>> os.environ['PYTHONPATH'] |
|
'anything' |
|
|
|
>>> getfixture('monkeypatch').delenv('PYTHONPATH') |
|
>>> with paths_on_pythonpath(['foo', 'bar']): |
|
... assert 'foo' in os.environ['PYTHONPATH'] |
|
>>> os.environ.get('PYTHONPATH') |
|
""" |
|
nothing = object() |
|
orig_pythonpath = os.environ.get('PYTHONPATH', nothing) |
|
current_pythonpath = os.environ.get('PYTHONPATH', '') |
|
try: |
|
prefix = os.pathsep.join(unique_everseen(paths)) |
|
to_join = filter(None, [prefix, current_pythonpath]) |
|
new_path = os.pathsep.join(to_join) |
|
if new_path: |
|
os.environ['PYTHONPATH'] = new_path |
|
yield |
|
finally: |
|
if orig_pythonpath is nothing: |
|
os.environ.pop('PYTHONPATH', None) |
|
else: |
|
os.environ['PYTHONPATH'] = orig_pythonpath |
|
|