diff --git a/mypy/build.py b/mypy/build.py index 21a5559b329a..6ea2300702ec 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -1912,14 +1912,21 @@ def exclude_from_backups(target_dir: str) -> None: def create_metastore(options: Options, parallel_worker: bool) -> MetadataStore: """Create the appropriate metadata store.""" + cache_dir_prefix = _cache_dir_prefix(options) if options.sqlite_cache: - mds: MetadataStore = SqliteMetadataStore( - _cache_dir_prefix(options), - set_journal_mode=not parallel_worker, - num_shards=options.sqlite_num_shards, - ) + try: + mds: MetadataStore = SqliteMetadataStore( + cache_dir_prefix, + set_journal_mode=not parallel_worker, + num_shards=options.sqlite_num_shards, + ) + except ImportError: + # The sqlite3 Python module can be present even when the _sqlite3 + # extension module is not available. Fall back to the filesystem + # cache instead of crashing when initializing the default cache. + mds = FilesystemMetadataStore(cache_dir_prefix) else: - mds = FilesystemMetadataStore(_cache_dir_prefix(options)) + mds = FilesystemMetadataStore(cache_dir_prefix) return mds diff --git a/mypy/test/testmetastore.py b/mypy/test/testmetastore.py new file mode 100644 index 000000000000..7a7d4ae7aada --- /dev/null +++ b/mypy/test/testmetastore.py @@ -0,0 +1,35 @@ +"""Unit tests for metadata stores.""" + +from __future__ import annotations + +import tempfile +import unittest +from unittest.mock import patch + +from mypy import build +from mypy.metastore import FilesystemMetadataStore +from mypy.options import Options + + +class TestMetadataStore(unittest.TestCase): + @unittest.skipUnless( + build.__file__.endswith(".py"), "mock patching is unreliable for compiled mypy" + ) + def test_create_metastore_falls_back_to_filesystem_when_sqlite_missing(self) -> None: + options = Options() + options.sqlite_cache = True + + with tempfile.TemporaryDirectory() as tmpdir: + options.cache_dir = tmpdir + with patch( + "mypy.build.SqliteMetadataStore", + side_effect=ModuleNotFoundError("No module named '_sqlite3'"), + ): + store = build.create_metastore(options, parallel_worker=False) + + try: + assert isinstance(store, FilesystemMetadataStore) + assert store.write("example.meta.json", b"{}") + assert store.read("example.meta.json") == b"{}" + finally: + store.close()