1. Removed the unicode wrapper around fake_env io 2. Overrided pyfakefs cStringIO with StringIO (Python 2.x) 3. copy_dir will copy file in binary file to the fake filesystem *unless* they are bib, yaml, txt or md files 4. ui.handle_exception() conserve traceback Numerous unicode subtelties required diving into pyfakefs and configobj source code. Unicode on Python 2.x is the worst.
191 lines
5.3 KiB
Python
191 lines
5.3 KiB
Python
import sys
|
|
import io
|
|
import os
|
|
import shutil
|
|
import glob
|
|
import unittest
|
|
|
|
import dotdot
|
|
from pyfakefs import (fake_filesystem, fake_filesystem_shutil,
|
|
fake_filesystem_glob)
|
|
|
|
# pyfakefs uses cStringIO under Python 2.x, which does not accept arbitrary unicode strings
|
|
# (see https://docs.python.org/2/library/stringio.html#module-cStringIO)
|
|
# io.StringIO does not accept `str`, so we're left with the StringIO module
|
|
# PS: this nonsense explains why Python 3.x was created.
|
|
try:
|
|
import StringIO
|
|
fake_filesystem.io = StringIO
|
|
except ImportError:
|
|
pass
|
|
|
|
from pubs.p3 import input, _fake_stdio, _get_fake_stdio_ucontent
|
|
from pubs import content, filebroker
|
|
|
|
# code for fake fs
|
|
|
|
real_os = os
|
|
real_open = open
|
|
real_shutil = shutil
|
|
real_glob = glob
|
|
real_io = io
|
|
|
|
|
|
ENCODING = 'utf8'
|
|
|
|
class FakeIO(object):
|
|
|
|
def __init__(self, fake_open):
|
|
self.fake_open = fake_open
|
|
|
|
def open(self, file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True):
|
|
# encoding is ignored by pyfakefs
|
|
# https://github.com/jmcgeheeiv/pyfakefs/blob/master/pyfakefs/fake_filesystem.py#L2143
|
|
return self.fake_open(file, mode=mode, buffering=buffering)
|
|
|
|
|
|
BytesIO = real_io.BytesIO
|
|
StringIO = real_io.StringIO
|
|
|
|
|
|
def create_fake_fs(module_list):
|
|
|
|
fake_fs = fake_filesystem.FakeFilesystem()
|
|
fake_os = fake_filesystem.FakeOsModule(fake_fs)
|
|
fake_open = fake_filesystem.FakeFileOpen(fake_fs)
|
|
fake_shutil = fake_filesystem_shutil.FakeShutilModule(fake_fs)
|
|
fake_glob = fake_filesystem_glob.FakeGlobModule(fake_fs)
|
|
fake_io = FakeIO(fake_open)
|
|
|
|
fake_fs.CreateDirectory(fake_os.path.expanduser('~'))
|
|
|
|
sys.modules['os'] = fake_os
|
|
sys.modules['shutil'] = fake_shutil
|
|
sys.modules['glob'] = fake_glob
|
|
sys.modules['io'] = fake_io
|
|
|
|
for md in module_list:
|
|
md.os = fake_os
|
|
md.shutil = fake_shutil
|
|
md.open = fake_open
|
|
md.file = None
|
|
md.io = fake_io
|
|
|
|
return {'fs': fake_fs,
|
|
'os': fake_os,
|
|
'open': fake_open,
|
|
'io': fake_io,
|
|
'shutil': fake_shutil,
|
|
'glob': fake_glob}
|
|
|
|
|
|
def unset_fake_fs(module_list):
|
|
try:
|
|
__builtins__.open = real_open
|
|
except AttributeError:
|
|
__builtins__['open'] = real_open
|
|
|
|
sys.modules['os'] = real_os
|
|
sys.modules['shutil'] = real_shutil
|
|
sys.modules['glob'] = real_glob
|
|
sys.modules['io'] = real_io
|
|
|
|
for md in module_list:
|
|
md.os = real_os
|
|
md.shutil = real_shutil
|
|
md.open = real_open
|
|
md.io = real_io
|
|
|
|
|
|
def copy_dir(fs, real_dir, fake_dir = None):
|
|
"""Copy all the data directory into the fake fs"""
|
|
if fake_dir is None:
|
|
fake_dir = real_dir
|
|
for filename in real_os.listdir(real_dir):
|
|
real_path = os.path.abspath(real_os.path.join(real_dir, filename))
|
|
fake_path = fs['os'].path.join(fake_dir, filename)
|
|
if real_os.path.isfile(real_path):
|
|
_, ext = real_os.path.splitext(filename)
|
|
if ext in ['.yaml', '.bib', '.txt', '.md']:
|
|
with real_io.open(real_path, 'r', encoding='utf-8') as f:
|
|
fs['fs'].CreateFile(fake_path, contents=f.read())
|
|
else:
|
|
with real_io.open(real_path, 'rb') as f:
|
|
fs['fs'].CreateFile(fake_path, contents=f.read())
|
|
|
|
if real_os.path.isdir(real_path):
|
|
fs['fs'].CreateDirectory(fake_path)
|
|
copy_dir(fs, real_path, fake_path)
|
|
|
|
|
|
# redirecting output
|
|
|
|
def redirect(f):
|
|
def newf(*args, **kwargs):
|
|
old_stderr, old_stdout = sys.stderr, sys.stdout
|
|
stdout = _fake_stdio()
|
|
stderr = _fake_stdio()
|
|
sys.stdout, sys.stderr = stdout, stderr
|
|
try:
|
|
return f(*args, **kwargs), _get_fake_stdio_ucontent(stdout), _get_fake_stdio_ucontent(stderr)
|
|
finally:
|
|
sys.stderr, sys.stdout = old_stderr, old_stdout
|
|
return newf
|
|
|
|
|
|
# Test helpers
|
|
|
|
# automating input
|
|
|
|
real_input = input
|
|
|
|
|
|
class FakeInput():
|
|
""" Replace the input() command, and mock user input during tests
|
|
|
|
Instanciate as :
|
|
input = FakeInput(['yes', 'no'])
|
|
then replace the input command in every module of the package :
|
|
input.as_global()
|
|
Then :
|
|
input() returns 'yes'
|
|
input() returns 'no'
|
|
input() raises IndexError
|
|
"""
|
|
|
|
class UnexpectedInput(Exception):
|
|
pass
|
|
|
|
def __init__(self, inputs, module_list=tuple()):
|
|
self.inputs = list(inputs) or []
|
|
self.module_list = module_list
|
|
self._cursor = 0
|
|
|
|
def as_global(self):
|
|
for md in self.module_list:
|
|
md.input = self
|
|
md.editor_input = self
|
|
# if mdname.endswith('files'):
|
|
# md.editor_input = self
|
|
|
|
def add_input(self, inp):
|
|
self.inputs.append(inp)
|
|
|
|
def __call__(self, *args, **kwargs):
|
|
try:
|
|
inp = self.inputs[self._cursor]
|
|
self._cursor += 1
|
|
return inp
|
|
except IndexError:
|
|
raise self.UnexpectedInput('Unexpected user input in test.')
|
|
|
|
|
|
class TestFakeFs(unittest.TestCase):
|
|
"""Abstract TestCase intializing the fake filesystem."""
|
|
|
|
def setUp(self):
|
|
self.fs = create_fake_fs([content, filebroker])
|
|
|
|
def tearDown(self):
|
|
unset_fake_fs([content, filebroker])
|