33import subprocess
44import sys
55import time
6+ from collections import deque
7+ from collections .abc import Generator , Sequence
68
79import pytest
810
911import fsspec
10- from fsspec .implementations .cached import CachingFileSystem
1112
1213
1314@pytest .fixture ()
@@ -27,16 +28,85 @@ def m():
2728 m .pseudo_dirs .append ("" )
2829
2930
30- @pytest .fixture
31+ class InstanceCacheInspector :
32+ """
33+ Helper class to inspect instance caches of filesystem classes in tests.
34+ """
35+
36+ def clear (self ) -> None :
37+ """
38+ Clear instance caches of all currently imported filesystem classes.
39+ """
40+ classes = deque ([fsspec .spec .AbstractFileSystem ])
41+ while classes :
42+ cls = classes .popleft ()
43+ cls .clear_instance_cache ()
44+ classes .extend (cls .__subclasses__ ())
45+
46+ def gather_counts (self , * , omit_zero : bool = True ) -> dict [str , int ]:
47+ """
48+ Gather counts of filesystem instances in the instance caches
49+ of all currently imported filesystem classes.
50+
51+ Parameters
52+ ----------
53+ omit_zero:
54+ Whether to omit instance types with no cached instances.
55+ """
56+ out : dict [str , int ] = {}
57+ classes = deque ([fsspec .spec .AbstractFileSystem ])
58+ while classes :
59+ cls = classes .popleft ()
60+ count = len (cls ._cache ) # there is no public interface for the cache
61+ # note: skip intermediate AbstractFileSystem subclasses
62+ # if they proxy the protocol attribute via a property.
63+ if isinstance (cls .protocol , (Sequence , str )):
64+ key = cls .protocol if isinstance (cls .protocol , str ) else cls .protocol [0 ]
65+ if count or not omit_zero :
66+ out [key ] = count
67+ classes .extend (cls .__subclasses__ ())
68+ return out
69+
70+
71+ @pytest .fixture (scope = "function" , autouse = True )
72+ def instance_caches () -> Generator [InstanceCacheInspector , None , None ]:
73+ """
74+ Fixture to ensure empty filesystem instance caches before and after a test.
75+
76+ Used by default for all tests.
77+ Clears caches of all imported filesystem classes.
78+ Can be used to write test assertions about instance caches.
79+
80+ Usage:
81+
82+ def test_something(instance_caches):
83+ # Test code here
84+ fsspec.open("file://abc")
85+ fsspec.open("memory://foo/bar")
86+
87+ # Test assertion
88+ assert instance_caches.gather_counts() == {"file": 1, "memory": 1}
89+
90+ Returns
91+ -------
92+ instance_caches: An instance cache inspector for clearing and inspecting caches.
93+ """
94+ ic = InstanceCacheInspector ()
95+
96+ ic .clear ()
97+ try :
98+ yield ic
99+ finally :
100+ ic .clear ()
101+
102+
103+ @pytest .fixture (scope = "function" )
31104def ftp_writable (tmpdir ):
32105 """
33106 Fixture providing a writable FTP filesystem.
34107 """
35108 pytest .importorskip ("pyftpdlib" )
36- from fsspec .implementations .ftp import FTPFileSystem
37109
38- FTPFileSystem .clear_instance_cache () # remove lingering connections
39- CachingFileSystem .clear_instance_cache ()
40110 d = str (tmpdir )
41111 with open (os .path .join (d , "out" ), "wb" ) as f :
42112 f .write (b"hello" * 10000 )
0 commit comments