##// END OF EJS Templates
Simplify implementation of TemporaryWorkingDirectory....
Thomas Kluyver -
Show More
@@ -1,164 +1,164 b''
1 1 """
2 2 Contains base test class for nbconvert
3 3 """
4 4 #-----------------------------------------------------------------------------
5 5 #Copyright (c) 2013, the IPython Development Team.
6 6 #
7 7 #Distributed under the terms of the Modified BSD License.
8 8 #
9 9 #The full license is in the file COPYING.txt, distributed with this software.
10 10 #-----------------------------------------------------------------------------
11 11
12 12 #-----------------------------------------------------------------------------
13 13 # Imports
14 14 #-----------------------------------------------------------------------------
15 15
16 16 import io
17 17 import os
18 18 import glob
19 19 import shutil
20 20 import unittest
21 21
22 22 import IPython
23 23 from IPython.nbformat import current
24 24 from IPython.utils.tempdir import TemporaryWorkingDirectory
25 25 from IPython.utils.path import get_ipython_package_dir
26 26 from IPython.utils.process import get_output_error_code
27 27 from IPython.testing.tools import get_ipython_cmd
28 28
29 29 # a trailing space allows for simpler concatenation with the other arguments
30 30 ipy_cmd = get_ipython_cmd(as_string=True) + " "
31 31
32 32 #-----------------------------------------------------------------------------
33 33 # Classes and functions
34 34 #-----------------------------------------------------------------------------
35 35
36 36
37 37 class TestsBase(unittest.TestCase):
38 38 """Base tests class. Contains useful fuzzy comparison and nbconvert
39 39 functions."""
40 40
41 41
42 42 def fuzzy_compare(self, a, b, newlines_are_spaces=True, tabs_are_spaces=True,
43 43 fuzzy_spacing=True, ignore_spaces=False,
44 44 ignore_newlines=False, case_sensitive=False, leave_padding=False):
45 45 """
46 46 Performs a fuzzy comparison of two strings. A fuzzy comparison is a
47 47 comparison that ignores insignificant differences in the two comparands.
48 48 The significance of certain differences can be specified via the keyword
49 49 parameters of this method.
50 50 """
51 51
52 52 if not leave_padding:
53 53 a = a.strip()
54 54 b = b.strip()
55 55
56 56 if ignore_newlines:
57 57 a = a.replace('\n', '')
58 58 b = b.replace('\n', '')
59 59
60 60 if newlines_are_spaces:
61 61 a = a.replace('\n', ' ')
62 62 b = b.replace('\n', ' ')
63 63
64 64 if tabs_are_spaces:
65 65 a = a.replace('\t', ' ')
66 66 b = b.replace('\t', ' ')
67 67
68 68 if ignore_spaces:
69 69 a = a.replace(' ', '')
70 70 b = b.replace(' ', '')
71 71
72 72 if fuzzy_spacing:
73 73 a = self.recursive_replace(a, ' ', ' ')
74 74 b = self.recursive_replace(b, ' ', ' ')
75 75
76 76 if not case_sensitive:
77 77 a = a.lower()
78 78 b = b.lower()
79 79
80 80 self.assertEqual(a, b)
81 81
82 82
83 83 def recursive_replace(self, text, search, replacement):
84 84 """
85 85 Performs a recursive replacement operation. Replaces all instances
86 86 of a search string in a text string with a replacement string until
87 87 the search string no longer exists. Recursion is needed because the
88 88 replacement string may generate additional search strings.
89 89
90 90 For example:
91 91 Replace "ii" with "i" in the string "Hiiii" yields "Hii"
92 92 Another replacement cds "Hi" (the desired output)
93 93
94 94 Parameters
95 95 ----------
96 96 text : string
97 97 Text to replace in.
98 98 search : string
99 99 String to search for within "text"
100 100 replacement : string
101 101 String to replace "search" with
102 102 """
103 103 while search in text:
104 104 text = text.replace(search, replacement)
105 105 return text
106 106
107 107 def create_temp_cwd(self, copy_filenames=None):
108 108 temp_dir = TemporaryWorkingDirectory()
109 109
110 110 #Copy the files if requested.
111 111 if copy_filenames is not None:
112 self.copy_files_to(copy_filenames)
112 self.copy_files_to(copy_filenames, dest=temp_dir.name)
113 113
114 114 #Return directory handler
115 115 return temp_dir
116 116
117 117 def create_empty_notebook(self, path):
118 118 nb = current.new_notebook()
119 119 nb.worksheets.append(current.new_worksheet())
120 120 with io.open(path, 'w', encoding='utf-8') as f:
121 121 current.write(nb, f, 'json')
122 122
123 123
124 124 def copy_files_to(self, copy_filenames, dest='.'):
125 125 "Copy test files into the destination directory"
126 126 if not os.path.isdir(dest):
127 127 os.makedirs(dest)
128 128 files_path = self._get_files_path()
129 129 for pattern in copy_filenames:
130 130 for match in glob.glob(os.path.join(files_path, pattern)):
131 131 shutil.copyfile(match, os.path.join(dest, os.path.basename(match)))
132 132
133 133
134 134 def _get_files_path(self):
135 135
136 136 #Get the relative path to this module in the IPython directory.
137 137 names = self.__module__.split('.')[1:-1]
138 138 names.append('files')
139 139
140 140 #Build a path using the IPython directory and the relative path we just
141 141 #found.
142 142 path = get_ipython_package_dir()
143 143 for name in names:
144 144 path = os.path.join(path, name)
145 145 return path
146 146
147 147
148 148 def call(self, parameters, ignore_return_code=False):
149 149 """
150 150 Execute a, IPython shell command, listening for both Errors and non-zero
151 151 return codes.
152 152
153 153 Parameters
154 154 ----------
155 155 parameters : str
156 156 List of parameters to pass to IPython.
157 157 ignore_return_code : optional bool (default False)
158 158 Throw an OSError if the return code
159 159 """
160 160
161 161 stdout, stderr, retcode = get_output_error_code(ipy_cmd + parameters)
162 162 if not (retcode == 0 or ignore_return_code):
163 163 raise OSError(stderr)
164 164 return stdout, stderr
@@ -1,151 +1,145 b''
1 1 """TemporaryDirectory class, copied from Python 3.2.
2 2
3 3 This is copied from the stdlib and will be standard in Python 3.2 and onwards.
4 4 """
5 5 from __future__ import print_function
6 6
7 7 import os as _os
8 8 import warnings as _warnings
9 9 import sys as _sys
10 10
11 11 # This code should only be used in Python versions < 3.2, since after that we
12 12 # can rely on the stdlib itself.
13 13 try:
14 14 from tempfile import TemporaryDirectory
15 15
16 16 except ImportError:
17 17 from tempfile import mkdtemp, template
18 18
19 19 class TemporaryDirectory(object):
20 20 """Create and return a temporary directory. This has the same
21 21 behavior as mkdtemp but can be used as a context manager. For
22 22 example:
23 23
24 24 with TemporaryDirectory() as tmpdir:
25 25 ...
26 26
27 27 Upon exiting the context, the directory and everthing contained
28 28 in it are removed.
29 29 """
30 30
31 31 def __init__(self, suffix="", prefix=template, dir=None):
32 32 self.name = mkdtemp(suffix, prefix, dir)
33 33 self._closed = False
34 34
35 35 def __enter__(self):
36 36 return self.name
37 37
38 38 def cleanup(self, _warn=False):
39 39 if self.name and not self._closed:
40 40 try:
41 41 self._rmtree(self.name)
42 42 except (TypeError, AttributeError) as ex:
43 43 # Issue #10188: Emit a warning on stderr
44 44 # if the directory could not be cleaned
45 45 # up due to missing globals
46 46 if "None" not in str(ex):
47 47 raise
48 48 print("ERROR: {!r} while cleaning up {!r}".format(ex, self,),
49 49 file=_sys.stderr)
50 50 return
51 51 self._closed = True
52 52 if _warn:
53 53 self._warn("Implicitly cleaning up {!r}".format(self),
54 54 Warning)
55 55
56 56 def __exit__(self, exc, value, tb):
57 57 self.cleanup()
58 58
59 59 def __del__(self):
60 60 # Issue a ResourceWarning if implicit cleanup needed
61 61 self.cleanup(_warn=True)
62 62
63 63
64 64 # XXX (ncoghlan): The following code attempts to make
65 65 # this class tolerant of the module nulling out process
66 66 # that happens during CPython interpreter shutdown
67 67 # Alas, it doesn't actually manage it. See issue #10188
68 68 _listdir = staticmethod(_os.listdir)
69 69 _path_join = staticmethod(_os.path.join)
70 70 _isdir = staticmethod(_os.path.isdir)
71 71 _remove = staticmethod(_os.remove)
72 72 _rmdir = staticmethod(_os.rmdir)
73 73 _os_error = _os.error
74 74 _warn = _warnings.warn
75 75
76 76 def _rmtree(self, path):
77 77 # Essentially a stripped down version of shutil.rmtree. We can't
78 78 # use globals because they may be None'ed out at shutdown.
79 79 for name in self._listdir(path):
80 80 fullname = self._path_join(path, name)
81 81 try:
82 82 isdir = self._isdir(fullname)
83 83 except self._os_error:
84 84 isdir = False
85 85 if isdir:
86 86 self._rmtree(fullname)
87 87 else:
88 88 try:
89 89 self._remove(fullname)
90 90 except self._os_error:
91 91 pass
92 92 try:
93 93 self._rmdir(path)
94 94 except self._os_error:
95 95 pass
96 96
97 97
98 98 class NamedFileInTemporaryDirectory(object):
99 99
100 100 def __init__(self, filename, mode='w+b', bufsize=-1, **kwds):
101 101 """
102 102 Open a file named `filename` in a temporary directory.
103 103
104 104 This context manager is preferred over `NamedTemporaryFile` in
105 105 stdlib `tempfile` when one needs to reopen the file.
106 106
107 107 Arguments `mode` and `bufsize` are passed to `open`.
108 108 Rest of the arguments are passed to `TemporaryDirectory`.
109 109
110 110 """
111 111 self._tmpdir = TemporaryDirectory(**kwds)
112 112 path = _os.path.join(self._tmpdir.name, filename)
113 113 self.file = open(path, mode, bufsize)
114 114
115 115 def cleanup(self):
116 116 self.file.close()
117 117 self._tmpdir.cleanup()
118 118
119 119 __del__ = cleanup
120 120
121 121 def __enter__(self):
122 122 return self.file
123 123
124 124 def __exit__(self, type, value, traceback):
125 125 self.cleanup()
126 126
127 127
128 128 class TemporaryWorkingDirectory(TemporaryDirectory):
129 129 """
130 130 Creates a temporary directory and sets the cwd to that directory.
131 131 Automatically reverts to previous cwd upon cleanup.
132 132 Usage example:
133 133
134 with TemporaryWorakingDirectory() as tmpdir:
134 with TemporaryWorkingDirectory() as tmpdir:
135 135 ...
136 136 """
137
138 def __init__(self, **kw):
139 super(TemporaryWorkingDirectory, self).__init__(**kw)
140
141 #Change cwd to new temp dir. Remember old cwd.
137 def __enter__(self):
142 138 self.old_wd = _os.getcwd()
143 139 _os.chdir(self.name)
140 return super(TemporaryWorkingDirectory, self).__enter__()
144 141
145
146 def cleanup(self, _warn=False):
147 #Revert to old cwd.
142 def __exit__(self, exc, value, tb):
148 143 _os.chdir(self.old_wd)
144 return super(TemporaryWorkingDirectory, self).__exit__(exc, value, tb)
149 145
150 #Cleanup
151 super(TemporaryWorkingDirectory, self).cleanup(_warn=_warn)
General Comments 0
You need to be logged in to leave comments. Login now