1# Licensed under the Apache License, Version 2.0 (the "License"); 2# you may not use this file except in compliance with the License. 3# You may obtain a copy of the License at 4# 5# http://www.apache.org/licenses/LICENSE-2.0 6# 7# Unless required by applicable law or agreed to in writing, software 8# distributed under the License is distributed on an "AS IS" BASIS, 9# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10# See the License for the specific language governing permissions and 11# limitations under the License. 12 13""" 14Provides patches for some commonly used modules that enable them to work 15with pyfakefs. 16""" 17import sys 18 19try: 20 import pandas as pd 21 import pandas.io.parsers as parsers 22except ImportError: 23 parsers = None 24 25try: 26 import xlrd 27except ImportError: 28 xlrd = None 29 30try: 31 from django.core.files import locks 32except ImportError: 33 locks = None 34 35# From pandas v 1.2 onwards the python fs functions are used even when the engine 36# selected is "c". This means that we don't explicitly have to change the engine. 37patch_pandas = parsers is not None and [int(v) for v in pd.__version__.split(".")] < [ 38 1, 39 2, 40 0, 41] 42 43 44def get_modules_to_patch(): 45 modules_to_patch = {} 46 if xlrd is not None: 47 modules_to_patch["xlrd"] = XLRDModule 48 if locks is not None: 49 modules_to_patch["django.core.files.locks"] = FakeLocks 50 return modules_to_patch 51 52 53def get_classes_to_patch(): 54 classes_to_patch = {} 55 if patch_pandas: 56 classes_to_patch["TextFileReader"] = ["pandas.io.parsers"] 57 return classes_to_patch 58 59 60def get_fake_module_classes(): 61 fake_module_classes = {} 62 if patch_pandas: 63 fake_module_classes["TextFileReader"] = FakeTextFileReader 64 return fake_module_classes 65 66 67if xlrd is not None: 68 69 class XLRDModule: 70 """Patches the xlrd module, which is used as the default Excel file 71 reader by pandas. Disables using memory mapped files, which are 72 implemented platform-specific on OS level.""" 73 74 def __init__(self, _): 75 self._xlrd_module = xlrd 76 77 def open_workbook( 78 self, 79 filename=None, 80 logfile=sys.stdout, 81 verbosity=0, 82 use_mmap=False, 83 file_contents=None, 84 encoding_override=None, 85 formatting_info=False, 86 on_demand=False, 87 ragged_rows=False, 88 ): 89 return self._xlrd_module.open_workbook( 90 filename, 91 logfile, 92 verbosity, 93 False, 94 file_contents, 95 encoding_override, 96 formatting_info, 97 on_demand, 98 ragged_rows, 99 ) 100 101 def __getattr__(self, name): 102 """Forwards any unfaked calls to the standard xlrd module.""" 103 return getattr(self._xlrd_module, name) 104 105 106if patch_pandas: 107 # we currently need to add fake modules for both the parser module and 108 # the contained text reader - maybe this can be simplified 109 110 class FakeTextFileReader: 111 fake_parsers = None 112 113 def __init__(self, filesystem): 114 if self.fake_parsers is None: 115 self.__class__.fake_parsers = ParsersModule(filesystem) 116 117 def __call__(self, *args, **kwargs): 118 return self.fake_parsers.TextFileReader(*args, **kwargs) 119 120 def __getattr__(self, name): 121 return getattr(self.fake_parsers.TextFileReader, name) 122 123 class ParsersModule: 124 def __init__(self, _): 125 self._parsers_module = parsers 126 127 class TextFileReader(parsers.TextFileReader): 128 def __init__(self, *args, **kwargs): 129 kwargs["engine"] = "python" 130 super().__init__(*args, **kwargs) 131 132 def __getattr__(self, name): 133 """Forwards any unfaked calls to the standard xlrd module.""" 134 return getattr(self._parsers_module, name) 135 136 137if locks is not None: 138 139 class FakeLocks: 140 """django.core.files.locks uses low level OS functions, fake it.""" 141 142 _locks_module = locks 143 144 def __init__(self, _): 145 pass 146 147 @staticmethod 148 def lock(f, flags): 149 return True 150 151 @staticmethod 152 def unlock(f): 153 return True 154 155 def __getattr__(self, name): 156 return getattr(self._locks_module, name) 157