Repository: ibis-project/ibis

Issue:
ci(examples): occasional test flakiness in `test_examples`
### What happened?

The test (`test_examples`) that downloads examples and runs a simple computation with them is flaky. See https://github.com/ibis-project/ibis/actions/runs/5358583495/jobs/9721149724?pr=6521 for a run that failed because of (I think) the penguins CSV file being in use by multiple processes at the same time which Windows doesn't allow.

### What version of ibis are you using?

`master`

### What backend(s) are you using, if any?

DuckDB

### Relevant log output

_No response_

### Code of Conduct

- [X] I agree to follow this project's Code of Conduct


Discussion:
My hunch is that another test (perhaps a doctest?) is running in parallel and downloading (and therefore trying to write to) the file at the same time.

Resolving patch:
diff --git a/ibis/backends/conftest.py b/ibis/backends/conftest.py
--- a/ibis/backends/conftest.py
+++ b/ibis/backends/conftest.py
@@ -30,6 +30,7 @@
 LINUX = platform.system() == "Linux"
 MACOS = platform.system() == "Darwin"
 WINDOWS = platform.system() == "Windows"
+CI = os.environ.get("CI") is not None
 
 TEST_TABLES = {
     "functional_alltypes": ibis.schema(
diff --git a/ibis/examples/__init__.py b/ibis/examples/__init__.py
--- a/ibis/examples/__init__.py
+++ b/ibis/examples/__init__.py
@@ -4,6 +4,8 @@
 import os
 from typing import TYPE_CHECKING, Optional
 
+import filelock
+
 import ibis
 from ibis.common.grounds import Concrete
 
@@ -36,7 +38,11 @@ def fetch(
         table_name: str | None = None,
         backend: BaseBackend | None = None,
     ) -> ir.Table:
-        path = _EXAMPLES.fetch(self.key, progressbar=True)
+        key = self.key
+        # lock to ensure we don't clobber the file if fetched in another
+        # process
+        with filelock.FileLock(f"{key}.lock"):
+            path = _EXAMPLES.fetch(key, progressbar=True)
 
         if backend is None:
             backend = ibis.get_backend()


Test patch:
diff --git a/ibis/examples/tests/test_examples.py b/ibis/examples/tests/test_examples.py
--- a/ibis/examples/tests/test_examples.py
+++ b/ibis/examples/tests/test_examples.py
@@ -1,20 +1,20 @@
-import os
 import uuid
 
 import pytest
 
 import ibis.examples
 import ibis.util
-from ibis.backends.conftest import LINUX, SANDBOXED
+from ibis.backends.conftest import CI, LINUX, SANDBOXED
 
 pytestmark = pytest.mark.examples
 
 duckdb = pytest.importorskip("duckdb")
 pytest.importorskip("pooch")
 
-# large files
+# large files or files that are used elsewhere
 ignored = frozenset(
     (
+        # large
         "imdb_name_basics",
         "imdb_title_akas",
         "imdb_title_basics",
@@ -24,7 +24,21 @@
         "imdb_title_ratings",
         "wowah_data_raw",
     )
-    * (os.environ.get("CI") is None)
+    * (not CI)  # ignore locally, but not in CI
+    + (
+        # use in doctests, avoid possible simultaneous use of the downloaded file
+        "Aids2",
+        "billboard",
+        "fish_encounters",
+        "penguins",
+        "penguins_raw_raw",
+        "relig_income_raw",
+        "us_rent_income",
+        "warpbreaks",
+        "who",
+        "world_bank_pop_raw",
+    )
+    * CI  # ignore in CI, but not locally
 )
 
 xfail_linux_nix = pytest.mark.xfail(