def test_permission_error(self, fob, _): from streamlit import compatibility if compatibility.is_running_py3(): ErrorType = PermissionError else: ErrorType = OSError fob.side_effect = ErrorType("This error should be caught!") lso = LocalSourcesWatcher.LocalSourcesWatcher(REPORT, NOOP_CALLBACK)
def _register_watcher(self, filepath, module_name): if compatibility.is_running_py3(): ErrorType = PermissionError else: ErrorType = OSError try: wm = WatchedModule(watcher=FileWatcher(filepath, self.on_file_changed), module_name=module_name) except ErrorType: # If you don't have permission to read this file, don't even add it # to watchers. return self._watched_modules[filepath] = wm
def add_magic(code, script_path): """Modifies the code to support magic Streamlit commands. Parameters ---------- code : str The Python code. script_path : str The path to the script file. Returns ------- ast.Module The syntax tree for the code. """ if compatibility.is_running_py3(): # Pass script_path so we get pretty exceptions. tree = ast.parse(code, script_path, "exec") return _modify_ast_subtree(tree, is_root=True) return code
def _to_bytes(self, obj, context): """Hash objects to bytes, including code with dependencies. Python's built in `hash` does not produce consistent results across runs.""" try: if _is_magicmock(obj): # MagicMock can result in objects that appear to be infinitely # deep, so we don't try to hash them at all. return self.to_bytes(id(obj)) elif isinstance(obj, bytes) or isinstance(obj, bytearray): return obj elif isinstance(obj, string_types): # noqa: F821 # Don't allow the user to override string since # str == bytes on python 2 return obj.encode() elif type(obj) in self.hash_funcs: # Escape hatch for unsupported objects return self.to_bytes(self.hash_funcs[type(obj)](obj)) elif isinstance(obj, float): return self.to_bytes(hash(obj)) elif isinstance(obj, int): return _int_to_bytes(obj) elif isinstance(obj, list) or isinstance(obj, tuple): h = hashlib.new(self.name) # Hash the name of the container so that ["a"] hashes differently from ("a",) # Otherwise we'd only be hashing the data and the hashes would be the same. self._update(h, type(obj).__name__.encode() + b":") for e in obj: self._update(h, e, context) return h.digest() elif isinstance(obj, dict): h = hashlib.new(self.name) self._update(h, type(obj).__name__.encode() + b":") for e in obj.items(): self._update(h, e, context) return h.digest() elif obj is None: # Special string since hashes change between sessions. # We don't use Python's `hash` since hashes are not consistent # across runs. return NONESENSE elif obj is True: return b"bool:1" elif obj is False: return b"bool:0" elif type_util.is_type( obj, "pandas.core.frame.DataFrame") or type_util.is_type( obj, "pandas.core.series.Series"): import pandas as pd if len(obj) >= PANDAS_ROWS_LARGE: obj = obj.sample(n=PANDAS_SAMPLE_SIZE, random_state=0) try: return pd.util.hash_pandas_object(obj).sum() except TypeError: # Use pickle if pandas cannot hash the object for example if # it contains unhashable objects. return pickle.dumps(obj, pickle.HIGHEST_PROTOCOL) elif type_util.is_type(obj, "numpy.ndarray"): h = hashlib.new(self.name) self._update(h, obj.shape) if obj.size >= NP_SIZE_LARGE: import numpy as np state = np.random.RandomState(0) obj = state.choice(obj.flat, size=NP_SAMPLE_SIZE) self._update(h, obj.tobytes()) return h.digest() elif inspect.isbuiltin(obj): return self.to_bytes(obj.__name__) elif hasattr(obj, "name") and ( isinstance(obj, io.IOBase) # Handle temporary files used during testing or isinstance(obj, tempfile._TemporaryFileWrapper) or (not compatibility.is_running_py3() and isinstance(obj, file))): # Hash files as name + last modification date + offset. h = hashlib.new(self.name) self._update(h, obj.name) self._update(h, os.path.getmtime(obj.name)) self._update(h, obj.tell()) return h.digest() elif inspect.isroutine(obj): if hasattr(obj, "__wrapped__"): # Ignore the wrapper of wrapped functions. return self.to_bytes(obj.__wrapped__) if obj.__module__.startswith("streamlit"): # Ignore streamlit modules even if they are in the CWD # (e.g. during development). return self.to_bytes("%s.%s" % (obj.__module__, obj.__name__)) h = hashlib.new(self.name) if self._file_should_be_hashed(obj.__code__.co_filename): context = _get_context(obj) if obj.__defaults__: self._update(h, obj.__defaults__, context) h.update(self._code_to_bytes(obj.__code__, context)) else: # Don't hash code that is not in the current working directory. self._update(h, obj.__module__) self._update(h, obj.__name__) return h.digest() elif inspect.iscode(obj): return self._code_to_bytes(obj, context) elif inspect.ismodule(obj): # TODO: Figure out how to best show this kind of warning to the # user. In the meantime, show nothing. This scenario is too common, # so the current warning is quite annoying... # st.warning(('Streamlit does not support hashing modules. ' # 'We did not hash `%s`.') % obj.__name__) # TODO: Hash more than just the name for internal modules. return self.to_bytes(obj.__name__) elif inspect.isclass(obj): # TODO: Figure out how to best show this kind of warning to the # user. In the meantime, show nothing. This scenario is too common, # (e.g. in every "except" statement) so the current warning is # quite annoying... # st.warning(('Streamlit does not support hashing classes. ' # 'We did not hash `%s`.') % obj.__name__) # TODO: Hash more than just the name of classes. return self.to_bytes(obj.__name__) elif isinstance(obj, functools.partial): # The return value of functools.partial is not a plain function: # it's a callable object that remembers the original function plus # the values you pickled into it. So here we need to special-case it. h = hashlib.new(self.name) self._update(h, obj.args) self._update(h, obj.func) self._update(h, obj.keywords) return h.digest() else: # As a last resort h = hashlib.new(self.name) self._update(h, type(obj).__name__.encode() + b":") for e in obj.__reduce__(): self._update(h, e, context) return h.digest() except UnhashableType as e: raise e except Exception as e: LOGGER.error(e) msg = _hashing_error_message(type(obj)) raise UnhashableType(msg)