-
Notifications
You must be signed in to change notification settings - Fork 178
Gh safestore keyerror test #1243
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
cb21fda
b26c1dc
2ede146
51b4b35
b1244a2
7eb46d7
6703492
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| import pytest | ||
| from malariagen_data.util import SafeStore | ||
|
|
||
|
|
||
| def test_safestore_getitem_success(): | ||
| store = {"foo": "bar"} | ||
| safe_store = SafeStore(store) | ||
| assert safe_store["foo"] == "bar" | ||
|
|
||
|
|
||
| def test_safestore_getitem_keyerror_raises_filenotfounderror(): | ||
| store = {} | ||
| safe_store = SafeStore(store) | ||
| with pytest.raises(FileNotFoundError) as excinfo: | ||
| _ = safe_store["missing_key"] | ||
| # Ensure the missing key is represented in the error message | ||
| assert "missing_key" in str(excinfo.value) | ||
| # Ensure the original KeyError is preserved as the cause | ||
| assert isinstance(excinfo.value.__cause__, KeyError) | ||
|
|
||
|
|
||
| def test_safestore_len(): | ||
| store = {"a": 1, "b": 2} | ||
| safe_store = SafeStore(store) | ||
| assert len(safe_store) == 2 | ||
|
|
||
|
|
||
| def test_safestore_iter(): | ||
| store = {"a": 1, "b": 2} | ||
| safe_store = SafeStore(store) | ||
| assert set(iter(safe_store)) == {"a", "b"} | ||
|
|
||
|
|
||
| def test_safestore_getattr_passthrough(): | ||
| class MockStore: | ||
| def __init__(self): | ||
| self.attr = "value" | ||
|
|
||
| store = MockStore() | ||
| safe_store = SafeStore(store) | ||
| assert safe_store.attr == "value" | ||
|
|
||
|
|
||
| def test_safestore_getattr_setstate_raises_attributeerror(): | ||
| store = {} | ||
| safe_store = SafeStore(store) | ||
| with pytest.raises(AttributeError): | ||
| _ = safe_store.__setstate__ | ||
|
|
||
|
|
||
| def test_safestore_setitem_raises_notimplemented(): | ||
| store = {} | ||
| safe_store = SafeStore(store) | ||
| with pytest.raises(NotImplementedError): | ||
| safe_store["foo"] = "bar" | ||
|
|
||
|
|
||
| def test_safestore_delitem_raises_notimplemented(): | ||
| store = {"foo": "bar"} | ||
| safe_store = SafeStore(store) | ||
| with pytest.raises(NotImplementedError): | ||
| del safe_store["foo"] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,222 @@ | ||
| # don't import any costly modules | ||
| import sys | ||
| import os | ||
|
|
||
|
|
||
| is_pypy = '__pypy__' in sys.builtin_module_names | ||
|
|
||
|
|
||
| def warn_distutils_present(): | ||
| if 'distutils' not in sys.modules: | ||
| return | ||
| if is_pypy and sys.version_info < (3, 7): | ||
| # PyPy for 3.6 unconditionally imports distutils, so bypass the warning | ||
| # https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250 | ||
| return | ||
| import warnings | ||
|
|
||
| warnings.warn( | ||
| "Distutils was imported before Setuptools, but importing Setuptools " | ||
| "also replaces the `distutils` module in `sys.modules`. This may lead " | ||
| "to undesirable behaviors or errors. To avoid these issues, avoid " | ||
| "using distutils directly, ensure that setuptools is installed in the " | ||
| "traditional way (e.g. not an editable install), and/or make sure " | ||
| "that setuptools is always imported before distutils." | ||
| ) | ||
|
|
||
|
|
||
| def clear_distutils(): | ||
| if 'distutils' not in sys.modules: | ||
| return | ||
| import warnings | ||
|
|
||
| warnings.warn("Setuptools is replacing distutils.") | ||
| mods = [ | ||
| name | ||
| for name in sys.modules | ||
| if name == "distutils" or name.startswith("distutils.") | ||
| ] | ||
| for name in mods: | ||
| del sys.modules[name] | ||
|
|
||
|
|
||
| def enabled(): | ||
| """ | ||
| Allow selection of distutils by environment variable. | ||
| """ | ||
| which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'local') | ||
| return which == 'local' | ||
|
|
||
|
|
||
| def ensure_local_distutils(): | ||
| import importlib | ||
|
|
||
| clear_distutils() | ||
|
|
||
| # With the DistutilsMetaFinder in place, | ||
| # perform an import to cause distutils to be | ||
| # loaded from setuptools._distutils. Ref #2906. | ||
| with shim(): | ||
| importlib.import_module('distutils') | ||
|
|
||
| # check that submodules load as expected | ||
| core = importlib.import_module('distutils.core') | ||
| assert '_distutils' in core.__file__, core.__file__ | ||
| assert 'setuptools._distutils.log' not in sys.modules | ||
|
|
||
|
|
||
| def do_override(): | ||
| """ | ||
| Ensure that the local copy of distutils is preferred over stdlib. | ||
|
|
||
| See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401 | ||
| for more motivation. | ||
| """ | ||
| if enabled(): | ||
| warn_distutils_present() | ||
| ensure_local_distutils() | ||
|
|
||
|
|
||
| class _TrivialRe: | ||
| def __init__(self, *patterns): | ||
| self._patterns = patterns | ||
|
|
||
| def match(self, string): | ||
| return all(pat in string for pat in self._patterns) | ||
|
|
||
|
|
||
| class DistutilsMetaFinder: | ||
| def find_spec(self, fullname, path, target=None): | ||
| # optimization: only consider top level modules and those | ||
| # found in the CPython test suite. | ||
| if path is not None and not fullname.startswith('test.'): | ||
| return | ||
|
|
||
| method_name = 'spec_for_{fullname}'.format(**locals()) | ||
| method = getattr(self, method_name, lambda: None) | ||
| return method() | ||
|
|
||
| def spec_for_distutils(self): | ||
| if self.is_cpython(): | ||
| return | ||
|
|
||
| import importlib | ||
| import importlib.abc | ||
| import importlib.util | ||
|
|
||
| try: | ||
| mod = importlib.import_module('setuptools._distutils') | ||
| except Exception: | ||
| # There are a couple of cases where setuptools._distutils | ||
| # may not be present: | ||
| # - An older Setuptools without a local distutils is | ||
| # taking precedence. Ref #2957. | ||
| # - Path manipulation during sitecustomize removes | ||
| # setuptools from the path but only after the hook | ||
| # has been loaded. Ref #2980. | ||
| # In either case, fall back to stdlib behavior. | ||
| return | ||
|
|
||
| class DistutilsLoader(importlib.abc.Loader): | ||
| def create_module(self, spec): | ||
| mod.__name__ = 'distutils' | ||
| return mod | ||
|
|
||
| def exec_module(self, module): | ||
| pass | ||
|
|
||
| return importlib.util.spec_from_loader( | ||
| 'distutils', DistutilsLoader(), origin=mod.__file__ | ||
| ) | ||
|
|
||
| @staticmethod | ||
| def is_cpython(): | ||
| """ | ||
| Suppress supplying distutils for CPython (build and tests). | ||
| Ref #2965 and #3007. | ||
| """ | ||
| return os.path.isfile('pybuilddir.txt') | ||
|
|
||
| def spec_for_pip(self): | ||
| """ | ||
| Ensure stdlib distutils when running under pip. | ||
| See pypa/pip#8761 for rationale. | ||
| """ | ||
| if self.pip_imported_during_build(): | ||
| return | ||
| clear_distutils() | ||
| self.spec_for_distutils = lambda: None | ||
|
|
||
| @classmethod | ||
| def pip_imported_during_build(cls): | ||
| """ | ||
| Detect if pip is being imported in a build script. Ref #2355. | ||
| """ | ||
| import traceback | ||
|
|
||
| return any( | ||
| cls.frame_file_is_setup(frame) for frame, line in traceback.walk_stack(None) | ||
| ) | ||
|
|
||
| @staticmethod | ||
| def frame_file_is_setup(frame): | ||
| """ | ||
| Return True if the indicated frame suggests a setup.py file. | ||
| """ | ||
| # some frames may not have __file__ (#2940) | ||
| return frame.f_globals.get('__file__', '').endswith('setup.py') | ||
|
|
||
| def spec_for_sensitive_tests(self): | ||
| """ | ||
| Ensure stdlib distutils when running select tests under CPython. | ||
|
|
||
| python/cpython#91169 | ||
| """ | ||
| clear_distutils() | ||
| self.spec_for_distutils = lambda: None | ||
|
|
||
| sensitive_tests = ( | ||
| [ | ||
| 'test.test_distutils', | ||
| 'test.test_peg_generator', | ||
| 'test.test_importlib', | ||
| ] | ||
| if sys.version_info < (3, 10) | ||
| else [ | ||
| 'test.test_distutils', | ||
| ] | ||
| ) | ||
|
|
||
|
|
||
| for name in DistutilsMetaFinder.sensitive_tests: | ||
| setattr( | ||
| DistutilsMetaFinder, | ||
| f'spec_for_{name}', | ||
| DistutilsMetaFinder.spec_for_sensitive_tests, | ||
| ) | ||
|
|
||
|
|
||
| DISTUTILS_FINDER = DistutilsMetaFinder() | ||
|
|
||
|
|
||
| def add_shim(): | ||
| DISTUTILS_FINDER in sys.meta_path or insert_shim() | ||
|
|
||
|
|
||
| class shim: | ||
| def __enter__(self): | ||
| insert_shim() | ||
|
|
||
| def __exit__(self, exc, value, tb): | ||
| remove_shim() | ||
|
|
||
|
|
||
| def insert_shim(): | ||
| sys.meta_path.insert(0, DISTUTILS_FINDER) | ||
|
|
||
|
|
||
| def remove_shim(): | ||
| try: | ||
| sys.meta_path.remove(DISTUTILS_FINDER) | ||
| except ValueError: | ||
| pass |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| __import__('_distutils_hack').do_override() |
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1 @@ | ||||||||
| import os; var = 'SETUPTOOLS_USE_DISTUTILS'; enabled = os.environ.get(var, 'local') == 'local'; enabled and __import__('_distutils_hack').add_shim(); | ||||||||
|
||||||||
| import os; var = 'SETUPTOOLS_USE_DISTUTILS'; enabled = os.environ.get(var, 'local') == 'local'; enabled and __import__('_distutils_hack').add_shim(); | |
| # This file belonged to a local Python virtual environment and should not be committed to version control. | |
| # Recreate your virtual environment locally instead of relying on venv/ content from the repository. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| pip |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| Copyright (c) 2008-present The pip developers (see AUTHORS.txt file) | ||
|
|
||
| Permission is hereby granted, free of charge, to any person obtaining | ||
| a copy of this software and associated documentation files (the | ||
| "Software"), to deal in the Software without restriction, including | ||
| without limitation the rights to use, copy, modify, merge, publish, | ||
| distribute, sublicense, and/or sell copies of the Software, and to | ||
| permit persons to whom the Software is furnished to do so, subject to | ||
| the following conditions: | ||
|
|
||
| The above copyright notice and this permission notice shall be | ||
| included in all copies or substantial portions of the Software. | ||
|
|
||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
| EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
| MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
| NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | ||
| LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
| OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
| WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
Uh oh!
There was an error while loading. Please reload this page.