koichi12 commited on
Commit
7d0a722
·
verified ·
1 Parent(s): 581a426

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. tuning-competition-baseline/.venv/lib/python3.11/site-packages/fsspec/archive.py +73 -0
  2. tuning-competition-baseline/.venv/lib/python3.11/site-packages/fsspec/asyn.py +1081 -0
  3. tuning-competition-baseline/.venv/lib/python3.11/site-packages/fsspec/caching.py +875 -0
  4. tuning-competition-baseline/.venv/lib/python3.11/site-packages/fsspec/callbacks.py +324 -0
  5. tuning-competition-baseline/.venv/lib/python3.11/site-packages/fsspec/compression.py +174 -0
  6. tuning-competition-baseline/.venv/lib/python3.11/site-packages/fsspec/config.py +131 -0
  7. tuning-competition-baseline/.venv/lib/python3.11/site-packages/fsspec/core.py +710 -0
  8. tuning-competition-baseline/.venv/lib/python3.11/site-packages/fsspec/dircache.py +98 -0
  9. tuning-competition-baseline/.venv/lib/python3.11/site-packages/fsspec/exceptions.py +17 -0
  10. tuning-competition-baseline/.venv/lib/python3.11/site-packages/fsspec/fuse.py +324 -0
  11. tuning-competition-baseline/.venv/lib/python3.11/site-packages/fsspec/generic.py +403 -0
  12. tuning-competition-baseline/.venv/lib/python3.11/site-packages/fsspec/gui.py +414 -0
  13. tuning-competition-baseline/.venv/lib/python3.11/site-packages/fsspec/parquet.py +549 -0
  14. tuning-competition-baseline/.venv/lib/python3.11/site-packages/fsspec/spec.py +1965 -0
  15. tuning-competition-baseline/.venv/lib/python3.11/site-packages/functorch/__pycache__/__init__.cpython-311.pyc +0 -0
  16. tuning-competition-baseline/.venv/lib/python3.11/site-packages/functorch/_src/__init__.py +0 -0
  17. tuning-competition-baseline/.venv/lib/python3.11/site-packages/functorch/_src/eager_transforms/__init__.py +7 -0
  18. tuning-competition-baseline/.venv/lib/python3.11/site-packages/functorch/_src/eager_transforms/__pycache__/__init__.cpython-311.pyc +0 -0
  19. tuning-competition-baseline/.venv/lib/python3.11/site-packages/functorch/compile/__pycache__/__init__.cpython-311.pyc +0 -0
  20. tuning-competition-baseline/.venv/lib/python3.11/site-packages/functorch/dim/__pycache__/dim.cpython-311.pyc +0 -0
  21. tuning-competition-baseline/.venv/lib/python3.11/site-packages/functorch/dim/dim.py +121 -0
  22. tuning-competition-baseline/.venv/lib/python3.11/site-packages/functorch/dim/op_properties.py +311 -0
  23. tuning-competition-baseline/.venv/lib/python3.11/site-packages/functorch/dim/reference.py +645 -0
  24. tuning-competition-baseline/.venv/lib/python3.11/site-packages/functorch/dim/tree_map.py +14 -0
  25. tuning-competition-baseline/.venv/lib/python3.11/site-packages/functorch/experimental/__pycache__/__init__.cpython-311.pyc +0 -0
  26. tuning-competition-baseline/.venv/lib/python3.11/site-packages/functorch/experimental/ops.py +1 -0
  27. tuning-competition-baseline/.venv/lib/python3.11/site-packages/jinja2/__init__.py +37 -0
  28. tuning-competition-baseline/.venv/lib/python3.11/site-packages/jinja2/__pycache__/_identifier.cpython-311.pyc +0 -0
  29. tuning-competition-baseline/.venv/lib/python3.11/site-packages/jinja2/__pycache__/loaders.cpython-311.pyc +0 -0
  30. tuning-competition-baseline/.venv/lib/python3.11/site-packages/jinja2/__pycache__/runtime.cpython-311.pyc +0 -0
  31. tuning-competition-baseline/.venv/lib/python3.11/site-packages/jinja2/environment.py +1667 -0
  32. tuning-competition-baseline/.venv/lib/python3.11/site-packages/jinja2/loaders.py +661 -0
  33. tuning-competition-baseline/.venv/lib/python3.11/site-packages/jinja2/parser.py +1034 -0
  34. tuning-competition-baseline/.venv/lib/python3.11/site-packages/jinja2/utils.py +755 -0
  35. tuning-competition-baseline/.venv/lib/python3.11/site-packages/nvidia_cufft_cu11-10.9.0.58.dist-info/License.txt +1568 -0
  36. tuning-competition-baseline/.venv/lib/python3.11/site-packages/nvidia_cufft_cu11-10.9.0.58.dist-info/METADATA +35 -0
  37. tuning-competition-baseline/.venv/lib/python3.11/site-packages/nvidia_cufft_cu11-10.9.0.58.dist-info/WHEEL +5 -0
  38. tuning-competition-baseline/.venv/lib/python3.11/site-packages/nvidia_cufft_cu11-10.9.0.58.dist-info/top_level.txt +1 -0
  39. tuning-competition-baseline/.venv/lib/python3.11/site-packages/nvidia_cusolver_cu11-11.4.1.48.dist-info/METADATA +36 -0
  40. tuning-competition-baseline/.venv/lib/python3.11/site-packages/nvidia_cusolver_cu11-11.4.1.48.dist-info/top_level.txt +1 -0
  41. tuning-competition-baseline/.venv/lib/python3.11/site-packages/pip/_internal/commands/__init__.py +132 -0
  42. tuning-competition-baseline/.venv/lib/python3.11/site-packages/pip/_internal/commands/__pycache__/configuration.cpython-311.pyc +0 -0
  43. tuning-competition-baseline/.venv/lib/python3.11/site-packages/pip/_internal/commands/__pycache__/hash.cpython-311.pyc +0 -0
  44. tuning-competition-baseline/.venv/lib/python3.11/site-packages/pip/_internal/commands/__pycache__/help.cpython-311.pyc +0 -0
  45. tuning-competition-baseline/.venv/lib/python3.11/site-packages/pip/_internal/commands/__pycache__/inspect.cpython-311.pyc +0 -0
  46. tuning-competition-baseline/.venv/lib/python3.11/site-packages/pip/_internal/commands/__pycache__/install.cpython-311.pyc +0 -0
  47. tuning-competition-baseline/.venv/lib/python3.11/site-packages/pip/_internal/commands/__pycache__/uninstall.cpython-311.pyc +0 -0
  48. tuning-competition-baseline/.venv/lib/python3.11/site-packages/pip/_internal/commands/check.py +67 -0
  49. tuning-competition-baseline/.venv/lib/python3.11/site-packages/pip/_internal/commands/completion.py +130 -0
  50. tuning-competition-baseline/.venv/lib/python3.11/site-packages/pip/_internal/commands/inspect.py +92 -0
tuning-competition-baseline/.venv/lib/python3.11/site-packages/fsspec/archive.py ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fsspec import AbstractFileSystem
2
+ from fsspec.utils import tokenize
3
+
4
+
5
+ class AbstractArchiveFileSystem(AbstractFileSystem):
6
+ """
7
+ A generic superclass for implementing Archive-based filesystems.
8
+
9
+ Currently, it is shared amongst
10
+ :class:`~fsspec.implementations.zip.ZipFileSystem`,
11
+ :class:`~fsspec.implementations.libarchive.LibArchiveFileSystem` and
12
+ :class:`~fsspec.implementations.tar.TarFileSystem`.
13
+ """
14
+
15
+ def __str__(self):
16
+ return f"<Archive-like object {type(self).__name__} at {id(self)}>"
17
+
18
+ __repr__ = __str__
19
+
20
+ def ukey(self, path):
21
+ return tokenize(path, self.fo, self.protocol)
22
+
23
+ def _all_dirnames(self, paths):
24
+ """Returns *all* directory names for each path in paths, including intermediate
25
+ ones.
26
+
27
+ Parameters
28
+ ----------
29
+ paths: Iterable of path strings
30
+ """
31
+ if len(paths) == 0:
32
+ return set()
33
+
34
+ dirnames = {self._parent(path) for path in paths} - {self.root_marker}
35
+ return dirnames | self._all_dirnames(dirnames)
36
+
37
+ def info(self, path, **kwargs):
38
+ self._get_dirs()
39
+ path = self._strip_protocol(path)
40
+ if path in {"", "/"} and self.dir_cache:
41
+ return {"name": "", "type": "directory", "size": 0}
42
+ if path in self.dir_cache:
43
+ return self.dir_cache[path]
44
+ elif path + "/" in self.dir_cache:
45
+ return self.dir_cache[path + "/"]
46
+ else:
47
+ raise FileNotFoundError(path)
48
+
49
+ def ls(self, path, detail=True, **kwargs):
50
+ self._get_dirs()
51
+ paths = {}
52
+ for p, f in self.dir_cache.items():
53
+ p = p.rstrip("/")
54
+ if "/" in p:
55
+ root = p.rsplit("/", 1)[0]
56
+ else:
57
+ root = ""
58
+ if root == path.rstrip("/"):
59
+ paths[p] = f
60
+ elif all(
61
+ (a == b)
62
+ for a, b in zip(path.split("/"), [""] + p.strip("/").split("/"))
63
+ ):
64
+ # root directory entry
65
+ ppath = p.rstrip("/").split("/", 1)[0]
66
+ if ppath not in paths:
67
+ out = {"name": ppath, "size": 0, "type": "directory"}
68
+ paths[ppath] = out
69
+ if detail:
70
+ out = sorted(paths.values(), key=lambda _: _["name"])
71
+ return out
72
+ else:
73
+ return sorted(paths)
tuning-competition-baseline/.venv/lib/python3.11/site-packages/fsspec/asyn.py ADDED
@@ -0,0 +1,1081 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ import asyncio.events
3
+ import functools
4
+ import inspect
5
+ import io
6
+ import numbers
7
+ import os
8
+ import re
9
+ import threading
10
+ from contextlib import contextmanager
11
+ from glob import has_magic
12
+ from typing import TYPE_CHECKING, Iterable
13
+
14
+ from .callbacks import DEFAULT_CALLBACK
15
+ from .exceptions import FSTimeoutError
16
+ from .implementations.local import LocalFileSystem, make_path_posix, trailing_sep
17
+ from .spec import AbstractBufferedFile, AbstractFileSystem
18
+ from .utils import glob_translate, is_exception, other_paths
19
+
20
+ private = re.compile("_[^_]")
21
+ iothread = [None] # dedicated fsspec IO thread
22
+ loop = [None] # global event loop for any non-async instance
23
+ _lock = None # global lock placeholder
24
+ get_running_loop = asyncio.get_running_loop
25
+
26
+
27
+ def get_lock():
28
+ """Allocate or return a threading lock.
29
+
30
+ The lock is allocated on first use to allow setting one lock per forked process.
31
+ """
32
+ global _lock
33
+ if not _lock:
34
+ _lock = threading.Lock()
35
+ return _lock
36
+
37
+
38
+ def reset_lock():
39
+ """Reset the global lock.
40
+
41
+ This should be called only on the init of a forked process to reset the lock to
42
+ None, enabling the new forked process to get a new lock.
43
+ """
44
+ global _lock
45
+
46
+ iothread[0] = None
47
+ loop[0] = None
48
+ _lock = None
49
+
50
+
51
+ async def _runner(event, coro, result, timeout=None):
52
+ timeout = timeout if timeout else None # convert 0 or 0.0 to None
53
+ if timeout is not None:
54
+ coro = asyncio.wait_for(coro, timeout=timeout)
55
+ try:
56
+ result[0] = await coro
57
+ except Exception as ex:
58
+ result[0] = ex
59
+ finally:
60
+ event.set()
61
+
62
+
63
+ def sync(loop, func, *args, timeout=None, **kwargs):
64
+ """
65
+ Make loop run coroutine until it returns. Runs in other thread
66
+
67
+ Examples
68
+ --------
69
+ >>> fsspec.asyn.sync(fsspec.asyn.get_loop(), func, *args,
70
+ timeout=timeout, **kwargs)
71
+ """
72
+ timeout = timeout if timeout else None # convert 0 or 0.0 to None
73
+ # NB: if the loop is not running *yet*, it is OK to submit work
74
+ # and we will wait for it
75
+ if loop is None or loop.is_closed():
76
+ raise RuntimeError("Loop is not running")
77
+ try:
78
+ loop0 = asyncio.events.get_running_loop()
79
+ if loop0 is loop:
80
+ raise NotImplementedError("Calling sync() from within a running loop")
81
+ except NotImplementedError:
82
+ raise
83
+ except RuntimeError:
84
+ pass
85
+ coro = func(*args, **kwargs)
86
+ result = [None]
87
+ event = threading.Event()
88
+ asyncio.run_coroutine_threadsafe(_runner(event, coro, result, timeout), loop)
89
+ while True:
90
+ # this loops allows thread to get interrupted
91
+ if event.wait(1):
92
+ break
93
+ if timeout is not None:
94
+ timeout -= 1
95
+ if timeout < 0:
96
+ raise FSTimeoutError
97
+
98
+ return_result = result[0]
99
+ if isinstance(return_result, asyncio.TimeoutError):
100
+ # suppress asyncio.TimeoutError, raise FSTimeoutError
101
+ raise FSTimeoutError from return_result
102
+ elif isinstance(return_result, BaseException):
103
+ raise return_result
104
+ else:
105
+ return return_result
106
+
107
+
108
+ def sync_wrapper(func, obj=None):
109
+ """Given a function, make so can be called in blocking contexts
110
+
111
+ Leave obj=None if defining within a class. Pass the instance if attaching
112
+ as an attribute of the instance.
113
+ """
114
+
115
+ @functools.wraps(func)
116
+ def wrapper(*args, **kwargs):
117
+ self = obj or args[0]
118
+ return sync(self.loop, func, *args, **kwargs)
119
+
120
+ return wrapper
121
+
122
+
123
+ @contextmanager
124
+ def _selector_policy():
125
+ original_policy = asyncio.get_event_loop_policy()
126
+ try:
127
+ if os.name == "nt" and hasattr(asyncio, "WindowsSelectorEventLoopPolicy"):
128
+ asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
129
+
130
+ yield
131
+ finally:
132
+ asyncio.set_event_loop_policy(original_policy)
133
+
134
+
135
+ def get_loop():
136
+ """Create or return the default fsspec IO loop
137
+
138
+ The loop will be running on a separate thread.
139
+ """
140
+ if loop[0] is None:
141
+ with get_lock():
142
+ # repeat the check just in case the loop got filled between the
143
+ # previous two calls from another thread
144
+ if loop[0] is None:
145
+ with _selector_policy():
146
+ loop[0] = asyncio.new_event_loop()
147
+ th = threading.Thread(target=loop[0].run_forever, name="fsspecIO")
148
+ th.daemon = True
149
+ th.start()
150
+ iothread[0] = th
151
+ return loop[0]
152
+
153
+
154
+ if TYPE_CHECKING:
155
+ import resource
156
+
157
+ ResourceError = resource.error
158
+ else:
159
+ try:
160
+ import resource
161
+ except ImportError:
162
+ resource = None
163
+ ResourceError = OSError
164
+ else:
165
+ ResourceError = getattr(resource, "error", OSError)
166
+
167
+ _DEFAULT_BATCH_SIZE = 128
168
+ _NOFILES_DEFAULT_BATCH_SIZE = 1280
169
+
170
+
171
+ def _get_batch_size(nofiles=False):
172
+ from fsspec.config import conf
173
+
174
+ if nofiles:
175
+ if "nofiles_gather_batch_size" in conf:
176
+ return conf["nofiles_gather_batch_size"]
177
+ else:
178
+ if "gather_batch_size" in conf:
179
+ return conf["gather_batch_size"]
180
+ if nofiles:
181
+ return _NOFILES_DEFAULT_BATCH_SIZE
182
+ if resource is None:
183
+ return _DEFAULT_BATCH_SIZE
184
+
185
+ try:
186
+ soft_limit, _ = resource.getrlimit(resource.RLIMIT_NOFILE)
187
+ except (ImportError, ValueError, ResourceError):
188
+ return _DEFAULT_BATCH_SIZE
189
+
190
+ if soft_limit == resource.RLIM_INFINITY:
191
+ return -1
192
+ else:
193
+ return soft_limit // 8
194
+
195
+
196
+ def running_async() -> bool:
197
+ """Being executed by an event loop?"""
198
+ try:
199
+ asyncio.get_running_loop()
200
+ return True
201
+ except RuntimeError:
202
+ return False
203
+
204
+
205
+ async def _run_coros_in_chunks(
206
+ coros,
207
+ batch_size=None,
208
+ callback=DEFAULT_CALLBACK,
209
+ timeout=None,
210
+ return_exceptions=False,
211
+ nofiles=False,
212
+ ):
213
+ """Run the given coroutines in chunks.
214
+
215
+ Parameters
216
+ ----------
217
+ coros: list of coroutines to run
218
+ batch_size: int or None
219
+ Number of coroutines to submit/wait on simultaneously.
220
+ If -1, then it will not be any throttling. If
221
+ None, it will be inferred from _get_batch_size()
222
+ callback: fsspec.callbacks.Callback instance
223
+ Gets a relative_update when each coroutine completes
224
+ timeout: number or None
225
+ If given, each coroutine times out after this time. Note that, since
226
+ there are multiple batches, the total run time of this function will in
227
+ general be longer
228
+ return_exceptions: bool
229
+ Same meaning as in asyncio.gather
230
+ nofiles: bool
231
+ If inferring the batch_size, does this operation involve local files?
232
+ If yes, you normally expect smaller batches.
233
+ """
234
+
235
+ if batch_size is None:
236
+ batch_size = _get_batch_size(nofiles=nofiles)
237
+
238
+ if batch_size == -1:
239
+ batch_size = len(coros)
240
+
241
+ assert batch_size > 0
242
+ results = []
243
+ for start in range(0, len(coros), batch_size):
244
+ chunk = [
245
+ asyncio.Task(asyncio.wait_for(c, timeout=timeout))
246
+ for c in coros[start : start + batch_size]
247
+ ]
248
+ if callback is not DEFAULT_CALLBACK:
249
+ [
250
+ t.add_done_callback(lambda *_, **__: callback.relative_update(1))
251
+ for t in chunk
252
+ ]
253
+ results.extend(
254
+ await asyncio.gather(*chunk, return_exceptions=return_exceptions),
255
+ )
256
+ return results
257
+
258
+
259
+ # these methods should be implemented as async by any async-able backend
260
+ async_methods = [
261
+ "_ls",
262
+ "_cat_file",
263
+ "_get_file",
264
+ "_put_file",
265
+ "_rm_file",
266
+ "_cp_file",
267
+ "_pipe_file",
268
+ "_expand_path",
269
+ "_info",
270
+ "_isfile",
271
+ "_isdir",
272
+ "_exists",
273
+ "_walk",
274
+ "_glob",
275
+ "_find",
276
+ "_du",
277
+ "_size",
278
+ "_mkdir",
279
+ "_makedirs",
280
+ ]
281
+
282
+
283
+ class AsyncFileSystem(AbstractFileSystem):
284
+ """Async file operations, default implementations
285
+
286
+ Passes bulk operations to asyncio.gather for concurrent operation.
287
+
288
+ Implementations that have concurrent batch operations and/or async methods
289
+ should inherit from this class instead of AbstractFileSystem. Docstrings are
290
+ copied from the un-underscored method in AbstractFileSystem, if not given.
291
+ """
292
+
293
+ # note that methods do not have docstring here; they will be copied
294
+ # for _* methods and inferred for overridden methods.
295
+
296
+ async_impl = True
297
+ mirror_sync_methods = True
298
+ disable_throttling = False
299
+
300
+ def __init__(self, *args, asynchronous=False, loop=None, batch_size=None, **kwargs):
301
+ self.asynchronous = asynchronous
302
+ self._pid = os.getpid()
303
+ if not asynchronous:
304
+ self._loop = loop or get_loop()
305
+ else:
306
+ self._loop = None
307
+ self.batch_size = batch_size
308
+ super().__init__(*args, **kwargs)
309
+
310
+ @property
311
+ def loop(self):
312
+ if self._pid != os.getpid():
313
+ raise RuntimeError("This class is not fork-safe")
314
+ return self._loop
315
+
316
+ async def _rm_file(self, path, **kwargs):
317
+ raise NotImplementedError
318
+
319
+ async def _rm(self, path, recursive=False, batch_size=None, **kwargs):
320
+ # TODO: implement on_error
321
+ batch_size = batch_size or self.batch_size
322
+ path = await self._expand_path(path, recursive=recursive)
323
+ return await _run_coros_in_chunks(
324
+ [self._rm_file(p, **kwargs) for p in reversed(path)],
325
+ batch_size=batch_size,
326
+ nofiles=True,
327
+ )
328
+
329
+ async def _cp_file(self, path1, path2, **kwargs):
330
+ raise NotImplementedError
331
+
332
+ async def _copy(
333
+ self,
334
+ path1,
335
+ path2,
336
+ recursive=False,
337
+ on_error=None,
338
+ maxdepth=None,
339
+ batch_size=None,
340
+ **kwargs,
341
+ ):
342
+ if on_error is None and recursive:
343
+ on_error = "ignore"
344
+ elif on_error is None:
345
+ on_error = "raise"
346
+
347
+ if isinstance(path1, list) and isinstance(path2, list):
348
+ # No need to expand paths when both source and destination
349
+ # are provided as lists
350
+ paths1 = path1
351
+ paths2 = path2
352
+ else:
353
+ source_is_str = isinstance(path1, str)
354
+ paths1 = await self._expand_path(
355
+ path1, maxdepth=maxdepth, recursive=recursive
356
+ )
357
+ if source_is_str and (not recursive or maxdepth is not None):
358
+ # Non-recursive glob does not copy directories
359
+ paths1 = [
360
+ p for p in paths1 if not (trailing_sep(p) or await self._isdir(p))
361
+ ]
362
+ if not paths1:
363
+ return
364
+
365
+ source_is_file = len(paths1) == 1
366
+ dest_is_dir = isinstance(path2, str) and (
367
+ trailing_sep(path2) or await self._isdir(path2)
368
+ )
369
+
370
+ exists = source_is_str and (
371
+ (has_magic(path1) and source_is_file)
372
+ or (not has_magic(path1) and dest_is_dir and not trailing_sep(path1))
373
+ )
374
+ paths2 = other_paths(
375
+ paths1,
376
+ path2,
377
+ exists=exists,
378
+ flatten=not source_is_str,
379
+ )
380
+
381
+ batch_size = batch_size or self.batch_size
382
+ coros = [self._cp_file(p1, p2, **kwargs) for p1, p2 in zip(paths1, paths2)]
383
+ result = await _run_coros_in_chunks(
384
+ coros, batch_size=batch_size, return_exceptions=True, nofiles=True
385
+ )
386
+
387
+ for ex in filter(is_exception, result):
388
+ if on_error == "ignore" and isinstance(ex, FileNotFoundError):
389
+ continue
390
+ raise ex
391
+
392
+ async def _pipe_file(self, path, value, **kwargs):
393
+ raise NotImplementedError
394
+
395
+ async def _pipe(self, path, value=None, batch_size=None, **kwargs):
396
+ if isinstance(path, str):
397
+ path = {path: value}
398
+ batch_size = batch_size or self.batch_size
399
+ return await _run_coros_in_chunks(
400
+ [self._pipe_file(k, v, **kwargs) for k, v in path.items()],
401
+ batch_size=batch_size,
402
+ nofiles=True,
403
+ )
404
+
405
+ async def _process_limits(self, url, start, end):
406
+ """Helper for "Range"-based _cat_file"""
407
+ size = None
408
+ suff = False
409
+ if start is not None and start < 0:
410
+ # if start is negative and end None, end is the "suffix length"
411
+ if end is None:
412
+ end = -start
413
+ start = ""
414
+ suff = True
415
+ else:
416
+ size = size or (await self._info(url))["size"]
417
+ start = size + start
418
+ elif start is None:
419
+ start = 0
420
+ if not suff:
421
+ if end is not None and end < 0:
422
+ if start is not None:
423
+ size = size or (await self._info(url))["size"]
424
+ end = size + end
425
+ elif end is None:
426
+ end = ""
427
+ if isinstance(end, numbers.Integral):
428
+ end -= 1 # bytes range is inclusive
429
+ return f"bytes={start}-{end}"
430
+
431
+ async def _cat_file(self, path, start=None, end=None, **kwargs):
432
+ raise NotImplementedError
433
+
434
+ async def _cat(
435
+ self, path, recursive=False, on_error="raise", batch_size=None, **kwargs
436
+ ):
437
+ paths = await self._expand_path(path, recursive=recursive)
438
+ coros = [self._cat_file(path, **kwargs) for path in paths]
439
+ batch_size = batch_size or self.batch_size
440
+ out = await _run_coros_in_chunks(
441
+ coros, batch_size=batch_size, nofiles=True, return_exceptions=True
442
+ )
443
+ if on_error == "raise":
444
+ ex = next(filter(is_exception, out), False)
445
+ if ex:
446
+ raise ex
447
+ if (
448
+ len(paths) > 1
449
+ or isinstance(path, list)
450
+ or paths[0] != self._strip_protocol(path)
451
+ ):
452
+ return {
453
+ k: v
454
+ for k, v in zip(paths, out)
455
+ if on_error != "omit" or not is_exception(v)
456
+ }
457
+ else:
458
+ return out[0]
459
+
460
+ async def _cat_ranges(
461
+ self,
462
+ paths,
463
+ starts,
464
+ ends,
465
+ max_gap=None,
466
+ batch_size=None,
467
+ on_error="return",
468
+ **kwargs,
469
+ ):
470
+ """Get the contents of byte ranges from one or more files
471
+
472
+ Parameters
473
+ ----------
474
+ paths: list
475
+ A list of of filepaths on this filesystems
476
+ starts, ends: int or list
477
+ Bytes limits of the read. If using a single int, the same value will be
478
+ used to read all the specified files.
479
+ """
480
+ # TODO: on_error
481
+ if max_gap is not None:
482
+ # use utils.merge_offset_ranges
483
+ raise NotImplementedError
484
+ if not isinstance(paths, list):
485
+ raise TypeError
486
+ if not isinstance(starts, Iterable):
487
+ starts = [starts] * len(paths)
488
+ if not isinstance(ends, Iterable):
489
+ ends = [ends] * len(paths)
490
+ if len(starts) != len(paths) or len(ends) != len(paths):
491
+ raise ValueError
492
+ coros = [
493
+ self._cat_file(p, start=s, end=e, **kwargs)
494
+ for p, s, e in zip(paths, starts, ends)
495
+ ]
496
+ batch_size = batch_size or self.batch_size
497
+ return await _run_coros_in_chunks(
498
+ coros, batch_size=batch_size, nofiles=True, return_exceptions=True
499
+ )
500
+
501
+ async def _put_file(self, lpath, rpath, **kwargs):
502
+ raise NotImplementedError
503
+
504
+ async def _put(
505
+ self,
506
+ lpath,
507
+ rpath,
508
+ recursive=False,
509
+ callback=DEFAULT_CALLBACK,
510
+ batch_size=None,
511
+ maxdepth=None,
512
+ **kwargs,
513
+ ):
514
+ """Copy file(s) from local.
515
+
516
+ Copies a specific file or tree of files (if recursive=True). If rpath
517
+ ends with a "/", it will be assumed to be a directory, and target files
518
+ will go within.
519
+
520
+ The put_file method will be called concurrently on a batch of files. The
521
+ batch_size option can configure the amount of futures that can be executed
522
+ at the same time. If it is -1, then all the files will be uploaded concurrently.
523
+ The default can be set for this instance by passing "batch_size" in the
524
+ constructor, or for all instances by setting the "gather_batch_size" key
525
+ in ``fsspec.config.conf``, falling back to 1/8th of the system limit .
526
+ """
527
+ if isinstance(lpath, list) and isinstance(rpath, list):
528
+ # No need to expand paths when both source and destination
529
+ # are provided as lists
530
+ rpaths = rpath
531
+ lpaths = lpath
532
+ else:
533
+ source_is_str = isinstance(lpath, str)
534
+ if source_is_str:
535
+ lpath = make_path_posix(lpath)
536
+ fs = LocalFileSystem()
537
+ lpaths = fs.expand_path(lpath, recursive=recursive, maxdepth=maxdepth)
538
+ if source_is_str and (not recursive or maxdepth is not None):
539
+ # Non-recursive glob does not copy directories
540
+ lpaths = [p for p in lpaths if not (trailing_sep(p) or fs.isdir(p))]
541
+ if not lpaths:
542
+ return
543
+
544
+ source_is_file = len(lpaths) == 1
545
+ dest_is_dir = isinstance(rpath, str) and (
546
+ trailing_sep(rpath) or await self._isdir(rpath)
547
+ )
548
+
549
+ rpath = self._strip_protocol(rpath)
550
+ exists = source_is_str and (
551
+ (has_magic(lpath) and source_is_file)
552
+ or (not has_magic(lpath) and dest_is_dir and not trailing_sep(lpath))
553
+ )
554
+ rpaths = other_paths(
555
+ lpaths,
556
+ rpath,
557
+ exists=exists,
558
+ flatten=not source_is_str,
559
+ )
560
+
561
+ is_dir = {l: os.path.isdir(l) for l in lpaths}
562
+ rdirs = [r for l, r in zip(lpaths, rpaths) if is_dir[l]]
563
+ file_pairs = [(l, r) for l, r in zip(lpaths, rpaths) if not is_dir[l]]
564
+
565
+ await asyncio.gather(*[self._makedirs(d, exist_ok=True) for d in rdirs])
566
+ batch_size = batch_size or self.batch_size
567
+
568
+ coros = []
569
+ callback.set_size(len(file_pairs))
570
+ for lfile, rfile in file_pairs:
571
+ put_file = callback.branch_coro(self._put_file)
572
+ coros.append(put_file(lfile, rfile, **kwargs))
573
+
574
+ return await _run_coros_in_chunks(
575
+ coros, batch_size=batch_size, callback=callback
576
+ )
577
+
578
+ async def _get_file(self, rpath, lpath, **kwargs):
579
+ raise NotImplementedError
580
+
581
+ async def _get(
582
+ self,
583
+ rpath,
584
+ lpath,
585
+ recursive=False,
586
+ callback=DEFAULT_CALLBACK,
587
+ maxdepth=None,
588
+ **kwargs,
589
+ ):
590
+ """Copy file(s) to local.
591
+
592
+ Copies a specific file or tree of files (if recursive=True). If lpath
593
+ ends with a "/", it will be assumed to be a directory, and target files
594
+ will go within. Can submit a list of paths, which may be glob-patterns
595
+ and will be expanded.
596
+
597
+ The get_file method will be called concurrently on a batch of files. The
598
+ batch_size option can configure the amount of futures that can be executed
599
+ at the same time. If it is -1, then all the files will be uploaded concurrently.
600
+ The default can be set for this instance by passing "batch_size" in the
601
+ constructor, or for all instances by setting the "gather_batch_size" key
602
+ in ``fsspec.config.conf``, falling back to 1/8th of the system limit .
603
+ """
604
+ if isinstance(lpath, list) and isinstance(rpath, list):
605
+ # No need to expand paths when both source and destination
606
+ # are provided as lists
607
+ rpaths = rpath
608
+ lpaths = lpath
609
+ else:
610
+ source_is_str = isinstance(rpath, str)
611
+ # First check for rpath trailing slash as _strip_protocol removes it.
612
+ source_not_trailing_sep = source_is_str and not trailing_sep(rpath)
613
+ rpath = self._strip_protocol(rpath)
614
+ rpaths = await self._expand_path(
615
+ rpath, recursive=recursive, maxdepth=maxdepth
616
+ )
617
+ if source_is_str and (not recursive or maxdepth is not None):
618
+ # Non-recursive glob does not copy directories
619
+ rpaths = [
620
+ p for p in rpaths if not (trailing_sep(p) or await self._isdir(p))
621
+ ]
622
+ if not rpaths:
623
+ return
624
+
625
+ lpath = make_path_posix(lpath)
626
+ source_is_file = len(rpaths) == 1
627
+ dest_is_dir = isinstance(lpath, str) and (
628
+ trailing_sep(lpath) or LocalFileSystem().isdir(lpath)
629
+ )
630
+
631
+ exists = source_is_str and (
632
+ (has_magic(rpath) and source_is_file)
633
+ or (not has_magic(rpath) and dest_is_dir and source_not_trailing_sep)
634
+ )
635
+ lpaths = other_paths(
636
+ rpaths,
637
+ lpath,
638
+ exists=exists,
639
+ flatten=not source_is_str,
640
+ )
641
+
642
+ [os.makedirs(os.path.dirname(lp), exist_ok=True) for lp in lpaths]
643
+ batch_size = kwargs.pop("batch_size", self.batch_size)
644
+
645
+ coros = []
646
+ callback.set_size(len(lpaths))
647
+ for lpath, rpath in zip(lpaths, rpaths):
648
+ get_file = callback.branch_coro(self._get_file)
649
+ coros.append(get_file(rpath, lpath, **kwargs))
650
+ return await _run_coros_in_chunks(
651
+ coros, batch_size=batch_size, callback=callback
652
+ )
653
+
654
+ async def _isfile(self, path):
655
+ try:
656
+ return (await self._info(path))["type"] == "file"
657
+ except: # noqa: E722
658
+ return False
659
+
660
+ async def _isdir(self, path):
661
+ try:
662
+ return (await self._info(path))["type"] == "directory"
663
+ except OSError:
664
+ return False
665
+
666
+ async def _size(self, path):
667
+ return (await self._info(path)).get("size", None)
668
+
669
+ async def _sizes(self, paths, batch_size=None):
670
+ batch_size = batch_size or self.batch_size
671
+ return await _run_coros_in_chunks(
672
+ [self._size(p) for p in paths], batch_size=batch_size
673
+ )
674
+
675
+ async def _exists(self, path, **kwargs):
676
+ try:
677
+ await self._info(path, **kwargs)
678
+ return True
679
+ except FileNotFoundError:
680
+ return False
681
+
682
+ async def _info(self, path, **kwargs):
683
+ raise NotImplementedError
684
+
685
+ async def _ls(self, path, detail=True, **kwargs):
686
+ raise NotImplementedError
687
+
688
+ async def _walk(self, path, maxdepth=None, on_error="omit", **kwargs):
689
+ if maxdepth is not None and maxdepth < 1:
690
+ raise ValueError("maxdepth must be at least 1")
691
+
692
+ path = self._strip_protocol(path)
693
+ full_dirs = {}
694
+ dirs = {}
695
+ files = {}
696
+
697
+ detail = kwargs.pop("detail", False)
698
+ try:
699
+ listing = await self._ls(path, detail=True, **kwargs)
700
+ except (FileNotFoundError, OSError) as e:
701
+ if on_error == "raise":
702
+ raise
703
+ elif callable(on_error):
704
+ on_error(e)
705
+ if detail:
706
+ yield path, {}, {}
707
+ else:
708
+ yield path, [], []
709
+ return
710
+
711
+ for info in listing:
712
+ # each info name must be at least [path]/part , but here
713
+ # we check also for names like [path]/part/
714
+ pathname = info["name"].rstrip("/")
715
+ name = pathname.rsplit("/", 1)[-1]
716
+ if info["type"] == "directory" and pathname != path:
717
+ # do not include "self" path
718
+ full_dirs[name] = pathname
719
+ dirs[name] = info
720
+ elif pathname == path:
721
+ # file-like with same name as give path
722
+ files[""] = info
723
+ else:
724
+ files[name] = info
725
+
726
+ if detail:
727
+ yield path, dirs, files
728
+ else:
729
+ yield path, list(dirs), list(files)
730
+
731
+ if maxdepth is not None:
732
+ maxdepth -= 1
733
+ if maxdepth < 1:
734
+ return
735
+
736
+ for d in dirs:
737
+ async for _ in self._walk(
738
+ full_dirs[d], maxdepth=maxdepth, detail=detail, **kwargs
739
+ ):
740
+ yield _
741
+
742
+ async def _glob(self, path, maxdepth=None, **kwargs):
743
+ if maxdepth is not None and maxdepth < 1:
744
+ raise ValueError("maxdepth must be at least 1")
745
+
746
+ import re
747
+
748
+ seps = (os.path.sep, os.path.altsep) if os.path.altsep else (os.path.sep,)
749
+ ends_with_sep = path.endswith(seps) # _strip_protocol strips trailing slash
750
+ path = self._strip_protocol(path)
751
+ append_slash_to_dirname = ends_with_sep or path.endswith(
752
+ tuple(sep + "**" for sep in seps)
753
+ )
754
+ idx_star = path.find("*") if path.find("*") >= 0 else len(path)
755
+ idx_qmark = path.find("?") if path.find("?") >= 0 else len(path)
756
+ idx_brace = path.find("[") if path.find("[") >= 0 else len(path)
757
+
758
+ min_idx = min(idx_star, idx_qmark, idx_brace)
759
+
760
+ detail = kwargs.pop("detail", False)
761
+
762
+ if not has_magic(path):
763
+ if await self._exists(path, **kwargs):
764
+ if not detail:
765
+ return [path]
766
+ else:
767
+ return {path: await self._info(path, **kwargs)}
768
+ else:
769
+ if not detail:
770
+ return [] # glob of non-existent returns empty
771
+ else:
772
+ return {}
773
+ elif "/" in path[:min_idx]:
774
+ min_idx = path[:min_idx].rindex("/")
775
+ root = path[: min_idx + 1]
776
+ depth = path[min_idx + 1 :].count("/") + 1
777
+ else:
778
+ root = ""
779
+ depth = path[min_idx + 1 :].count("/") + 1
780
+
781
+ if "**" in path:
782
+ if maxdepth is not None:
783
+ idx_double_stars = path.find("**")
784
+ depth_double_stars = path[idx_double_stars:].count("/") + 1
785
+ depth = depth - depth_double_stars + maxdepth
786
+ else:
787
+ depth = None
788
+
789
+ allpaths = await self._find(
790
+ root, maxdepth=depth, withdirs=True, detail=True, **kwargs
791
+ )
792
+
793
+ pattern = glob_translate(path + ("/" if ends_with_sep else ""))
794
+ pattern = re.compile(pattern)
795
+
796
+ out = {
797
+ p: info
798
+ for p, info in sorted(allpaths.items())
799
+ if pattern.match(
800
+ (
801
+ p + "/"
802
+ if append_slash_to_dirname and info["type"] == "directory"
803
+ else p
804
+ )
805
+ )
806
+ }
807
+
808
+ if detail:
809
+ return out
810
+ else:
811
+ return list(out)
812
+
813
+ async def _du(self, path, total=True, maxdepth=None, **kwargs):
814
+ sizes = {}
815
+ # async for?
816
+ for f in await self._find(path, maxdepth=maxdepth, **kwargs):
817
+ info = await self._info(f)
818
+ sizes[info["name"]] = info["size"]
819
+ if total:
820
+ return sum(sizes.values())
821
+ else:
822
+ return sizes
823
+
824
+ async def _find(self, path, maxdepth=None, withdirs=False, **kwargs):
825
+ path = self._strip_protocol(path)
826
+ out = {}
827
+ detail = kwargs.pop("detail", False)
828
+
829
+ # Add the root directory if withdirs is requested
830
+ # This is needed for posix glob compliance
831
+ if withdirs and path != "" and await self._isdir(path):
832
+ out[path] = await self._info(path)
833
+
834
+ # async for?
835
+ async for _, dirs, files in self._walk(path, maxdepth, detail=True, **kwargs):
836
+ if withdirs:
837
+ files.update(dirs)
838
+ out.update({info["name"]: info for name, info in files.items()})
839
+ if not out and (await self._isfile(path)):
840
+ # walk works on directories, but find should also return [path]
841
+ # when path happens to be a file
842
+ out[path] = {}
843
+ names = sorted(out)
844
+ if not detail:
845
+ return names
846
+ else:
847
+ return {name: out[name] for name in names}
848
+
849
+ async def _expand_path(self, path, recursive=False, maxdepth=None):
850
+ if maxdepth is not None and maxdepth < 1:
851
+ raise ValueError("maxdepth must be at least 1")
852
+
853
+ if isinstance(path, str):
854
+ out = await self._expand_path([path], recursive, maxdepth)
855
+ else:
856
+ out = set()
857
+ path = [self._strip_protocol(p) for p in path]
858
+ for p in path: # can gather here
859
+ if has_magic(p):
860
+ bit = set(await self._glob(p, maxdepth=maxdepth))
861
+ out |= bit
862
+ if recursive:
863
+ # glob call above expanded one depth so if maxdepth is defined
864
+ # then decrement it in expand_path call below. If it is zero
865
+ # after decrementing then avoid expand_path call.
866
+ if maxdepth is not None and maxdepth <= 1:
867
+ continue
868
+ out |= set(
869
+ await self._expand_path(
870
+ list(bit),
871
+ recursive=recursive,
872
+ maxdepth=maxdepth - 1 if maxdepth is not None else None,
873
+ )
874
+ )
875
+ continue
876
+ elif recursive:
877
+ rec = set(await self._find(p, maxdepth=maxdepth, withdirs=True))
878
+ out |= rec
879
+ if p not in out and (recursive is False or (await self._exists(p))):
880
+ # should only check once, for the root
881
+ out.add(p)
882
+ if not out:
883
+ raise FileNotFoundError(path)
884
+ return sorted(out)
885
+
886
+ async def _mkdir(self, path, create_parents=True, **kwargs):
887
+ pass # not necessary to implement, may not have directories
888
+
889
+ async def _makedirs(self, path, exist_ok=False):
890
+ pass # not necessary to implement, may not have directories
891
+
892
+ async def open_async(self, path, mode="rb", **kwargs):
893
+ if "b" not in mode or kwargs.get("compression"):
894
+ raise ValueError
895
+ raise NotImplementedError
896
+
897
+
898
+ def mirror_sync_methods(obj):
899
+ """Populate sync and async methods for obj
900
+
901
+ For each method will create a sync version if the name refers to an async method
902
+ (coroutine) and there is no override in the child class; will create an async
903
+ method for the corresponding sync method if there is no implementation.
904
+
905
+ Uses the methods specified in
906
+ - async_methods: the set that an implementation is expected to provide
907
+ - default_async_methods: that can be derived from their sync version in
908
+ AbstractFileSystem
909
+ - AsyncFileSystem: async-specific default coroutines
910
+ """
911
+ from fsspec import AbstractFileSystem
912
+
913
+ for method in async_methods + dir(AsyncFileSystem):
914
+ if not method.startswith("_"):
915
+ continue
916
+ smethod = method[1:]
917
+ if private.match(method):
918
+ isco = inspect.iscoroutinefunction(getattr(obj, method, None))
919
+ unsync = getattr(getattr(obj, smethod, False), "__func__", None)
920
+ is_default = unsync is getattr(AbstractFileSystem, smethod, "")
921
+ if isco and is_default:
922
+ mth = sync_wrapper(getattr(obj, method), obj=obj)
923
+ setattr(obj, smethod, mth)
924
+ if not mth.__doc__:
925
+ mth.__doc__ = getattr(
926
+ getattr(AbstractFileSystem, smethod, None), "__doc__", ""
927
+ )
928
+
929
+
930
+ class FSSpecCoroutineCancel(Exception):
931
+ pass
932
+
933
+
934
+ def _dump_running_tasks(
935
+ printout=True, cancel=True, exc=FSSpecCoroutineCancel, with_task=False
936
+ ):
937
+ import traceback
938
+
939
+ tasks = [t for t in asyncio.tasks.all_tasks(loop[0]) if not t.done()]
940
+ if printout:
941
+ [task.print_stack() for task in tasks]
942
+ out = [
943
+ {
944
+ "locals": task._coro.cr_frame.f_locals,
945
+ "file": task._coro.cr_frame.f_code.co_filename,
946
+ "firstline": task._coro.cr_frame.f_code.co_firstlineno,
947
+ "linelo": task._coro.cr_frame.f_lineno,
948
+ "stack": traceback.format_stack(task._coro.cr_frame),
949
+ "task": task if with_task else None,
950
+ }
951
+ for task in tasks
952
+ ]
953
+ if cancel:
954
+ for t in tasks:
955
+ cbs = t._callbacks
956
+ t.cancel()
957
+ asyncio.futures.Future.set_exception(t, exc)
958
+ asyncio.futures.Future.cancel(t)
959
+ [cb[0](t) for cb in cbs] # cancels any dependent concurrent.futures
960
+ try:
961
+ t._coro.throw(exc) # exits coro, unless explicitly handled
962
+ except exc:
963
+ pass
964
+ return out
965
+
966
+
967
+ class AbstractAsyncStreamedFile(AbstractBufferedFile):
968
+ # no read buffering, and always auto-commit
969
+ # TODO: readahead might still be useful here, but needs async version
970
+
971
+ async def read(self, length=-1):
972
+ """
973
+ Return data from cache, or fetch pieces as necessary
974
+
975
+ Parameters
976
+ ----------
977
+ length: int (-1)
978
+ Number of bytes to read; if <0, all remaining bytes.
979
+ """
980
+ length = -1 if length is None else int(length)
981
+ if self.mode != "rb":
982
+ raise ValueError("File not in read mode")
983
+ if length < 0:
984
+ length = self.size - self.loc
985
+ if self.closed:
986
+ raise ValueError("I/O operation on closed file.")
987
+ if length == 0:
988
+ # don't even bother calling fetch
989
+ return b""
990
+ out = await self._fetch_range(self.loc, self.loc + length)
991
+ self.loc += len(out)
992
+ return out
993
+
994
+ async def write(self, data):
995
+ """
996
+ Write data to buffer.
997
+
998
+ Buffer only sent on flush() or if buffer is greater than
999
+ or equal to blocksize.
1000
+
1001
+ Parameters
1002
+ ----------
1003
+ data: bytes
1004
+ Set of bytes to be written.
1005
+ """
1006
+ if self.mode not in {"wb", "ab"}:
1007
+ raise ValueError("File not in write mode")
1008
+ if self.closed:
1009
+ raise ValueError("I/O operation on closed file.")
1010
+ if self.forced:
1011
+ raise ValueError("This file has been force-flushed, can only close")
1012
+ out = self.buffer.write(data)
1013
+ self.loc += out
1014
+ if self.buffer.tell() >= self.blocksize:
1015
+ await self.flush()
1016
+ return out
1017
+
1018
+ async def close(self):
1019
+ """Close file
1020
+
1021
+ Finalizes writes, discards cache
1022
+ """
1023
+ if getattr(self, "_unclosable", False):
1024
+ return
1025
+ if self.closed:
1026
+ return
1027
+ if self.mode == "rb":
1028
+ self.cache = None
1029
+ else:
1030
+ if not self.forced:
1031
+ await self.flush(force=True)
1032
+
1033
+ if self.fs is not None:
1034
+ self.fs.invalidate_cache(self.path)
1035
+ self.fs.invalidate_cache(self.fs._parent(self.path))
1036
+
1037
+ self.closed = True
1038
+
1039
+ async def flush(self, force=False):
1040
+ if self.closed:
1041
+ raise ValueError("Flush on closed file")
1042
+ if force and self.forced:
1043
+ raise ValueError("Force flush cannot be called more than once")
1044
+ if force:
1045
+ self.forced = True
1046
+
1047
+ if self.mode not in {"wb", "ab"}:
1048
+ # no-op to flush on read-mode
1049
+ return
1050
+
1051
+ if not force and self.buffer.tell() < self.blocksize:
1052
+ # Defer write on small block
1053
+ return
1054
+
1055
+ if self.offset is None:
1056
+ # Initialize a multipart upload
1057
+ self.offset = 0
1058
+ try:
1059
+ await self._initiate_upload()
1060
+ except: # noqa: E722
1061
+ self.closed = True
1062
+ raise
1063
+
1064
+ if await self._upload_chunk(final=force) is not False:
1065
+ self.offset += self.buffer.seek(0, 2)
1066
+ self.buffer = io.BytesIO()
1067
+
1068
+ async def __aenter__(self):
1069
+ return self
1070
+
1071
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
1072
+ await self.close()
1073
+
1074
+ async def _fetch_range(self, start, end):
1075
+ raise NotImplementedError
1076
+
1077
+ async def _initiate_upload(self):
1078
+ pass
1079
+
1080
+ async def _upload_chunk(self, final=False):
1081
+ raise NotImplementedError
tuning-competition-baseline/.venv/lib/python3.11/site-packages/fsspec/caching.py ADDED
@@ -0,0 +1,875 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import collections
4
+ import functools
5
+ import logging
6
+ import math
7
+ import os
8
+ import threading
9
+ import warnings
10
+ from concurrent.futures import Future, ThreadPoolExecutor
11
+ from typing import (
12
+ TYPE_CHECKING,
13
+ Any,
14
+ Callable,
15
+ ClassVar,
16
+ Generic,
17
+ NamedTuple,
18
+ OrderedDict,
19
+ TypeVar,
20
+ )
21
+
22
+ if TYPE_CHECKING:
23
+ import mmap
24
+
25
+ from typing_extensions import ParamSpec
26
+
27
+ P = ParamSpec("P")
28
+ else:
29
+ P = TypeVar("P")
30
+
31
+ T = TypeVar("T")
32
+
33
+
34
+ logger = logging.getLogger("fsspec")
35
+
36
+ Fetcher = Callable[[int, int], bytes] # Maps (start, end) to bytes
37
+
38
+
39
+ class BaseCache:
40
+ """Pass-though cache: doesn't keep anything, calls every time
41
+
42
+ Acts as base class for other cachers
43
+
44
+ Parameters
45
+ ----------
46
+ blocksize: int
47
+ How far to read ahead in numbers of bytes
48
+ fetcher: func
49
+ Function of the form f(start, end) which gets bytes from remote as
50
+ specified
51
+ size: int
52
+ How big this file is
53
+ """
54
+
55
+ name: ClassVar[str] = "none"
56
+
57
+ def __init__(self, blocksize: int, fetcher: Fetcher, size: int) -> None:
58
+ self.blocksize = blocksize
59
+ self.fetcher = fetcher
60
+ self.size = size
61
+
62
+ def _fetch(self, start: int | None, stop: int | None) -> bytes:
63
+ if start is None:
64
+ start = 0
65
+ if stop is None:
66
+ stop = self.size
67
+ if start >= self.size or start >= stop:
68
+ return b""
69
+ return self.fetcher(start, stop)
70
+
71
+
72
+ class MMapCache(BaseCache):
73
+ """memory-mapped sparse file cache
74
+
75
+ Opens temporary file, which is filled blocks-wise when data is requested.
76
+ Ensure there is enough disc space in the temporary location.
77
+
78
+ This cache method might only work on posix
79
+ """
80
+
81
+ name = "mmap"
82
+
83
+ def __init__(
84
+ self,
85
+ blocksize: int,
86
+ fetcher: Fetcher,
87
+ size: int,
88
+ location: str | None = None,
89
+ blocks: set[int] | None = None,
90
+ ) -> None:
91
+ super().__init__(blocksize, fetcher, size)
92
+ self.blocks = set() if blocks is None else blocks
93
+ self.location = location
94
+ self.cache = self._makefile()
95
+
96
+ def _makefile(self) -> mmap.mmap | bytearray:
97
+ import mmap
98
+ import tempfile
99
+
100
+ if self.size == 0:
101
+ return bytearray()
102
+
103
+ # posix version
104
+ if self.location is None or not os.path.exists(self.location):
105
+ if self.location is None:
106
+ fd = tempfile.TemporaryFile()
107
+ self.blocks = set()
108
+ else:
109
+ fd = open(self.location, "wb+")
110
+ fd.seek(self.size - 1)
111
+ fd.write(b"1")
112
+ fd.flush()
113
+ else:
114
+ fd = open(self.location, "r+b")
115
+
116
+ return mmap.mmap(fd.fileno(), self.size)
117
+
118
+ def _fetch(self, start: int | None, end: int | None) -> bytes:
119
+ logger.debug(f"MMap cache fetching {start}-{end}")
120
+ if start is None:
121
+ start = 0
122
+ if end is None:
123
+ end = self.size
124
+ if start >= self.size or start >= end:
125
+ return b""
126
+ start_block = start // self.blocksize
127
+ end_block = end // self.blocksize
128
+ need = [i for i in range(start_block, end_block + 1) if i not in self.blocks]
129
+ while need:
130
+ # TODO: not a for loop so we can consolidate blocks later to
131
+ # make fewer fetch calls; this could be parallel
132
+ i = need.pop(0)
133
+ sstart = i * self.blocksize
134
+ send = min(sstart + self.blocksize, self.size)
135
+ logger.debug(f"MMap get block #{i} ({sstart}-{send}")
136
+ self.cache[sstart:send] = self.fetcher(sstart, send)
137
+ self.blocks.add(i)
138
+
139
+ return self.cache[start:end]
140
+
141
+ def __getstate__(self) -> dict[str, Any]:
142
+ state = self.__dict__.copy()
143
+ # Remove the unpicklable entries.
144
+ del state["cache"]
145
+ return state
146
+
147
+ def __setstate__(self, state: dict[str, Any]) -> None:
148
+ # Restore instance attributes
149
+ self.__dict__.update(state)
150
+ self.cache = self._makefile()
151
+
152
+
153
+ class ReadAheadCache(BaseCache):
154
+ """Cache which reads only when we get beyond a block of data
155
+
156
+ This is a much simpler version of BytesCache, and does not attempt to
157
+ fill holes in the cache or keep fragments alive. It is best suited to
158
+ many small reads in a sequential order (e.g., reading lines from a file).
159
+ """
160
+
161
+ name = "readahead"
162
+
163
+ def __init__(self, blocksize: int, fetcher: Fetcher, size: int) -> None:
164
+ super().__init__(blocksize, fetcher, size)
165
+ self.cache = b""
166
+ self.start = 0
167
+ self.end = 0
168
+
169
+ def _fetch(self, start: int | None, end: int | None) -> bytes:
170
+ if start is None:
171
+ start = 0
172
+ if end is None or end > self.size:
173
+ end = self.size
174
+ if start >= self.size or start >= end:
175
+ return b""
176
+ l = end - start
177
+ if start >= self.start and end <= self.end:
178
+ # cache hit
179
+ return self.cache[start - self.start : end - self.start]
180
+ elif self.start <= start < self.end:
181
+ # partial hit
182
+ part = self.cache[start - self.start :]
183
+ l -= len(part)
184
+ start = self.end
185
+ else:
186
+ # miss
187
+ part = b""
188
+ end = min(self.size, end + self.blocksize)
189
+ self.cache = self.fetcher(start, end) # new block replaces old
190
+ self.start = start
191
+ self.end = self.start + len(self.cache)
192
+ return part + self.cache[:l]
193
+
194
+
195
+ class FirstChunkCache(BaseCache):
196
+ """Caches the first block of a file only
197
+
198
+ This may be useful for file types where the metadata is stored in the header,
199
+ but is randomly accessed.
200
+ """
201
+
202
+ name = "first"
203
+
204
+ def __init__(self, blocksize: int, fetcher: Fetcher, size: int) -> None:
205
+ super().__init__(blocksize, fetcher, size)
206
+ self.cache: bytes | None = None
207
+
208
+ def _fetch(self, start: int | None, end: int | None) -> bytes:
209
+ start = start or 0
210
+ end = end or self.size
211
+ if start < self.blocksize:
212
+ if self.cache is None:
213
+ if end > self.blocksize:
214
+ data = self.fetcher(0, end)
215
+ self.cache = data[: self.blocksize]
216
+ return data[start:]
217
+ self.cache = self.fetcher(0, self.blocksize)
218
+ part = self.cache[start:end]
219
+ if end > self.blocksize:
220
+ part += self.fetcher(self.blocksize, end)
221
+ return part
222
+ else:
223
+ return self.fetcher(start, end)
224
+
225
+
226
+ class BlockCache(BaseCache):
227
+ """
228
+ Cache holding memory as a set of blocks.
229
+
230
+ Requests are only ever made ``blocksize`` at a time, and are
231
+ stored in an LRU cache. The least recently accessed block is
232
+ discarded when more than ``maxblocks`` are stored.
233
+
234
+ Parameters
235
+ ----------
236
+ blocksize : int
237
+ The number of bytes to store in each block.
238
+ Requests are only ever made for ``blocksize``, so this
239
+ should balance the overhead of making a request against
240
+ the granularity of the blocks.
241
+ fetcher : Callable
242
+ size : int
243
+ The total size of the file being cached.
244
+ maxblocks : int
245
+ The maximum number of blocks to cache for. The maximum memory
246
+ use for this cache is then ``blocksize * maxblocks``.
247
+ """
248
+
249
+ name = "blockcache"
250
+
251
+ def __init__(
252
+ self, blocksize: int, fetcher: Fetcher, size: int, maxblocks: int = 32
253
+ ) -> None:
254
+ super().__init__(blocksize, fetcher, size)
255
+ self.nblocks = math.ceil(size / blocksize)
256
+ self.maxblocks = maxblocks
257
+ self._fetch_block_cached = functools.lru_cache(maxblocks)(self._fetch_block)
258
+
259
+ def __repr__(self) -> str:
260
+ return (
261
+ f"<BlockCache blocksize={self.blocksize}, "
262
+ f"size={self.size}, nblocks={self.nblocks}>"
263
+ )
264
+
265
+ def cache_info(self):
266
+ """
267
+ The statistics on the block cache.
268
+
269
+ Returns
270
+ -------
271
+ NamedTuple
272
+ Returned directly from the LRU Cache used internally.
273
+ """
274
+ return self._fetch_block_cached.cache_info()
275
+
276
+ def __getstate__(self) -> dict[str, Any]:
277
+ state = self.__dict__
278
+ del state["_fetch_block_cached"]
279
+ return state
280
+
281
+ def __setstate__(self, state: dict[str, Any]) -> None:
282
+ self.__dict__.update(state)
283
+ self._fetch_block_cached = functools.lru_cache(state["maxblocks"])(
284
+ self._fetch_block
285
+ )
286
+
287
+ def _fetch(self, start: int | None, end: int | None) -> bytes:
288
+ if start is None:
289
+ start = 0
290
+ if end is None:
291
+ end = self.size
292
+ if start >= self.size or start >= end:
293
+ return b""
294
+
295
+ # byte position -> block numbers
296
+ start_block_number = start // self.blocksize
297
+ end_block_number = end // self.blocksize
298
+
299
+ # these are cached, so safe to do multiple calls for the same start and end.
300
+ for block_number in range(start_block_number, end_block_number + 1):
301
+ self._fetch_block_cached(block_number)
302
+
303
+ return self._read_cache(
304
+ start,
305
+ end,
306
+ start_block_number=start_block_number,
307
+ end_block_number=end_block_number,
308
+ )
309
+
310
+ def _fetch_block(self, block_number: int) -> bytes:
311
+ """
312
+ Fetch the block of data for `block_number`.
313
+ """
314
+ if block_number > self.nblocks:
315
+ raise ValueError(
316
+ f"'block_number={block_number}' is greater than "
317
+ f"the number of blocks ({self.nblocks})"
318
+ )
319
+
320
+ start = block_number * self.blocksize
321
+ end = start + self.blocksize
322
+ logger.info("BlockCache fetching block %d", block_number)
323
+ block_contents = super()._fetch(start, end)
324
+ return block_contents
325
+
326
+ def _read_cache(
327
+ self, start: int, end: int, start_block_number: int, end_block_number: int
328
+ ) -> bytes:
329
+ """
330
+ Read from our block cache.
331
+
332
+ Parameters
333
+ ----------
334
+ start, end : int
335
+ The start and end byte positions.
336
+ start_block_number, end_block_number : int
337
+ The start and end block numbers.
338
+ """
339
+ start_pos = start % self.blocksize
340
+ end_pos = end % self.blocksize
341
+
342
+ if start_block_number == end_block_number:
343
+ block: bytes = self._fetch_block_cached(start_block_number)
344
+ return block[start_pos:end_pos]
345
+
346
+ else:
347
+ # read from the initial
348
+ out = []
349
+ out.append(self._fetch_block_cached(start_block_number)[start_pos:])
350
+
351
+ # intermediate blocks
352
+ # Note: it'd be nice to combine these into one big request. However
353
+ # that doesn't play nicely with our LRU cache.
354
+ for block_number in range(start_block_number + 1, end_block_number):
355
+ out.append(self._fetch_block_cached(block_number))
356
+
357
+ # final block
358
+ out.append(self._fetch_block_cached(end_block_number)[:end_pos])
359
+
360
+ return b"".join(out)
361
+
362
+
363
+ class BytesCache(BaseCache):
364
+ """Cache which holds data in a in-memory bytes object
365
+
366
+ Implements read-ahead by the block size, for semi-random reads progressing
367
+ through the file.
368
+
369
+ Parameters
370
+ ----------
371
+ trim: bool
372
+ As we read more data, whether to discard the start of the buffer when
373
+ we are more than a blocksize ahead of it.
374
+ """
375
+
376
+ name: ClassVar[str] = "bytes"
377
+
378
+ def __init__(
379
+ self, blocksize: int, fetcher: Fetcher, size: int, trim: bool = True
380
+ ) -> None:
381
+ super().__init__(blocksize, fetcher, size)
382
+ self.cache = b""
383
+ self.start: int | None = None
384
+ self.end: int | None = None
385
+ self.trim = trim
386
+
387
+ def _fetch(self, start: int | None, end: int | None) -> bytes:
388
+ # TODO: only set start/end after fetch, in case it fails?
389
+ # is this where retry logic might go?
390
+ if start is None:
391
+ start = 0
392
+ if end is None:
393
+ end = self.size
394
+ if start >= self.size or start >= end:
395
+ return b""
396
+ if (
397
+ self.start is not None
398
+ and start >= self.start
399
+ and self.end is not None
400
+ and end < self.end
401
+ ):
402
+ # cache hit: we have all the required data
403
+ offset = start - self.start
404
+ return self.cache[offset : offset + end - start]
405
+
406
+ if self.blocksize:
407
+ bend = min(self.size, end + self.blocksize)
408
+ else:
409
+ bend = end
410
+
411
+ if bend == start or start > self.size:
412
+ return b""
413
+
414
+ if (self.start is None or start < self.start) and (
415
+ self.end is None or end > self.end
416
+ ):
417
+ # First read, or extending both before and after
418
+ self.cache = self.fetcher(start, bend)
419
+ self.start = start
420
+ else:
421
+ assert self.start is not None
422
+ assert self.end is not None
423
+
424
+ if start < self.start:
425
+ if self.end is None or self.end - end > self.blocksize:
426
+ self.cache = self.fetcher(start, bend)
427
+ self.start = start
428
+ else:
429
+ new = self.fetcher(start, self.start)
430
+ self.start = start
431
+ self.cache = new + self.cache
432
+ elif self.end is not None and bend > self.end:
433
+ if self.end > self.size:
434
+ pass
435
+ elif end - self.end > self.blocksize:
436
+ self.cache = self.fetcher(start, bend)
437
+ self.start = start
438
+ else:
439
+ new = self.fetcher(self.end, bend)
440
+ self.cache = self.cache + new
441
+
442
+ self.end = self.start + len(self.cache)
443
+ offset = start - self.start
444
+ out = self.cache[offset : offset + end - start]
445
+ if self.trim:
446
+ num = (self.end - self.start) // (self.blocksize + 1)
447
+ if num > 1:
448
+ self.start += self.blocksize * num
449
+ self.cache = self.cache[self.blocksize * num :]
450
+ return out
451
+
452
+ def __len__(self) -> int:
453
+ return len(self.cache)
454
+
455
+
456
+ class AllBytes(BaseCache):
457
+ """Cache entire contents of the file"""
458
+
459
+ name: ClassVar[str] = "all"
460
+
461
+ def __init__(
462
+ self,
463
+ blocksize: int | None = None,
464
+ fetcher: Fetcher | None = None,
465
+ size: int | None = None,
466
+ data: bytes | None = None,
467
+ ) -> None:
468
+ super().__init__(blocksize, fetcher, size) # type: ignore[arg-type]
469
+ if data is None:
470
+ data = self.fetcher(0, self.size)
471
+ self.data = data
472
+
473
+ def _fetch(self, start: int | None, stop: int | None) -> bytes:
474
+ return self.data[start:stop]
475
+
476
+
477
+ class KnownPartsOfAFile(BaseCache):
478
+ """
479
+ Cache holding known file parts.
480
+
481
+ Parameters
482
+ ----------
483
+ blocksize: int
484
+ How far to read ahead in numbers of bytes
485
+ fetcher: func
486
+ Function of the form f(start, end) which gets bytes from remote as
487
+ specified
488
+ size: int
489
+ How big this file is
490
+ data: dict
491
+ A dictionary mapping explicit `(start, stop)` file-offset tuples
492
+ with known bytes.
493
+ strict: bool, default True
494
+ Whether to fetch reads that go beyond a known byte-range boundary.
495
+ If `False`, any read that ends outside a known part will be zero
496
+ padded. Note that zero padding will not be used for reads that
497
+ begin outside a known byte-range.
498
+ """
499
+
500
+ name: ClassVar[str] = "parts"
501
+
502
+ def __init__(
503
+ self,
504
+ blocksize: int,
505
+ fetcher: Fetcher,
506
+ size: int,
507
+ data: dict[tuple[int, int], bytes] = {},
508
+ strict: bool = True,
509
+ **_: Any,
510
+ ):
511
+ super().__init__(blocksize, fetcher, size)
512
+ self.strict = strict
513
+
514
+ # simple consolidation of contiguous blocks
515
+ if data:
516
+ old_offsets = sorted(data.keys())
517
+ offsets = [old_offsets[0]]
518
+ blocks = [data.pop(old_offsets[0])]
519
+ for start, stop in old_offsets[1:]:
520
+ start0, stop0 = offsets[-1]
521
+ if start == stop0:
522
+ offsets[-1] = (start0, stop)
523
+ blocks[-1] += data.pop((start, stop))
524
+ else:
525
+ offsets.append((start, stop))
526
+ blocks.append(data.pop((start, stop)))
527
+
528
+ self.data = dict(zip(offsets, blocks))
529
+ else:
530
+ self.data = data
531
+
532
+ def _fetch(self, start: int | None, stop: int | None) -> bytes:
533
+ if start is None:
534
+ start = 0
535
+ if stop is None:
536
+ stop = self.size
537
+
538
+ out = b""
539
+ for (loc0, loc1), data in self.data.items():
540
+ # If self.strict=False, use zero-padded data
541
+ # for reads beyond the end of a "known" buffer
542
+ if loc0 <= start < loc1:
543
+ off = start - loc0
544
+ out = data[off : off + stop - start]
545
+ if not self.strict or loc0 <= stop <= loc1:
546
+ # The request is within a known range, or
547
+ # it begins within a known range, and we
548
+ # are allowed to pad reads beyond the
549
+ # buffer with zero
550
+ out += b"\x00" * (stop - start - len(out))
551
+ return out
552
+ else:
553
+ # The request ends outside a known range,
554
+ # and we are being "strict" about reads
555
+ # beyond the buffer
556
+ start = loc1
557
+ break
558
+
559
+ # We only get here if there is a request outside the
560
+ # known parts of the file. In an ideal world, this
561
+ # should never happen
562
+ if self.fetcher is None:
563
+ # We cannot fetch the data, so raise an error
564
+ raise ValueError(f"Read is outside the known file parts: {(start, stop)}. ")
565
+ # We can fetch the data, but should warn the user
566
+ # that this may be slow
567
+ warnings.warn(
568
+ f"Read is outside the known file parts: {(start, stop)}. "
569
+ f"IO/caching performance may be poor!"
570
+ )
571
+ logger.debug(f"KnownPartsOfAFile cache fetching {start}-{stop}")
572
+ return out + super()._fetch(start, stop)
573
+
574
+
575
+ class UpdatableLRU(Generic[P, T]):
576
+ """
577
+ Custom implementation of LRU cache that allows updating keys
578
+
579
+ Used by BackgroudBlockCache
580
+ """
581
+
582
+ class CacheInfo(NamedTuple):
583
+ hits: int
584
+ misses: int
585
+ maxsize: int
586
+ currsize: int
587
+
588
+ def __init__(self, func: Callable[P, T], max_size: int = 128) -> None:
589
+ self._cache: OrderedDict[Any, T] = collections.OrderedDict()
590
+ self._func = func
591
+ self._max_size = max_size
592
+ self._hits = 0
593
+ self._misses = 0
594
+ self._lock = threading.Lock()
595
+
596
+ def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T:
597
+ if kwargs:
598
+ raise TypeError(f"Got unexpected keyword argument {kwargs.keys()}")
599
+ with self._lock:
600
+ if args in self._cache:
601
+ self._cache.move_to_end(args)
602
+ self._hits += 1
603
+ return self._cache[args]
604
+
605
+ result = self._func(*args, **kwargs)
606
+
607
+ with self._lock:
608
+ self._cache[args] = result
609
+ self._misses += 1
610
+ if len(self._cache) > self._max_size:
611
+ self._cache.popitem(last=False)
612
+
613
+ return result
614
+
615
+ def is_key_cached(self, *args: Any) -> bool:
616
+ with self._lock:
617
+ return args in self._cache
618
+
619
+ def add_key(self, result: T, *args: Any) -> None:
620
+ with self._lock:
621
+ self._cache[args] = result
622
+ if len(self._cache) > self._max_size:
623
+ self._cache.popitem(last=False)
624
+
625
+ def cache_info(self) -> UpdatableLRU.CacheInfo:
626
+ with self._lock:
627
+ return self.CacheInfo(
628
+ maxsize=self._max_size,
629
+ currsize=len(self._cache),
630
+ hits=self._hits,
631
+ misses=self._misses,
632
+ )
633
+
634
+
635
+ class BackgroundBlockCache(BaseCache):
636
+ """
637
+ Cache holding memory as a set of blocks with pre-loading of
638
+ the next block in the background.
639
+
640
+ Requests are only ever made ``blocksize`` at a time, and are
641
+ stored in an LRU cache. The least recently accessed block is
642
+ discarded when more than ``maxblocks`` are stored. If the
643
+ next block is not in cache, it is loaded in a separate thread
644
+ in non-blocking way.
645
+
646
+ Parameters
647
+ ----------
648
+ blocksize : int
649
+ The number of bytes to store in each block.
650
+ Requests are only ever made for ``blocksize``, so this
651
+ should balance the overhead of making a request against
652
+ the granularity of the blocks.
653
+ fetcher : Callable
654
+ size : int
655
+ The total size of the file being cached.
656
+ maxblocks : int
657
+ The maximum number of blocks to cache for. The maximum memory
658
+ use for this cache is then ``blocksize * maxblocks``.
659
+ """
660
+
661
+ name: ClassVar[str] = "background"
662
+
663
+ def __init__(
664
+ self, blocksize: int, fetcher: Fetcher, size: int, maxblocks: int = 32
665
+ ) -> None:
666
+ super().__init__(blocksize, fetcher, size)
667
+ self.nblocks = math.ceil(size / blocksize)
668
+ self.maxblocks = maxblocks
669
+ self._fetch_block_cached = UpdatableLRU(self._fetch_block, maxblocks)
670
+
671
+ self._thread_executor = ThreadPoolExecutor(max_workers=1)
672
+ self._fetch_future_block_number: int | None = None
673
+ self._fetch_future: Future[bytes] | None = None
674
+ self._fetch_future_lock = threading.Lock()
675
+
676
+ def __repr__(self) -> str:
677
+ return (
678
+ f"<BackgroundBlockCache blocksize={self.blocksize}, "
679
+ f"size={self.size}, nblocks={self.nblocks}>"
680
+ )
681
+
682
+ def cache_info(self) -> UpdatableLRU.CacheInfo:
683
+ """
684
+ The statistics on the block cache.
685
+
686
+ Returns
687
+ -------
688
+ NamedTuple
689
+ Returned directly from the LRU Cache used internally.
690
+ """
691
+ return self._fetch_block_cached.cache_info()
692
+
693
+ def __getstate__(self) -> dict[str, Any]:
694
+ state = self.__dict__
695
+ del state["_fetch_block_cached"]
696
+ del state["_thread_executor"]
697
+ del state["_fetch_future_block_number"]
698
+ del state["_fetch_future"]
699
+ del state["_fetch_future_lock"]
700
+ return state
701
+
702
+ def __setstate__(self, state) -> None:
703
+ self.__dict__.update(state)
704
+ self._fetch_block_cached = UpdatableLRU(self._fetch_block, state["maxblocks"])
705
+ self._thread_executor = ThreadPoolExecutor(max_workers=1)
706
+ self._fetch_future_block_number = None
707
+ self._fetch_future = None
708
+ self._fetch_future_lock = threading.Lock()
709
+
710
+ def _fetch(self, start: int | None, end: int | None) -> bytes:
711
+ if start is None:
712
+ start = 0
713
+ if end is None:
714
+ end = self.size
715
+ if start >= self.size or start >= end:
716
+ return b""
717
+
718
+ # byte position -> block numbers
719
+ start_block_number = start // self.blocksize
720
+ end_block_number = end // self.blocksize
721
+
722
+ fetch_future_block_number = None
723
+ fetch_future = None
724
+ with self._fetch_future_lock:
725
+ # Background thread is running. Check we we can or must join it.
726
+ if self._fetch_future is not None:
727
+ assert self._fetch_future_block_number is not None
728
+ if self._fetch_future.done():
729
+ logger.info("BlockCache joined background fetch without waiting.")
730
+ self._fetch_block_cached.add_key(
731
+ self._fetch_future.result(), self._fetch_future_block_number
732
+ )
733
+ # Cleanup the fetch variables. Done with fetching the block.
734
+ self._fetch_future_block_number = None
735
+ self._fetch_future = None
736
+ else:
737
+ # Must join if we need the block for the current fetch
738
+ must_join = bool(
739
+ start_block_number
740
+ <= self._fetch_future_block_number
741
+ <= end_block_number
742
+ )
743
+ if must_join:
744
+ # Copy to the local variables to release lock
745
+ # before waiting for result
746
+ fetch_future_block_number = self._fetch_future_block_number
747
+ fetch_future = self._fetch_future
748
+
749
+ # Cleanup the fetch variables. Have a local copy.
750
+ self._fetch_future_block_number = None
751
+ self._fetch_future = None
752
+
753
+ # Need to wait for the future for the current read
754
+ if fetch_future is not None:
755
+ logger.info("BlockCache waiting for background fetch.")
756
+ # Wait until result and put it in cache
757
+ self._fetch_block_cached.add_key(
758
+ fetch_future.result(), fetch_future_block_number
759
+ )
760
+
761
+ # these are cached, so safe to do multiple calls for the same start and end.
762
+ for block_number in range(start_block_number, end_block_number + 1):
763
+ self._fetch_block_cached(block_number)
764
+
765
+ # fetch next block in the background if nothing is running in the background,
766
+ # the block is within file and it is not already cached
767
+ end_block_plus_1 = end_block_number + 1
768
+ with self._fetch_future_lock:
769
+ if (
770
+ self._fetch_future is None
771
+ and end_block_plus_1 <= self.nblocks
772
+ and not self._fetch_block_cached.is_key_cached(end_block_plus_1)
773
+ ):
774
+ self._fetch_future_block_number = end_block_plus_1
775
+ self._fetch_future = self._thread_executor.submit(
776
+ self._fetch_block, end_block_plus_1, "async"
777
+ )
778
+
779
+ return self._read_cache(
780
+ start,
781
+ end,
782
+ start_block_number=start_block_number,
783
+ end_block_number=end_block_number,
784
+ )
785
+
786
+ def _fetch_block(self, block_number: int, log_info: str = "sync") -> bytes:
787
+ """
788
+ Fetch the block of data for `block_number`.
789
+ """
790
+ if block_number > self.nblocks:
791
+ raise ValueError(
792
+ f"'block_number={block_number}' is greater than "
793
+ f"the number of blocks ({self.nblocks})"
794
+ )
795
+
796
+ start = block_number * self.blocksize
797
+ end = start + self.blocksize
798
+ logger.info("BlockCache fetching block (%s) %d", log_info, block_number)
799
+ block_contents = super()._fetch(start, end)
800
+ return block_contents
801
+
802
+ def _read_cache(
803
+ self, start: int, end: int, start_block_number: int, end_block_number: int
804
+ ) -> bytes:
805
+ """
806
+ Read from our block cache.
807
+
808
+ Parameters
809
+ ----------
810
+ start, end : int
811
+ The start and end byte positions.
812
+ start_block_number, end_block_number : int
813
+ The start and end block numbers.
814
+ """
815
+ start_pos = start % self.blocksize
816
+ end_pos = end % self.blocksize
817
+
818
+ if start_block_number == end_block_number:
819
+ block = self._fetch_block_cached(start_block_number)
820
+ return block[start_pos:end_pos]
821
+
822
+ else:
823
+ # read from the initial
824
+ out = []
825
+ out.append(self._fetch_block_cached(start_block_number)[start_pos:])
826
+
827
+ # intermediate blocks
828
+ # Note: it'd be nice to combine these into one big request. However
829
+ # that doesn't play nicely with our LRU cache.
830
+ for block_number in range(start_block_number + 1, end_block_number):
831
+ out.append(self._fetch_block_cached(block_number))
832
+
833
+ # final block
834
+ out.append(self._fetch_block_cached(end_block_number)[:end_pos])
835
+
836
+ return b"".join(out)
837
+
838
+
839
+ caches: dict[str | None, type[BaseCache]] = {
840
+ # one custom case
841
+ None: BaseCache,
842
+ }
843
+
844
+
845
+ def register_cache(cls: type[BaseCache], clobber: bool = False) -> None:
846
+ """'Register' cache implementation.
847
+
848
+ Parameters
849
+ ----------
850
+ clobber: bool, optional
851
+ If set to True (default is False) - allow to overwrite existing
852
+ entry.
853
+
854
+ Raises
855
+ ------
856
+ ValueError
857
+ """
858
+ name = cls.name
859
+ if not clobber and name in caches:
860
+ raise ValueError(f"Cache with name {name!r} is already known: {caches[name]}")
861
+ caches[name] = cls
862
+
863
+
864
+ for c in (
865
+ BaseCache,
866
+ MMapCache,
867
+ BytesCache,
868
+ ReadAheadCache,
869
+ BlockCache,
870
+ FirstChunkCache,
871
+ AllBytes,
872
+ KnownPartsOfAFile,
873
+ BackgroundBlockCache,
874
+ ):
875
+ register_cache(c)
tuning-competition-baseline/.venv/lib/python3.11/site-packages/fsspec/callbacks.py ADDED
@@ -0,0 +1,324 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from functools import wraps
2
+
3
+
4
+ class Callback:
5
+ """
6
+ Base class and interface for callback mechanism
7
+
8
+ This class can be used directly for monitoring file transfers by
9
+ providing ``callback=Callback(hooks=...)`` (see the ``hooks`` argument,
10
+ below), or subclassed for more specialised behaviour.
11
+
12
+ Parameters
13
+ ----------
14
+ size: int (optional)
15
+ Nominal quantity for the value that corresponds to a complete
16
+ transfer, e.g., total number of tiles or total number of
17
+ bytes
18
+ value: int (0)
19
+ Starting internal counter value
20
+ hooks: dict or None
21
+ A dict of named functions to be called on each update. The signature
22
+ of these must be ``f(size, value, **kwargs)``
23
+ """
24
+
25
+ def __init__(self, size=None, value=0, hooks=None, **kwargs):
26
+ self.size = size
27
+ self.value = value
28
+ self.hooks = hooks or {}
29
+ self.kw = kwargs
30
+
31
+ def __enter__(self):
32
+ return self
33
+
34
+ def __exit__(self, *exc_args):
35
+ self.close()
36
+
37
+ def close(self):
38
+ """Close callback."""
39
+
40
+ def branched(self, path_1, path_2, **kwargs):
41
+ """
42
+ Return callback for child transfers
43
+
44
+ If this callback is operating at a higher level, e.g., put, which may
45
+ trigger transfers that can also be monitored. The function returns a callback
46
+ that has to be passed to the child method, e.g., put_file,
47
+ as `callback=` argument.
48
+
49
+ The implementation uses `callback.branch` for compatibility.
50
+ When implementing callbacks, it is recommended to override this function instead
51
+ of `branch` and avoid calling `super().branched(...)`.
52
+
53
+ Prefer using this function over `branch`.
54
+
55
+ Parameters
56
+ ----------
57
+ path_1: str
58
+ Child's source path
59
+ path_2: str
60
+ Child's destination path
61
+ **kwargs:
62
+ Arbitrary keyword arguments
63
+
64
+ Returns
65
+ -------
66
+ callback: Callback
67
+ A callback instance to be passed to the child method
68
+ """
69
+ self.branch(path_1, path_2, kwargs)
70
+ # mutate kwargs so that we can force the caller to pass "callback=" explicitly
71
+ return kwargs.pop("callback", DEFAULT_CALLBACK)
72
+
73
+ def branch_coro(self, fn):
74
+ """
75
+ Wraps a coroutine, and pass a new child callback to it.
76
+ """
77
+
78
+ @wraps(fn)
79
+ async def func(path1, path2: str, **kwargs):
80
+ with self.branched(path1, path2, **kwargs) as child:
81
+ return await fn(path1, path2, callback=child, **kwargs)
82
+
83
+ return func
84
+
85
+ def set_size(self, size):
86
+ """
87
+ Set the internal maximum size attribute
88
+
89
+ Usually called if not initially set at instantiation. Note that this
90
+ triggers a ``call()``.
91
+
92
+ Parameters
93
+ ----------
94
+ size: int
95
+ """
96
+ self.size = size
97
+ self.call()
98
+
99
+ def absolute_update(self, value):
100
+ """
101
+ Set the internal value state
102
+
103
+ Triggers ``call()``
104
+
105
+ Parameters
106
+ ----------
107
+ value: int
108
+ """
109
+ self.value = value
110
+ self.call()
111
+
112
+ def relative_update(self, inc=1):
113
+ """
114
+ Delta increment the internal counter
115
+
116
+ Triggers ``call()``
117
+
118
+ Parameters
119
+ ----------
120
+ inc: int
121
+ """
122
+ self.value += inc
123
+ self.call()
124
+
125
+ def call(self, hook_name=None, **kwargs):
126
+ """
127
+ Execute hook(s) with current state
128
+
129
+ Each function is passed the internal size and current value
130
+
131
+ Parameters
132
+ ----------
133
+ hook_name: str or None
134
+ If given, execute on this hook
135
+ kwargs: passed on to (all) hook(s)
136
+ """
137
+ if not self.hooks:
138
+ return
139
+ kw = self.kw.copy()
140
+ kw.update(kwargs)
141
+ if hook_name:
142
+ if hook_name not in self.hooks:
143
+ return
144
+ return self.hooks[hook_name](self.size, self.value, **kw)
145
+ for hook in self.hooks.values() or []:
146
+ hook(self.size, self.value, **kw)
147
+
148
+ def wrap(self, iterable):
149
+ """
150
+ Wrap an iterable to call ``relative_update`` on each iterations
151
+
152
+ Parameters
153
+ ----------
154
+ iterable: Iterable
155
+ The iterable that is being wrapped
156
+ """
157
+ for item in iterable:
158
+ self.relative_update()
159
+ yield item
160
+
161
+ def branch(self, path_1, path_2, kwargs):
162
+ """
163
+ Set callbacks for child transfers
164
+
165
+ If this callback is operating at a higher level, e.g., put, which may
166
+ trigger transfers that can also be monitored. The passed kwargs are
167
+ to be *mutated* to add ``callback=``, if this class supports branching
168
+ to children.
169
+
170
+ Parameters
171
+ ----------
172
+ path_1: str
173
+ Child's source path
174
+ path_2: str
175
+ Child's destination path
176
+ kwargs: dict
177
+ arguments passed to child method, e.g., put_file.
178
+
179
+ Returns
180
+ -------
181
+
182
+ """
183
+ return None
184
+
185
+ def no_op(self, *_, **__):
186
+ pass
187
+
188
+ def __getattr__(self, item):
189
+ """
190
+ If undefined methods are called on this class, nothing happens
191
+ """
192
+ return self.no_op
193
+
194
+ @classmethod
195
+ def as_callback(cls, maybe_callback=None):
196
+ """Transform callback=... into Callback instance
197
+
198
+ For the special value of ``None``, return the global instance of
199
+ ``NoOpCallback``. This is an alternative to including
200
+ ``callback=DEFAULT_CALLBACK`` directly in a method signature.
201
+ """
202
+ if maybe_callback is None:
203
+ return DEFAULT_CALLBACK
204
+ return maybe_callback
205
+
206
+
207
+ class NoOpCallback(Callback):
208
+ """
209
+ This implementation of Callback does exactly nothing
210
+ """
211
+
212
+ def call(self, *args, **kwargs):
213
+ return None
214
+
215
+
216
+ class DotPrinterCallback(Callback):
217
+ """
218
+ Simple example Callback implementation
219
+
220
+ Almost identical to Callback with a hook that prints a char; here we
221
+ demonstrate how the outer layer may print "#" and the inner layer "."
222
+ """
223
+
224
+ def __init__(self, chr_to_print="#", **kwargs):
225
+ self.chr = chr_to_print
226
+ super().__init__(**kwargs)
227
+
228
+ def branch(self, path_1, path_2, kwargs):
229
+ """Mutate kwargs to add new instance with different print char"""
230
+ kwargs["callback"] = DotPrinterCallback(".")
231
+
232
+ def call(self, **kwargs):
233
+ """Just outputs a character"""
234
+ print(self.chr, end="")
235
+
236
+
237
+ class TqdmCallback(Callback):
238
+ """
239
+ A callback to display a progress bar using tqdm
240
+
241
+ Parameters
242
+ ----------
243
+ tqdm_kwargs : dict, (optional)
244
+ Any argument accepted by the tqdm constructor.
245
+ See the `tqdm doc <https://tqdm.github.io/docs/tqdm/#__init__>`_.
246
+ Will be forwarded to `tqdm_cls`.
247
+ tqdm_cls: (optional)
248
+ subclass of `tqdm.tqdm`. If not passed, it will default to `tqdm.tqdm`.
249
+
250
+ Examples
251
+ --------
252
+ >>> import fsspec
253
+ >>> from fsspec.callbacks import TqdmCallback
254
+ >>> fs = fsspec.filesystem("memory")
255
+ >>> path2distant_data = "/your-path"
256
+ >>> fs.upload(
257
+ ".",
258
+ path2distant_data,
259
+ recursive=True,
260
+ callback=TqdmCallback(),
261
+ )
262
+
263
+ You can forward args to tqdm using the ``tqdm_kwargs`` parameter.
264
+
265
+ >>> fs.upload(
266
+ ".",
267
+ path2distant_data,
268
+ recursive=True,
269
+ callback=TqdmCallback(tqdm_kwargs={"desc": "Your tqdm description"}),
270
+ )
271
+
272
+ You can also customize the progress bar by passing a subclass of `tqdm`.
273
+
274
+ .. code-block:: python
275
+
276
+ class TqdmFormat(tqdm):
277
+ '''Provides a `total_time` format parameter'''
278
+ @property
279
+ def format_dict(self):
280
+ d = super().format_dict
281
+ total_time = d["elapsed"] * (d["total"] or 0) / max(d["n"], 1)
282
+ d.update(total_time=self.format_interval(total_time) + " in total")
283
+ return d
284
+
285
+ >>> with TqdmCallback(
286
+ tqdm_kwargs={
287
+ "desc": "desc",
288
+ "bar_format": "{total_time}: {percentage:.0f}%|{bar}{r_bar}",
289
+ },
290
+ tqdm_cls=TqdmFormat,
291
+ ) as callback:
292
+ fs.upload(".", path2distant_data, recursive=True, callback=callback)
293
+ """
294
+
295
+ def __init__(self, tqdm_kwargs=None, *args, **kwargs):
296
+ try:
297
+ from tqdm import tqdm
298
+
299
+ except ImportError as exce:
300
+ raise ImportError(
301
+ "Using TqdmCallback requires tqdm to be installed"
302
+ ) from exce
303
+
304
+ self._tqdm_cls = kwargs.pop("tqdm_cls", tqdm)
305
+ self._tqdm_kwargs = tqdm_kwargs or {}
306
+ self.tqdm = None
307
+ super().__init__(*args, **kwargs)
308
+
309
+ def call(self, *args, **kwargs):
310
+ if self.tqdm is None:
311
+ self.tqdm = self._tqdm_cls(total=self.size, **self._tqdm_kwargs)
312
+ self.tqdm.total = self.size
313
+ self.tqdm.update(self.value - self.tqdm.n)
314
+
315
+ def close(self):
316
+ if self.tqdm is not None:
317
+ self.tqdm.close()
318
+ self.tqdm = None
319
+
320
+ def __del__(self):
321
+ return self.close()
322
+
323
+
324
+ DEFAULT_CALLBACK = _DEFAULT_CALLBACK = NoOpCallback()
tuning-competition-baseline/.venv/lib/python3.11/site-packages/fsspec/compression.py ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Helper functions for a standard streaming compression API"""
2
+ from zipfile import ZipFile
3
+
4
+ import fsspec.utils
5
+ from fsspec.spec import AbstractBufferedFile
6
+
7
+
8
+ def noop_file(file, mode, **kwargs):
9
+ return file
10
+
11
+
12
+ # TODO: files should also be available as contexts
13
+ # should be functions of the form func(infile, mode=, **kwargs) -> file-like
14
+ compr = {None: noop_file}
15
+
16
+
17
+ def register_compression(name, callback, extensions, force=False):
18
+ """Register an "inferable" file compression type.
19
+
20
+ Registers transparent file compression type for use with fsspec.open.
21
+ Compression can be specified by name in open, or "infer"-ed for any files
22
+ ending with the given extensions.
23
+
24
+ Args:
25
+ name: (str) The compression type name. Eg. "gzip".
26
+ callback: A callable of form (infile, mode, **kwargs) -> file-like.
27
+ Accepts an input file-like object, the target mode and kwargs.
28
+ Returns a wrapped file-like object.
29
+ extensions: (str, Iterable[str]) A file extension, or list of file
30
+ extensions for which to infer this compression scheme. Eg. "gz".
31
+ force: (bool) Force re-registration of compression type or extensions.
32
+
33
+ Raises:
34
+ ValueError: If name or extensions already registered, and not force.
35
+
36
+ """
37
+ if isinstance(extensions, str):
38
+ extensions = [extensions]
39
+
40
+ # Validate registration
41
+ if name in compr and not force:
42
+ raise ValueError(f"Duplicate compression registration: {name}")
43
+
44
+ for ext in extensions:
45
+ if ext in fsspec.utils.compressions and not force:
46
+ raise ValueError(f"Duplicate compression file extension: {ext} ({name})")
47
+
48
+ compr[name] = callback
49
+
50
+ for ext in extensions:
51
+ fsspec.utils.compressions[ext] = name
52
+
53
+
54
+ def unzip(infile, mode="rb", filename=None, **kwargs):
55
+ if "r" not in mode:
56
+ filename = filename or "file"
57
+ z = ZipFile(infile, mode="w", **kwargs)
58
+ fo = z.open(filename, mode="w")
59
+ fo.close = lambda closer=fo.close: closer() or z.close()
60
+ return fo
61
+ z = ZipFile(infile)
62
+ if filename is None:
63
+ filename = z.namelist()[0]
64
+ return z.open(filename, mode="r", **kwargs)
65
+
66
+
67
+ register_compression("zip", unzip, "zip")
68
+
69
+ try:
70
+ from bz2 import BZ2File
71
+ except ImportError:
72
+ pass
73
+ else:
74
+ register_compression("bz2", BZ2File, "bz2")
75
+
76
+ try: # pragma: no cover
77
+ from isal import igzip
78
+
79
+ def isal(infile, mode="rb", **kwargs):
80
+ return igzip.IGzipFile(fileobj=infile, mode=mode, **kwargs)
81
+
82
+ register_compression("gzip", isal, "gz")
83
+ except ImportError:
84
+ from gzip import GzipFile
85
+
86
+ register_compression(
87
+ "gzip", lambda f, **kwargs: GzipFile(fileobj=f, **kwargs), "gz"
88
+ )
89
+
90
+ try:
91
+ from lzma import LZMAFile
92
+
93
+ register_compression("lzma", LZMAFile, "lzma")
94
+ register_compression("xz", LZMAFile, "xz")
95
+ except ImportError:
96
+ pass
97
+
98
+ try:
99
+ import lzmaffi
100
+
101
+ register_compression("lzma", lzmaffi.LZMAFile, "lzma", force=True)
102
+ register_compression("xz", lzmaffi.LZMAFile, "xz", force=True)
103
+ except ImportError:
104
+ pass
105
+
106
+
107
+ class SnappyFile(AbstractBufferedFile):
108
+ def __init__(self, infile, mode, **kwargs):
109
+ import snappy
110
+
111
+ super().__init__(
112
+ fs=None, path="snappy", mode=mode.strip("b") + "b", size=999999999, **kwargs
113
+ )
114
+ self.infile = infile
115
+ if "r" in mode:
116
+ self.codec = snappy.StreamDecompressor()
117
+ else:
118
+ self.codec = snappy.StreamCompressor()
119
+
120
+ def _upload_chunk(self, final=False):
121
+ self.buffer.seek(0)
122
+ out = self.codec.add_chunk(self.buffer.read())
123
+ self.infile.write(out)
124
+ return True
125
+
126
+ def seek(self, loc, whence=0):
127
+ raise NotImplementedError("SnappyFile is not seekable")
128
+
129
+ def seekable(self):
130
+ return False
131
+
132
+ def _fetch_range(self, start, end):
133
+ """Get the specified set of bytes from remote"""
134
+ data = self.infile.read(end - start)
135
+ return self.codec.decompress(data)
136
+
137
+
138
+ try:
139
+ import snappy
140
+
141
+ snappy.compress
142
+ # Snappy may use the .sz file extension, but this is not part of the
143
+ # standard implementation.
144
+ register_compression("snappy", SnappyFile, [])
145
+
146
+ except (ImportError, NameError, AttributeError):
147
+ pass
148
+
149
+ try:
150
+ import lz4.frame
151
+
152
+ register_compression("lz4", lz4.frame.open, "lz4")
153
+ except ImportError:
154
+ pass
155
+
156
+ try:
157
+ import zstandard as zstd
158
+
159
+ def zstandard_file(infile, mode="rb"):
160
+ if "r" in mode:
161
+ cctx = zstd.ZstdDecompressor()
162
+ return cctx.stream_reader(infile)
163
+ else:
164
+ cctx = zstd.ZstdCompressor(level=10)
165
+ return cctx.stream_writer(infile)
166
+
167
+ register_compression("zstd", zstandard_file, "zst")
168
+ except ImportError:
169
+ pass
170
+
171
+
172
+ def available_compressions():
173
+ """Return a list of the implemented compressions."""
174
+ return list(compr)
tuning-competition-baseline/.venv/lib/python3.11/site-packages/fsspec/config.py ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import configparser
4
+ import json
5
+ import os
6
+ import warnings
7
+ from typing import Any
8
+
9
+ conf: dict[str, dict[str, Any]] = {}
10
+ default_conf_dir = os.path.join(os.path.expanduser("~"), ".config/fsspec")
11
+ conf_dir = os.environ.get("FSSPEC_CONFIG_DIR", default_conf_dir)
12
+
13
+
14
+ def set_conf_env(conf_dict, envdict=os.environ):
15
+ """Set config values from environment variables
16
+
17
+ Looks for variables of the form ``FSSPEC_<protocol>`` and
18
+ ``FSSPEC_<protocol>_<kwarg>``. For ``FSSPEC_<protocol>`` the value is parsed
19
+ as a json dictionary and used to ``update`` the config of the
20
+ corresponding protocol. For ``FSSPEC_<protocol>_<kwarg>`` there is no
21
+ attempt to convert the string value, but the kwarg keys will be lower-cased.
22
+
23
+ The ``FSSPEC_<protocol>_<kwarg>`` variables are applied after the
24
+ ``FSSPEC_<protocol>`` ones.
25
+
26
+ Parameters
27
+ ----------
28
+ conf_dict : dict(str, dict)
29
+ This dict will be mutated
30
+ envdict : dict-like(str, str)
31
+ Source for the values - usually the real environment
32
+ """
33
+ kwarg_keys = []
34
+ for key in envdict:
35
+ if key.startswith("FSSPEC_") and len(key) > 7 and key[7] != "_":
36
+ if key.count("_") > 1:
37
+ kwarg_keys.append(key)
38
+ continue
39
+ try:
40
+ value = json.loads(envdict[key])
41
+ except json.decoder.JSONDecodeError as ex:
42
+ warnings.warn(
43
+ f"Ignoring environment variable {key} due to a parse failure: {ex}"
44
+ )
45
+ else:
46
+ if isinstance(value, dict):
47
+ _, proto = key.split("_", 1)
48
+ conf_dict.setdefault(proto.lower(), {}).update(value)
49
+ else:
50
+ warnings.warn(
51
+ f"Ignoring environment variable {key} due to not being a dict:"
52
+ f" {type(value)}"
53
+ )
54
+ elif key.startswith("FSSPEC"):
55
+ warnings.warn(
56
+ f"Ignoring environment variable {key} due to having an unexpected name"
57
+ )
58
+
59
+ for key in kwarg_keys:
60
+ _, proto, kwarg = key.split("_", 2)
61
+ conf_dict.setdefault(proto.lower(), {})[kwarg.lower()] = envdict[key]
62
+
63
+
64
+ def set_conf_files(cdir, conf_dict):
65
+ """Set config values from files
66
+
67
+ Scans for INI and JSON files in the given dictionary, and uses their
68
+ contents to set the config. In case of repeated values, later values
69
+ win.
70
+
71
+ In the case of INI files, all values are strings, and these will not
72
+ be converted.
73
+
74
+ Parameters
75
+ ----------
76
+ cdir : str
77
+ Directory to search
78
+ conf_dict : dict(str, dict)
79
+ This dict will be mutated
80
+ """
81
+ if not os.path.isdir(cdir):
82
+ return
83
+ allfiles = sorted(os.listdir(cdir))
84
+ for fn in allfiles:
85
+ if fn.endswith(".ini"):
86
+ ini = configparser.ConfigParser()
87
+ ini.read(os.path.join(cdir, fn))
88
+ for key in ini:
89
+ if key == "DEFAULT":
90
+ continue
91
+ conf_dict.setdefault(key, {}).update(dict(ini[key]))
92
+ if fn.endswith(".json"):
93
+ with open(os.path.join(cdir, fn)) as f:
94
+ js = json.load(f)
95
+ for key in js:
96
+ conf_dict.setdefault(key, {}).update(dict(js[key]))
97
+
98
+
99
+ def apply_config(cls, kwargs, conf_dict=None):
100
+ """Supply default values for kwargs when instantiating class
101
+
102
+ Augments the passed kwargs, by finding entries in the config dict
103
+ which match the classes ``.protocol`` attribute (one or more str)
104
+
105
+ Parameters
106
+ ----------
107
+ cls : file system implementation
108
+ kwargs : dict
109
+ conf_dict : dict of dict
110
+ Typically this is the global configuration
111
+
112
+ Returns
113
+ -------
114
+ dict : the modified set of kwargs
115
+ """
116
+ if conf_dict is None:
117
+ conf_dict = conf
118
+ protos = cls.protocol if isinstance(cls.protocol, (tuple, list)) else [cls.protocol]
119
+ kw = {}
120
+ for proto in protos:
121
+ # default kwargs from the current state of the config
122
+ if proto in conf_dict:
123
+ kw.update(conf_dict[proto])
124
+ # explicit kwargs always win
125
+ kw.update(**kwargs)
126
+ kwargs = kw
127
+ return kwargs
128
+
129
+
130
+ set_conf_files(conf_dir, conf)
131
+ set_conf_env(conf)
tuning-competition-baseline/.venv/lib/python3.11/site-packages/fsspec/core.py ADDED
@@ -0,0 +1,710 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import io
4
+ import logging
5
+ import os
6
+ import re
7
+ from glob import has_magic
8
+ from pathlib import Path
9
+
10
+ # for backwards compat, we export cache things from here too
11
+ from .caching import ( # noqa: F401
12
+ BaseCache,
13
+ BlockCache,
14
+ BytesCache,
15
+ MMapCache,
16
+ ReadAheadCache,
17
+ caches,
18
+ )
19
+ from .compression import compr
20
+ from .registry import filesystem, get_filesystem_class
21
+ from .utils import (
22
+ _unstrip_protocol,
23
+ build_name_function,
24
+ infer_compression,
25
+ stringify_path,
26
+ )
27
+
28
+ logger = logging.getLogger("fsspec")
29
+
30
+
31
+ class OpenFile:
32
+ """
33
+ File-like object to be used in a context
34
+
35
+ Can layer (buffered) text-mode and compression over any file-system, which
36
+ are typically binary-only.
37
+
38
+ These instances are safe to serialize, as the low-level file object
39
+ is not created until invoked using ``with``.
40
+
41
+ Parameters
42
+ ----------
43
+ fs: FileSystem
44
+ The file system to use for opening the file. Should be a subclass or duck-type
45
+ with ``fsspec.spec.AbstractFileSystem``
46
+ path: str
47
+ Location to open
48
+ mode: str like 'rb', optional
49
+ Mode of the opened file
50
+ compression: str or None, optional
51
+ Compression to apply
52
+ encoding: str or None, optional
53
+ The encoding to use if opened in text mode.
54
+ errors: str or None, optional
55
+ How to handle encoding errors if opened in text mode.
56
+ newline: None or str
57
+ Passed to TextIOWrapper in text mode, how to handle line endings.
58
+ autoopen: bool
59
+ If True, calls open() immediately. Mostly used by pickle
60
+ pos: int
61
+ If given and autoopen is True, seek to this location immediately
62
+ """
63
+
64
+ def __init__(
65
+ self,
66
+ fs,
67
+ path,
68
+ mode="rb",
69
+ compression=None,
70
+ encoding=None,
71
+ errors=None,
72
+ newline=None,
73
+ ):
74
+ self.fs = fs
75
+ self.path = path
76
+ self.mode = mode
77
+ self.compression = get_compression(path, compression)
78
+ self.encoding = encoding
79
+ self.errors = errors
80
+ self.newline = newline
81
+ self.fobjects = []
82
+
83
+ def __reduce__(self):
84
+ return (
85
+ OpenFile,
86
+ (
87
+ self.fs,
88
+ self.path,
89
+ self.mode,
90
+ self.compression,
91
+ self.encoding,
92
+ self.errors,
93
+ self.newline,
94
+ ),
95
+ )
96
+
97
+ def __repr__(self):
98
+ return f"<OpenFile '{self.path}'>"
99
+
100
+ def __enter__(self):
101
+ mode = self.mode.replace("t", "").replace("b", "") + "b"
102
+
103
+ f = self.fs.open(self.path, mode=mode)
104
+
105
+ self.fobjects = [f]
106
+
107
+ if self.compression is not None:
108
+ compress = compr[self.compression]
109
+ f = compress(f, mode=mode[0])
110
+ self.fobjects.append(f)
111
+
112
+ if "b" not in self.mode:
113
+ # assume, for example, that 'r' is equivalent to 'rt' as in builtin
114
+ f = PickleableTextIOWrapper(
115
+ f, encoding=self.encoding, errors=self.errors, newline=self.newline
116
+ )
117
+ self.fobjects.append(f)
118
+
119
+ return self.fobjects[-1]
120
+
121
+ def __exit__(self, *args):
122
+ self.close()
123
+
124
+ @property
125
+ def full_name(self):
126
+ return _unstrip_protocol(self.path, self.fs)
127
+
128
+ def open(self):
129
+ """Materialise this as a real open file without context
130
+
131
+ The OpenFile object should be explicitly closed to avoid enclosed file
132
+ instances persisting. You must, therefore, keep a reference to the OpenFile
133
+ during the life of the file-like it generates.
134
+ """
135
+ return self.__enter__()
136
+
137
+ def close(self):
138
+ """Close all encapsulated file objects"""
139
+ for f in reversed(self.fobjects):
140
+ if "r" not in self.mode and not f.closed:
141
+ f.flush()
142
+ f.close()
143
+ self.fobjects.clear()
144
+
145
+
146
+ class OpenFiles(list):
147
+ """List of OpenFile instances
148
+
149
+ Can be used in a single context, which opens and closes all of the
150
+ contained files. Normal list access to get the elements works as
151
+ normal.
152
+
153
+ A special case is made for caching filesystems - the files will
154
+ be down/uploaded together at the start or end of the context, and
155
+ this may happen concurrently, if the target filesystem supports it.
156
+ """
157
+
158
+ def __init__(self, *args, mode="rb", fs=None):
159
+ self.mode = mode
160
+ self.fs = fs
161
+ self.files = []
162
+ super().__init__(*args)
163
+
164
+ def __enter__(self):
165
+ if self.fs is None:
166
+ raise ValueError("Context has already been used")
167
+
168
+ fs = self.fs
169
+ while True:
170
+ if hasattr(fs, "open_many"):
171
+ # check for concurrent cache download; or set up for upload
172
+ self.files = fs.open_many(self)
173
+ return self.files
174
+ if hasattr(fs, "fs") and fs.fs is not None:
175
+ fs = fs.fs
176
+ else:
177
+ break
178
+ return [s.__enter__() for s in self]
179
+
180
+ def __exit__(self, *args):
181
+ fs = self.fs
182
+ [s.__exit__(*args) for s in self]
183
+ if "r" not in self.mode:
184
+ while True:
185
+ if hasattr(fs, "open_many"):
186
+ # check for concurrent cache upload
187
+ fs.commit_many(self.files)
188
+ return
189
+ if hasattr(fs, "fs") and fs.fs is not None:
190
+ fs = fs.fs
191
+ else:
192
+ break
193
+
194
+ def __getitem__(self, item):
195
+ out = super().__getitem__(item)
196
+ if isinstance(item, slice):
197
+ return OpenFiles(out, mode=self.mode, fs=self.fs)
198
+ return out
199
+
200
+ def __repr__(self):
201
+ return f"<List of {len(self)} OpenFile instances>"
202
+
203
+
204
+ def open_files(
205
+ urlpath,
206
+ mode="rb",
207
+ compression=None,
208
+ encoding="utf8",
209
+ errors=None,
210
+ name_function=None,
211
+ num=1,
212
+ protocol=None,
213
+ newline=None,
214
+ auto_mkdir=True,
215
+ expand=True,
216
+ **kwargs,
217
+ ):
218
+ """Given a path or paths, return a list of ``OpenFile`` objects.
219
+
220
+ For writing, a str path must contain the "*" character, which will be filled
221
+ in by increasing numbers, e.g., "part*" -> "part1", "part2" if num=2.
222
+
223
+ For either reading or writing, can instead provide explicit list of paths.
224
+
225
+ Parameters
226
+ ----------
227
+ urlpath: string or list
228
+ Absolute or relative filepath(s). Prefix with a protocol like ``s3://``
229
+ to read from alternative filesystems. To read from multiple files you
230
+ can pass a globstring or a list of paths, with the caveat that they
231
+ must all have the same protocol.
232
+ mode: 'rb', 'wt', etc.
233
+ compression: string or None
234
+ If given, open file using compression codec. Can either be a compression
235
+ name (a key in ``fsspec.compression.compr``) or "infer" to guess the
236
+ compression from the filename suffix.
237
+ encoding: str
238
+ For text mode only
239
+ errors: None or str
240
+ Passed to TextIOWrapper in text mode
241
+ name_function: function or None
242
+ if opening a set of files for writing, those files do not yet exist,
243
+ so we need to generate their names by formatting the urlpath for
244
+ each sequence number
245
+ num: int [1]
246
+ if writing mode, number of files we expect to create (passed to
247
+ name+function)
248
+ protocol: str or None
249
+ If given, overrides the protocol found in the URL.
250
+ newline: bytes or None
251
+ Used for line terminator in text mode. If None, uses system default;
252
+ if blank, uses no translation.
253
+ auto_mkdir: bool (True)
254
+ If in write mode, this will ensure the target directory exists before
255
+ writing, by calling ``fs.mkdirs(exist_ok=True)``.
256
+ expand: bool
257
+ **kwargs: dict
258
+ Extra options that make sense to a particular storage connection, e.g.
259
+ host, port, username, password, etc.
260
+
261
+ Examples
262
+ --------
263
+ >>> files = open_files('2015-*-*.csv') # doctest: +SKIP
264
+ >>> files = open_files(
265
+ ... 's3://bucket/2015-*-*.csv.gz', compression='gzip'
266
+ ... ) # doctest: +SKIP
267
+
268
+ Returns
269
+ -------
270
+ An ``OpenFiles`` instance, which is a list of ``OpenFile`` objects that can
271
+ be used as a single context
272
+
273
+ Notes
274
+ -----
275
+ For a full list of the available protocols and the implementations that
276
+ they map across to see the latest online documentation:
277
+
278
+ - For implementations built into ``fsspec`` see
279
+ https://filesystem-spec.readthedocs.io/en/latest/api.html#built-in-implementations
280
+ - For implementations in separate packages see
281
+ https://filesystem-spec.readthedocs.io/en/latest/api.html#other-known-implementations
282
+ """
283
+ fs, fs_token, paths = get_fs_token_paths(
284
+ urlpath,
285
+ mode,
286
+ num=num,
287
+ name_function=name_function,
288
+ storage_options=kwargs,
289
+ protocol=protocol,
290
+ expand=expand,
291
+ )
292
+ if fs.protocol == "file":
293
+ fs.auto_mkdir = auto_mkdir
294
+ elif "r" not in mode and auto_mkdir:
295
+ parents = {fs._parent(path) for path in paths}
296
+ for parent in parents:
297
+ try:
298
+ fs.makedirs(parent, exist_ok=True)
299
+ except PermissionError:
300
+ pass
301
+ return OpenFiles(
302
+ [
303
+ OpenFile(
304
+ fs,
305
+ path,
306
+ mode=mode,
307
+ compression=compression,
308
+ encoding=encoding,
309
+ errors=errors,
310
+ newline=newline,
311
+ )
312
+ for path in paths
313
+ ],
314
+ mode=mode,
315
+ fs=fs,
316
+ )
317
+
318
+
319
+ def _un_chain(path, kwargs):
320
+ x = re.compile(".*[^a-z]+.*") # test for non protocol-like single word
321
+ bits = (
322
+ [p if "://" in p or x.match(p) else p + "://" for p in path.split("::")]
323
+ if "::" in path
324
+ else [path]
325
+ )
326
+ # [[url, protocol, kwargs], ...]
327
+ out = []
328
+ previous_bit = None
329
+ kwargs = kwargs.copy()
330
+ for bit in reversed(bits):
331
+ protocol = kwargs.pop("protocol", None) or split_protocol(bit)[0] or "file"
332
+ cls = get_filesystem_class(protocol)
333
+ extra_kwargs = cls._get_kwargs_from_urls(bit)
334
+ kws = kwargs.pop(protocol, {})
335
+ if bit is bits[0]:
336
+ kws.update(kwargs)
337
+ kw = dict(**extra_kwargs, **kws)
338
+ bit = cls._strip_protocol(bit)
339
+ if (
340
+ protocol in {"blockcache", "filecache", "simplecache"}
341
+ and "target_protocol" not in kw
342
+ ):
343
+ bit = previous_bit
344
+ out.append((bit, protocol, kw))
345
+ previous_bit = bit
346
+ out = list(reversed(out))
347
+ return out
348
+
349
+
350
+ def url_to_fs(url, **kwargs):
351
+ """
352
+ Turn fully-qualified and potentially chained URL into filesystem instance
353
+
354
+ Parameters
355
+ ----------
356
+ url : str
357
+ The fsspec-compatible URL
358
+ **kwargs: dict
359
+ Extra options that make sense to a particular storage connection, e.g.
360
+ host, port, username, password, etc.
361
+
362
+ Returns
363
+ -------
364
+ filesystem : FileSystem
365
+ The new filesystem discovered from ``url`` and created with
366
+ ``**kwargs``.
367
+ urlpath : str
368
+ The file-systems-specific URL for ``url``.
369
+ """
370
+ # non-FS arguments that appear in fsspec.open()
371
+ # inspect could keep this in sync with open()'s signature
372
+ known_kwargs = {
373
+ "compression",
374
+ "encoding",
375
+ "errors",
376
+ "expand",
377
+ "mode",
378
+ "name_function",
379
+ "newline",
380
+ "num",
381
+ }
382
+ kwargs = {k: v for k, v in kwargs.items() if k not in known_kwargs}
383
+ chain = _un_chain(url, kwargs)
384
+ inkwargs = {}
385
+ # Reverse iterate the chain, creating a nested target_* structure
386
+ for i, ch in enumerate(reversed(chain)):
387
+ urls, protocol, kw = ch
388
+ if i == len(chain) - 1:
389
+ inkwargs = dict(**kw, **inkwargs)
390
+ continue
391
+ inkwargs["target_options"] = dict(**kw, **inkwargs)
392
+ inkwargs["target_protocol"] = protocol
393
+ inkwargs["fo"] = urls
394
+ urlpath, protocol, _ = chain[0]
395
+ fs = filesystem(protocol, **inkwargs)
396
+ return fs, urlpath
397
+
398
+
399
+ def open(
400
+ urlpath,
401
+ mode="rb",
402
+ compression=None,
403
+ encoding="utf8",
404
+ errors=None,
405
+ protocol=None,
406
+ newline=None,
407
+ **kwargs,
408
+ ):
409
+ """Given a path or paths, return one ``OpenFile`` object.
410
+
411
+ Parameters
412
+ ----------
413
+ urlpath: string or list
414
+ Absolute or relative filepath. Prefix with a protocol like ``s3://``
415
+ to read from alternative filesystems. Should not include glob
416
+ character(s).
417
+ mode: 'rb', 'wt', etc.
418
+ compression: string or None
419
+ If given, open file using compression codec. Can either be a compression
420
+ name (a key in ``fsspec.compression.compr``) or "infer" to guess the
421
+ compression from the filename suffix.
422
+ encoding: str
423
+ For text mode only
424
+ errors: None or str
425
+ Passed to TextIOWrapper in text mode
426
+ protocol: str or None
427
+ If given, overrides the protocol found in the URL.
428
+ newline: bytes or None
429
+ Used for line terminator in text mode. If None, uses system default;
430
+ if blank, uses no translation.
431
+ **kwargs: dict
432
+ Extra options that make sense to a particular storage connection, e.g.
433
+ host, port, username, password, etc.
434
+
435
+ Examples
436
+ --------
437
+ >>> openfile = open('2015-01-01.csv') # doctest: +SKIP
438
+ >>> openfile = open(
439
+ ... 's3://bucket/2015-01-01.csv.gz', compression='gzip'
440
+ ... ) # doctest: +SKIP
441
+ >>> with openfile as f:
442
+ ... df = pd.read_csv(f) # doctest: +SKIP
443
+ ...
444
+
445
+ Returns
446
+ -------
447
+ ``OpenFile`` object.
448
+
449
+ Notes
450
+ -----
451
+ For a full list of the available protocols and the implementations that
452
+ they map across to see the latest online documentation:
453
+
454
+ - For implementations built into ``fsspec`` see
455
+ https://filesystem-spec.readthedocs.io/en/latest/api.html#built-in-implementations
456
+ - For implementations in separate packages see
457
+ https://filesystem-spec.readthedocs.io/en/latest/api.html#other-known-implementations
458
+ """
459
+ out = open_files(
460
+ urlpath=[urlpath],
461
+ mode=mode,
462
+ compression=compression,
463
+ encoding=encoding,
464
+ errors=errors,
465
+ protocol=protocol,
466
+ newline=newline,
467
+ expand=False,
468
+ **kwargs,
469
+ )
470
+ if not out:
471
+ raise FileNotFoundError(urlpath)
472
+ return out[0]
473
+
474
+
475
+ def open_local(
476
+ url: str | list[str] | Path | list[Path],
477
+ mode: str = "rb",
478
+ **storage_options: dict,
479
+ ) -> str | list[str]:
480
+ """Open file(s) which can be resolved to local
481
+
482
+ For files which either are local, or get downloaded upon open
483
+ (e.g., by file caching)
484
+
485
+ Parameters
486
+ ----------
487
+ url: str or list(str)
488
+ mode: str
489
+ Must be read mode
490
+ storage_options:
491
+ passed on to FS for or used by open_files (e.g., compression)
492
+ """
493
+ if "r" not in mode:
494
+ raise ValueError("Can only ensure local files when reading")
495
+ of = open_files(url, mode=mode, **storage_options)
496
+ if not getattr(of[0].fs, "local_file", False):
497
+ raise ValueError(
498
+ "open_local can only be used on a filesystem which"
499
+ " has attribute local_file=True"
500
+ )
501
+ with of as files:
502
+ paths = [f.name for f in files]
503
+ if (isinstance(url, str) and not has_magic(url)) or isinstance(url, Path):
504
+ return paths[0]
505
+ return paths
506
+
507
+
508
+ def get_compression(urlpath, compression):
509
+ if compression == "infer":
510
+ compression = infer_compression(urlpath)
511
+ if compression is not None and compression not in compr:
512
+ raise ValueError(f"Compression type {compression} not supported")
513
+ return compression
514
+
515
+
516
+ def split_protocol(urlpath):
517
+ """Return protocol, path pair"""
518
+ urlpath = stringify_path(urlpath)
519
+ if "://" in urlpath:
520
+ protocol, path = urlpath.split("://", 1)
521
+ if len(protocol) > 1:
522
+ # excludes Windows paths
523
+ return protocol, path
524
+ if urlpath.startswith("data:"):
525
+ return urlpath.split(":", 1)
526
+ return None, urlpath
527
+
528
+
529
+ def strip_protocol(urlpath):
530
+ """Return only path part of full URL, according to appropriate backend"""
531
+ protocol, _ = split_protocol(urlpath)
532
+ cls = get_filesystem_class(protocol)
533
+ return cls._strip_protocol(urlpath)
534
+
535
+
536
+ def expand_paths_if_needed(paths, mode, num, fs, name_function):
537
+ """Expand paths if they have a ``*`` in them (write mode) or any of ``*?[]``
538
+ in them (read mode).
539
+
540
+ :param paths: list of paths
541
+ mode: str
542
+ Mode in which to open files.
543
+ num: int
544
+ If opening in writing mode, number of files we expect to create.
545
+ fs: filesystem object
546
+ name_function: callable
547
+ If opening in writing mode, this callable is used to generate path
548
+ names. Names are generated for each partition by
549
+ ``urlpath.replace('*', name_function(partition_index))``.
550
+ :return: list of paths
551
+ """
552
+ expanded_paths = []
553
+ paths = list(paths)
554
+
555
+ if "w" in mode: # read mode
556
+ if sum([1 for p in paths if "*" in p]) > 1:
557
+ raise ValueError(
558
+ "When writing data, only one filename mask can be specified."
559
+ )
560
+ num = max(num, len(paths))
561
+
562
+ for curr_path in paths:
563
+ if "*" in curr_path:
564
+ # expand using name_function
565
+ expanded_paths.extend(_expand_paths(curr_path, name_function, num))
566
+ else:
567
+ expanded_paths.append(curr_path)
568
+ # if we generated more paths that asked for, trim the list
569
+ if len(expanded_paths) > num:
570
+ expanded_paths = expanded_paths[:num]
571
+
572
+ else: # read mode
573
+ for curr_path in paths:
574
+ if has_magic(curr_path):
575
+ # expand using glob
576
+ expanded_paths.extend(fs.glob(curr_path))
577
+ else:
578
+ expanded_paths.append(curr_path)
579
+
580
+ return expanded_paths
581
+
582
+
583
+ def get_fs_token_paths(
584
+ urlpath,
585
+ mode="rb",
586
+ num=1,
587
+ name_function=None,
588
+ storage_options=None,
589
+ protocol=None,
590
+ expand=True,
591
+ ):
592
+ """Filesystem, deterministic token, and paths from a urlpath and options.
593
+
594
+ Parameters
595
+ ----------
596
+ urlpath: string or iterable
597
+ Absolute or relative filepath, URL (may include protocols like
598
+ ``s3://``), or globstring pointing to data.
599
+ mode: str, optional
600
+ Mode in which to open files.
601
+ num: int, optional
602
+ If opening in writing mode, number of files we expect to create.
603
+ name_function: callable, optional
604
+ If opening in writing mode, this callable is used to generate path
605
+ names. Names are generated for each partition by
606
+ ``urlpath.replace('*', name_function(partition_index))``.
607
+ storage_options: dict, optional
608
+ Additional keywords to pass to the filesystem class.
609
+ protocol: str or None
610
+ To override the protocol specifier in the URL
611
+ expand: bool
612
+ Expand string paths for writing, assuming the path is a directory
613
+ """
614
+ if isinstance(urlpath, (list, tuple, set)):
615
+ if not urlpath:
616
+ raise ValueError("empty urlpath sequence")
617
+ urlpath0 = stringify_path(list(urlpath)[0])
618
+ else:
619
+ urlpath0 = stringify_path(urlpath)
620
+ storage_options = storage_options or {}
621
+ if protocol:
622
+ storage_options["protocol"] = protocol
623
+ chain = _un_chain(urlpath0, storage_options or {})
624
+ inkwargs = {}
625
+ # Reverse iterate the chain, creating a nested target_* structure
626
+ for i, ch in enumerate(reversed(chain)):
627
+ urls, nested_protocol, kw = ch
628
+ if i == len(chain) - 1:
629
+ inkwargs = dict(**kw, **inkwargs)
630
+ continue
631
+ inkwargs["target_options"] = dict(**kw, **inkwargs)
632
+ inkwargs["target_protocol"] = nested_protocol
633
+ inkwargs["fo"] = urls
634
+ paths, protocol, _ = chain[0]
635
+ fs = filesystem(protocol, **inkwargs)
636
+ if isinstance(urlpath, (list, tuple, set)):
637
+ pchains = [
638
+ _un_chain(stringify_path(u), storage_options or {})[0] for u in urlpath
639
+ ]
640
+ if len({pc[1] for pc in pchains}) > 1:
641
+ raise ValueError("Protocol mismatch getting fs from %s", urlpath)
642
+ paths = [pc[0] for pc in pchains]
643
+ else:
644
+ paths = fs._strip_protocol(paths)
645
+ if isinstance(paths, (list, tuple, set)):
646
+ paths = expand_paths_if_needed(paths, mode, num, fs, name_function)
647
+ else:
648
+ if "w" in mode and expand:
649
+ paths = _expand_paths(paths, name_function, num)
650
+ elif "x" in mode and expand:
651
+ paths = _expand_paths(paths, name_function, num)
652
+ elif "*" in paths:
653
+ paths = [f for f in sorted(fs.glob(paths)) if not fs.isdir(f)]
654
+ else:
655
+ paths = [paths]
656
+
657
+ return fs, fs._fs_token, paths
658
+
659
+
660
+ def _expand_paths(path, name_function, num):
661
+ if isinstance(path, str):
662
+ if path.count("*") > 1:
663
+ raise ValueError("Output path spec must contain exactly one '*'.")
664
+ elif "*" not in path:
665
+ path = os.path.join(path, "*.part")
666
+
667
+ if name_function is None:
668
+ name_function = build_name_function(num - 1)
669
+
670
+ paths = [path.replace("*", name_function(i)) for i in range(num)]
671
+ if paths != sorted(paths):
672
+ logger.warning(
673
+ "In order to preserve order between partitions"
674
+ " paths created with ``name_function`` should "
675
+ "sort to partition order"
676
+ )
677
+ elif isinstance(path, (tuple, list)):
678
+ assert len(path) == num
679
+ paths = list(path)
680
+ else:
681
+ raise ValueError(
682
+ "Path should be either\n"
683
+ "1. A list of paths: ['foo.json', 'bar.json', ...]\n"
684
+ "2. A directory: 'foo/\n"
685
+ "3. A path with a '*' in it: 'foo.*.json'"
686
+ )
687
+ return paths
688
+
689
+
690
+ class PickleableTextIOWrapper(io.TextIOWrapper):
691
+ """TextIOWrapper cannot be pickled. This solves it.
692
+
693
+ Requires that ``buffer`` be pickleable, which all instances of
694
+ AbstractBufferedFile are.
695
+ """
696
+
697
+ def __init__(
698
+ self,
699
+ buffer,
700
+ encoding=None,
701
+ errors=None,
702
+ newline=None,
703
+ line_buffering=False,
704
+ write_through=False,
705
+ ):
706
+ self.args = buffer, encoding, errors, newline, line_buffering, write_through
707
+ super().__init__(*self.args)
708
+
709
+ def __reduce__(self):
710
+ return PickleableTextIOWrapper, self.args
tuning-competition-baseline/.venv/lib/python3.11/site-packages/fsspec/dircache.py ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import time
2
+ from collections.abc import MutableMapping
3
+ from functools import lru_cache
4
+
5
+
6
+ class DirCache(MutableMapping):
7
+ """
8
+ Caching of directory listings, in a structure like::
9
+
10
+ {"path0": [
11
+ {"name": "path0/file0",
12
+ "size": 123,
13
+ "type": "file",
14
+ ...
15
+ },
16
+ {"name": "path0/file1",
17
+ },
18
+ ...
19
+ ],
20
+ "path1": [...]
21
+ }
22
+
23
+ Parameters to this class control listing expiry or indeed turn
24
+ caching off
25
+ """
26
+
27
+ def __init__(
28
+ self,
29
+ use_listings_cache=True,
30
+ listings_expiry_time=None,
31
+ max_paths=None,
32
+ **kwargs,
33
+ ):
34
+ """
35
+
36
+ Parameters
37
+ ----------
38
+ use_listings_cache: bool
39
+ If False, this cache never returns items, but always reports KeyError,
40
+ and setting items has no effect
41
+ listings_expiry_time: int or float (optional)
42
+ Time in seconds that a listing is considered valid. If None,
43
+ listings do not expire.
44
+ max_paths: int (optional)
45
+ The number of most recent listings that are considered valid; 'recent'
46
+ refers to when the entry was set.
47
+ """
48
+ self._cache = {}
49
+ self._times = {}
50
+ if max_paths:
51
+ self._q = lru_cache(max_paths + 1)(lambda key: self._cache.pop(key, None))
52
+ self.use_listings_cache = use_listings_cache
53
+ self.listings_expiry_time = listings_expiry_time
54
+ self.max_paths = max_paths
55
+
56
+ def __getitem__(self, item):
57
+ if self.listings_expiry_time is not None:
58
+ if self._times.get(item, 0) - time.time() < -self.listings_expiry_time:
59
+ del self._cache[item]
60
+ if self.max_paths:
61
+ self._q(item)
62
+ return self._cache[item] # maybe raises KeyError
63
+
64
+ def clear(self):
65
+ self._cache.clear()
66
+
67
+ def __len__(self):
68
+ return len(self._cache)
69
+
70
+ def __contains__(self, item):
71
+ try:
72
+ self[item]
73
+ return True
74
+ except KeyError:
75
+ return False
76
+
77
+ def __setitem__(self, key, value):
78
+ if not self.use_listings_cache:
79
+ return
80
+ if self.max_paths:
81
+ self._q(key)
82
+ self._cache[key] = value
83
+ if self.listings_expiry_time is not None:
84
+ self._times[key] = time.time()
85
+
86
+ def __delitem__(self, key):
87
+ del self._cache[key]
88
+
89
+ def __iter__(self):
90
+ entries = list(self._cache)
91
+
92
+ return (k for k in entries if k in self)
93
+
94
+ def __reduce__(self):
95
+ return (
96
+ DirCache,
97
+ (self.use_listings_cache, self.listings_expiry_time, self.max_paths),
98
+ )
tuning-competition-baseline/.venv/lib/python3.11/site-packages/fsspec/exceptions.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ fsspec user-defined exception classes
3
+ """
4
+ import asyncio
5
+
6
+
7
+ class BlocksizeMismatchError(ValueError):
8
+ """
9
+ Raised when a cached file is opened with a different blocksize than it was
10
+ written with
11
+ """
12
+
13
+
14
+ class FSTimeoutError(asyncio.TimeoutError):
15
+ """
16
+ Raised when a fsspec function timed out occurs
17
+ """
tuning-competition-baseline/.venv/lib/python3.11/site-packages/fsspec/fuse.py ADDED
@@ -0,0 +1,324 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import logging
3
+ import os
4
+ import stat
5
+ import threading
6
+ import time
7
+ from errno import EIO, ENOENT
8
+
9
+ from fuse import FUSE, FuseOSError, LoggingMixIn, Operations
10
+
11
+ from fsspec import __version__
12
+ from fsspec.core import url_to_fs
13
+
14
+ logger = logging.getLogger("fsspec.fuse")
15
+
16
+
17
+ class FUSEr(Operations):
18
+ def __init__(self, fs, path, ready_file=False):
19
+ self.fs = fs
20
+ self.cache = {}
21
+ self.root = path.rstrip("/") + "/"
22
+ self.counter = 0
23
+ logger.info("Starting FUSE at %s", path)
24
+ self._ready_file = ready_file
25
+
26
+ def getattr(self, path, fh=None):
27
+ logger.debug("getattr %s", path)
28
+ if self._ready_file and path in ["/.fuse_ready", ".fuse_ready"]:
29
+ return {"type": "file", "st_size": 5}
30
+
31
+ path = "".join([self.root, path.lstrip("/")]).rstrip("/")
32
+ try:
33
+ info = self.fs.info(path)
34
+ except FileNotFoundError:
35
+ raise FuseOSError(ENOENT)
36
+
37
+ data = {"st_uid": info.get("uid", 1000), "st_gid": info.get("gid", 1000)}
38
+ perm = info.get("mode", 0o777)
39
+
40
+ if info["type"] != "file":
41
+ data["st_mode"] = stat.S_IFDIR | perm
42
+ data["st_size"] = 0
43
+ data["st_blksize"] = 0
44
+ else:
45
+ data["st_mode"] = stat.S_IFREG | perm
46
+ data["st_size"] = info["size"]
47
+ data["st_blksize"] = 5 * 2**20
48
+ data["st_nlink"] = 1
49
+ data["st_atime"] = info["atime"] if "atime" in info else time.time()
50
+ data["st_ctime"] = info["ctime"] if "ctime" in info else time.time()
51
+ data["st_mtime"] = info["mtime"] if "mtime" in info else time.time()
52
+ return data
53
+
54
+ def readdir(self, path, fh):
55
+ logger.debug("readdir %s", path)
56
+ path = "".join([self.root, path.lstrip("/")])
57
+ files = self.fs.ls(path, False)
58
+ files = [os.path.basename(f.rstrip("/")) for f in files]
59
+ return [".", ".."] + files
60
+
61
+ def mkdir(self, path, mode):
62
+ path = "".join([self.root, path.lstrip("/")])
63
+ self.fs.mkdir(path)
64
+ return 0
65
+
66
+ def rmdir(self, path):
67
+ path = "".join([self.root, path.lstrip("/")])
68
+ self.fs.rmdir(path)
69
+ return 0
70
+
71
+ def read(self, path, size, offset, fh):
72
+ logger.debug("read %s", (path, size, offset))
73
+ if self._ready_file and path in ["/.fuse_ready", ".fuse_ready"]:
74
+ # status indicator
75
+ return b"ready"
76
+
77
+ f = self.cache[fh]
78
+ f.seek(offset)
79
+ out = f.read(size)
80
+ return out
81
+
82
+ def write(self, path, data, offset, fh):
83
+ logger.debug("write %s", (path, offset))
84
+ f = self.cache[fh]
85
+ f.seek(offset)
86
+ f.write(data)
87
+ return len(data)
88
+
89
+ def create(self, path, flags, fi=None):
90
+ logger.debug("create %s", (path, flags))
91
+ fn = "".join([self.root, path.lstrip("/")])
92
+ self.fs.touch(fn) # OS will want to get attributes immediately
93
+ f = self.fs.open(fn, "wb")
94
+ self.cache[self.counter] = f
95
+ self.counter += 1
96
+ return self.counter - 1
97
+
98
+ def open(self, path, flags):
99
+ logger.debug("open %s", (path, flags))
100
+ fn = "".join([self.root, path.lstrip("/")])
101
+ if flags % 2 == 0:
102
+ # read
103
+ mode = "rb"
104
+ else:
105
+ # write/create
106
+ mode = "wb"
107
+ self.cache[self.counter] = self.fs.open(fn, mode)
108
+ self.counter += 1
109
+ return self.counter - 1
110
+
111
+ def truncate(self, path, length, fh=None):
112
+ fn = "".join([self.root, path.lstrip("/")])
113
+ if length != 0:
114
+ raise NotImplementedError
115
+ # maybe should be no-op since open with write sets size to zero anyway
116
+ self.fs.touch(fn)
117
+
118
+ def unlink(self, path):
119
+ fn = "".join([self.root, path.lstrip("/")])
120
+ try:
121
+ self.fs.rm(fn, False)
122
+ except (OSError, FileNotFoundError):
123
+ raise FuseOSError(EIO)
124
+
125
+ def release(self, path, fh):
126
+ try:
127
+ if fh in self.cache:
128
+ f = self.cache[fh]
129
+ f.close()
130
+ self.cache.pop(fh)
131
+ except Exception as e:
132
+ print(e)
133
+ return 0
134
+
135
+ def chmod(self, path, mode):
136
+ if hasattr(self.fs, "chmod"):
137
+ path = "".join([self.root, path.lstrip("/")])
138
+ return self.fs.chmod(path, mode)
139
+ raise NotImplementedError
140
+
141
+
142
+ def run(
143
+ fs,
144
+ path,
145
+ mount_point,
146
+ foreground=True,
147
+ threads=False,
148
+ ready_file=False,
149
+ ops_class=FUSEr,
150
+ ):
151
+ """Mount stuff in a local directory
152
+
153
+ This uses fusepy to make it appear as if a given path on an fsspec
154
+ instance is in fact resident within the local file-system.
155
+
156
+ This requires that fusepy by installed, and that FUSE be available on
157
+ the system (typically requiring a package to be installed with
158
+ apt, yum, brew, etc.).
159
+
160
+ Parameters
161
+ ----------
162
+ fs: file-system instance
163
+ From one of the compatible implementations
164
+ path: str
165
+ Location on that file-system to regard as the root directory to
166
+ mount. Note that you typically should include the terminating "/"
167
+ character.
168
+ mount_point: str
169
+ An empty directory on the local file-system where the contents of
170
+ the remote path will appear.
171
+ foreground: bool
172
+ Whether or not calling this function will block. Operation will
173
+ typically be more stable if True.
174
+ threads: bool
175
+ Whether or not to create threads when responding to file operations
176
+ within the mounter directory. Operation will typically be more
177
+ stable if False.
178
+ ready_file: bool
179
+ Whether the FUSE process is ready. The ``.fuse_ready`` file will
180
+ exist in the ``mount_point`` directory if True. Debugging purpose.
181
+ ops_class: FUSEr or Subclass of FUSEr
182
+ To override the default behavior of FUSEr. For Example, logging
183
+ to file.
184
+
185
+ """
186
+ func = lambda: FUSE(
187
+ ops_class(fs, path, ready_file=ready_file),
188
+ mount_point,
189
+ nothreads=not threads,
190
+ foreground=foreground,
191
+ )
192
+ if not foreground:
193
+ th = threading.Thread(target=func)
194
+ th.daemon = True
195
+ th.start()
196
+ return th
197
+ else: # pragma: no cover
198
+ try:
199
+ func()
200
+ except KeyboardInterrupt:
201
+ pass
202
+
203
+
204
+ def main(args):
205
+ """Mount filesystem from chained URL to MOUNT_POINT.
206
+
207
+ Examples:
208
+
209
+ python3 -m fsspec.fuse memory /usr/share /tmp/mem
210
+
211
+ python3 -m fsspec.fuse local /tmp/source /tmp/local \\
212
+ -l /tmp/fsspecfuse.log
213
+
214
+ You can also mount chained-URLs and use special settings:
215
+
216
+ python3 -m fsspec.fuse 'filecache::zip::file://data.zip' \\
217
+ / /tmp/zip \\
218
+ -o 'filecache-cache_storage=/tmp/simplecache'
219
+
220
+ You can specify the type of the setting by using `[int]` or `[bool]`,
221
+ (`true`, `yes`, `1` represents the Boolean value `True`):
222
+
223
+ python3 -m fsspec.fuse 'simplecache::ftp://ftp1.at.proftpd.org' \\
224
+ /historic/packages/RPMS /tmp/ftp \\
225
+ -o 'simplecache-cache_storage=/tmp/simplecache' \\
226
+ -o 'simplecache-check_files=false[bool]' \\
227
+ -o 'ftp-listings_expiry_time=60[int]' \\
228
+ -o 'ftp-username=anonymous' \\
229
+ -o 'ftp-password=xieyanbo'
230
+ """
231
+
232
+ class RawDescriptionArgumentParser(argparse.ArgumentParser):
233
+ def format_help(self):
234
+ usage = super().format_help()
235
+ parts = usage.split("\n\n")
236
+ parts[1] = self.description.rstrip()
237
+ return "\n\n".join(parts)
238
+
239
+ parser = RawDescriptionArgumentParser(prog="fsspec.fuse", description=main.__doc__)
240
+ parser.add_argument("--version", action="version", version=__version__)
241
+ parser.add_argument("url", type=str, help="fs url")
242
+ parser.add_argument("source_path", type=str, help="source directory in fs")
243
+ parser.add_argument("mount_point", type=str, help="local directory")
244
+ parser.add_argument(
245
+ "-o",
246
+ "--option",
247
+ action="append",
248
+ help="Any options of protocol included in the chained URL",
249
+ )
250
+ parser.add_argument(
251
+ "-l", "--log-file", type=str, help="Logging FUSE debug info (Default: '')"
252
+ )
253
+ parser.add_argument(
254
+ "-f",
255
+ "--foreground",
256
+ action="store_false",
257
+ help="Running in foreground or not (Default: False)",
258
+ )
259
+ parser.add_argument(
260
+ "-t",
261
+ "--threads",
262
+ action="store_false",
263
+ help="Running with threads support (Default: False)",
264
+ )
265
+ parser.add_argument(
266
+ "-r",
267
+ "--ready-file",
268
+ action="store_false",
269
+ help="The `.fuse_ready` file will exist after FUSE is ready. "
270
+ "(Debugging purpose, Default: False)",
271
+ )
272
+ args = parser.parse_args(args)
273
+
274
+ kwargs = {}
275
+ for item in args.option or []:
276
+ key, sep, value = item.partition("=")
277
+ if not sep:
278
+ parser.error(message=f"Wrong option: {item!r}")
279
+ val = value.lower()
280
+ if val.endswith("[int]"):
281
+ value = int(value[: -len("[int]")])
282
+ elif val.endswith("[bool]"):
283
+ value = val[: -len("[bool]")] in ["1", "yes", "true"]
284
+
285
+ if "-" in key:
286
+ fs_name, setting_name = key.split("-", 1)
287
+ if fs_name in kwargs:
288
+ kwargs[fs_name][setting_name] = value
289
+ else:
290
+ kwargs[fs_name] = {setting_name: value}
291
+ else:
292
+ kwargs[key] = value
293
+
294
+ if args.log_file:
295
+ logging.basicConfig(
296
+ level=logging.DEBUG,
297
+ filename=args.log_file,
298
+ format="%(asctime)s %(message)s",
299
+ )
300
+
301
+ class LoggingFUSEr(FUSEr, LoggingMixIn):
302
+ pass
303
+
304
+ fuser = LoggingFUSEr
305
+ else:
306
+ fuser = FUSEr
307
+
308
+ fs, url_path = url_to_fs(args.url, **kwargs)
309
+ logger.debug("Mounting %s to %s", url_path, str(args.mount_point))
310
+ run(
311
+ fs,
312
+ args.source_path,
313
+ args.mount_point,
314
+ foreground=args.foreground,
315
+ threads=args.threads,
316
+ ready_file=args.ready_file,
317
+ ops_class=fuser,
318
+ )
319
+
320
+
321
+ if __name__ == "__main__":
322
+ import sys
323
+
324
+ main(sys.argv[1:])
tuning-competition-baseline/.venv/lib/python3.11/site-packages/fsspec/generic.py ADDED
@@ -0,0 +1,403 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import inspect
4
+ import logging
5
+ import os
6
+ import shutil
7
+ import uuid
8
+ from typing import Optional
9
+
10
+ from .asyn import AsyncFileSystem, _run_coros_in_chunks, sync_wrapper
11
+ from .callbacks import DEFAULT_CALLBACK
12
+ from .core import filesystem, get_filesystem_class, split_protocol, url_to_fs
13
+
14
+ _generic_fs = {}
15
+ logger = logging.getLogger("fsspec.generic")
16
+
17
+
18
+ def set_generic_fs(protocol, **storage_options):
19
+ _generic_fs[protocol] = filesystem(protocol, **storage_options)
20
+
21
+
22
+ default_method = "default"
23
+
24
+
25
+ def _resolve_fs(url, method=None, protocol=None, storage_options=None):
26
+ """Pick instance of backend FS"""
27
+ method = method or default_method
28
+ protocol = protocol or split_protocol(url)[0]
29
+ storage_options = storage_options or {}
30
+ if method == "default":
31
+ return filesystem(protocol)
32
+ if method == "generic":
33
+ return _generic_fs[protocol]
34
+ if method == "current":
35
+ cls = get_filesystem_class(protocol)
36
+ return cls.current()
37
+ if method == "options":
38
+ fs, _ = url_to_fs(url, **storage_options.get(protocol, {}))
39
+ return fs
40
+ raise ValueError(f"Unknown FS resolution method: {method}")
41
+
42
+
43
+ def rsync(
44
+ source,
45
+ destination,
46
+ delete_missing=False,
47
+ source_field="size",
48
+ dest_field="size",
49
+ update_cond="different",
50
+ inst_kwargs=None,
51
+ fs=None,
52
+ **kwargs,
53
+ ):
54
+ """Sync files between two directory trees
55
+
56
+ (experimental)
57
+
58
+ Parameters
59
+ ----------
60
+ source: str
61
+ Root of the directory tree to take files from. This must be a directory, but
62
+ do not include any terminating "/" character
63
+ destination: str
64
+ Root path to copy into. The contents of this location should be
65
+ identical to the contents of ``source`` when done. This will be made a
66
+ directory, and the terminal "/" should not be included.
67
+ delete_missing: bool
68
+ If there are paths in the destination that don't exist in the
69
+ source and this is True, delete them. Otherwise, leave them alone.
70
+ source_field: str | callable
71
+ If ``update_field`` is "different", this is the key in the info
72
+ of source files to consider for difference. Maybe a function of the
73
+ info dict.
74
+ dest_field: str | callable
75
+ If ``update_field`` is "different", this is the key in the info
76
+ of destination files to consider for difference. May be a function of
77
+ the info dict.
78
+ update_cond: "different"|"always"|"never"
79
+ If "always", every file is copied, regardless of whether it exists in
80
+ the destination. If "never", files that exist in the destination are
81
+ not copied again. If "different" (default), only copy if the info
82
+ fields given by ``source_field`` and ``dest_field`` (usually "size")
83
+ are different. Other comparisons may be added in the future.
84
+ inst_kwargs: dict|None
85
+ If ``fs`` is None, use this set of keyword arguments to make a
86
+ GenericFileSystem instance
87
+ fs: GenericFileSystem|None
88
+ Instance to use if explicitly given. The instance defines how to
89
+ to make downstream file system instances from paths.
90
+ """
91
+ fs = fs or GenericFileSystem(**(inst_kwargs or {}))
92
+ source = fs._strip_protocol(source)
93
+ destination = fs._strip_protocol(destination)
94
+ allfiles = fs.find(source, withdirs=True, detail=True)
95
+ if not fs.isdir(source):
96
+ raise ValueError("Can only rsync on a directory")
97
+ otherfiles = fs.find(destination, withdirs=True, detail=True)
98
+ dirs = [
99
+ a
100
+ for a, v in allfiles.items()
101
+ if v["type"] == "directory" and a.replace(source, destination) not in otherfiles
102
+ ]
103
+ logger.debug(f"{len(dirs)} directories to create")
104
+ if dirs:
105
+ fs.make_many_dirs(
106
+ [dirn.replace(source, destination) for dirn in dirs], exist_ok=True
107
+ )
108
+ allfiles = {a: v for a, v in allfiles.items() if v["type"] == "file"}
109
+ logger.debug(f"{len(allfiles)} files to consider for copy")
110
+ to_delete = [
111
+ o
112
+ for o, v in otherfiles.items()
113
+ if o.replace(destination, source) not in allfiles and v["type"] == "file"
114
+ ]
115
+ for k, v in allfiles.copy().items():
116
+ otherfile = k.replace(source, destination)
117
+ if otherfile in otherfiles:
118
+ if update_cond == "always":
119
+ allfiles[k] = otherfile
120
+ elif update_cond == "different":
121
+ inf1 = source_field(v) if callable(source_field) else v[source_field]
122
+ v2 = otherfiles[otherfile]
123
+ inf2 = dest_field(v2) if callable(dest_field) else v2[dest_field]
124
+ if inf1 != inf2:
125
+ # details mismatch, make copy
126
+ allfiles[k] = otherfile
127
+ else:
128
+ # details match, don't copy
129
+ allfiles.pop(k)
130
+ else:
131
+ # file not in target yet
132
+ allfiles[k] = otherfile
133
+ logger.debug(f"{len(allfiles)} files to copy")
134
+ if allfiles:
135
+ source_files, target_files = zip(*allfiles.items())
136
+ fs.cp(source_files, target_files, **kwargs)
137
+ logger.debug(f"{len(to_delete)} files to delete")
138
+ if delete_missing:
139
+ fs.rm(to_delete)
140
+
141
+
142
+ class GenericFileSystem(AsyncFileSystem):
143
+ """Wrapper over all other FS types
144
+
145
+ <experimental!>
146
+
147
+ This implementation is a single unified interface to be able to run FS operations
148
+ over generic URLs, and dispatch to the specific implementations using the URL
149
+ protocol prefix.
150
+
151
+ Note: instances of this FS are always async, even if you never use it with any async
152
+ backend.
153
+ """
154
+
155
+ protocol = "generic" # there is no real reason to ever use a protocol with this FS
156
+
157
+ def __init__(self, default_method="default", **kwargs):
158
+ """
159
+
160
+ Parameters
161
+ ----------
162
+ default_method: str (optional)
163
+ Defines how to configure backend FS instances. Options are:
164
+ - "default": instantiate like FSClass(), with no
165
+ extra arguments; this is the default instance of that FS, and can be
166
+ configured via the config system
167
+ - "generic": takes instances from the `_generic_fs` dict in this module,
168
+ which you must populate before use. Keys are by protocol
169
+ - "current": takes the most recently instantiated version of each FS
170
+ """
171
+ self.method = default_method
172
+ super().__init__(**kwargs)
173
+
174
+ def _parent(self, path):
175
+ fs = _resolve_fs(path, self.method)
176
+ return fs.unstrip_protocol(fs._parent(path))
177
+
178
+ def _strip_protocol(self, path):
179
+ # normalization only
180
+ fs = _resolve_fs(path, self.method)
181
+ return fs.unstrip_protocol(fs._strip_protocol(path))
182
+
183
+ async def _find(self, path, maxdepth=None, withdirs=False, detail=False, **kwargs):
184
+ fs = _resolve_fs(path, self.method)
185
+ if fs.async_impl:
186
+ out = await fs._find(
187
+ path, maxdepth=maxdepth, withdirs=withdirs, detail=True, **kwargs
188
+ )
189
+ else:
190
+ out = fs.find(
191
+ path, maxdepth=maxdepth, withdirs=withdirs, detail=True, **kwargs
192
+ )
193
+ result = {}
194
+ for k, v in out.items():
195
+ name = fs.unstrip_protocol(k)
196
+ v["name"] = name
197
+ result[name] = v
198
+ if detail:
199
+ return result
200
+ return list(result)
201
+
202
+ async def _info(self, url, **kwargs):
203
+ fs = _resolve_fs(url, self.method)
204
+ if fs.async_impl:
205
+ out = await fs._info(url, **kwargs)
206
+ else:
207
+ out = fs.info(url, **kwargs)
208
+ out["name"] = fs.unstrip_protocol(out["name"])
209
+ return out
210
+
211
+ async def _ls(
212
+ self,
213
+ url,
214
+ detail=True,
215
+ **kwargs,
216
+ ):
217
+ fs = _resolve_fs(url, self.method)
218
+ if fs.async_impl:
219
+ out = await fs._ls(url, detail=True, **kwargs)
220
+ else:
221
+ out = fs.ls(url, detail=True, **kwargs)
222
+ for o in out:
223
+ o["name"] = fs.unstrip_protocol(o["name"])
224
+ if detail:
225
+ return out
226
+ else:
227
+ return [o["name"] for o in out]
228
+
229
+ async def _cat_file(
230
+ self,
231
+ url,
232
+ **kwargs,
233
+ ):
234
+ fs = _resolve_fs(url, self.method)
235
+ if fs.async_impl:
236
+ return await fs._cat_file(url, **kwargs)
237
+ else:
238
+ return fs.cat_file(url, **kwargs)
239
+
240
+ async def _pipe_file(
241
+ self,
242
+ path,
243
+ value,
244
+ **kwargs,
245
+ ):
246
+ fs = _resolve_fs(path, self.method)
247
+ if fs.async_impl:
248
+ return await fs._pipe_file(path, value, **kwargs)
249
+ else:
250
+ return fs.pipe_file(path, value, **kwargs)
251
+
252
+ async def _rm(self, url, **kwargs):
253
+ urls = url
254
+ if isinstance(urls, str):
255
+ urls = [urls]
256
+ fs = _resolve_fs(urls[0], self.method)
257
+ if fs.async_impl:
258
+ await fs._rm(urls, **kwargs)
259
+ else:
260
+ fs.rm(url, **kwargs)
261
+
262
+ async def _makedirs(self, path, exist_ok=False):
263
+ logger.debug("Make dir %s", path)
264
+ fs = _resolve_fs(path, self.method)
265
+ if fs.async_impl:
266
+ await fs._makedirs(path, exist_ok=exist_ok)
267
+ else:
268
+ fs.makedirs(path, exist_ok=exist_ok)
269
+
270
+ def rsync(self, source, destination, **kwargs):
271
+ """Sync files between two directory trees
272
+
273
+ See `func:rsync` for more details.
274
+ """
275
+ rsync(source, destination, fs=self, **kwargs)
276
+
277
+ async def _cp_file(
278
+ self,
279
+ url,
280
+ url2,
281
+ blocksize=2**20,
282
+ callback=DEFAULT_CALLBACK,
283
+ **kwargs,
284
+ ):
285
+ fs = _resolve_fs(url, self.method)
286
+ fs2 = _resolve_fs(url2, self.method)
287
+ if fs is fs2:
288
+ # pure remote
289
+ if fs.async_impl:
290
+ return await fs._cp_file(url, url2, **kwargs)
291
+ else:
292
+ return fs.cp_file(url, url2, **kwargs)
293
+ kw = {"blocksize": 0, "cache_type": "none"}
294
+ try:
295
+ f1 = (
296
+ await fs.open_async(url, "rb")
297
+ if hasattr(fs, "open_async")
298
+ else fs.open(url, "rb", **kw)
299
+ )
300
+ callback.set_size(await maybe_await(f1.size))
301
+ f2 = (
302
+ await fs2.open_async(url2, "wb")
303
+ if hasattr(fs2, "open_async")
304
+ else fs2.open(url2, "wb", **kw)
305
+ )
306
+ while f1.size is None or f2.tell() < f1.size:
307
+ data = await maybe_await(f1.read(blocksize))
308
+ if f1.size is None and not data:
309
+ break
310
+ await maybe_await(f2.write(data))
311
+ callback.absolute_update(f2.tell())
312
+ finally:
313
+ try:
314
+ await maybe_await(f2.close())
315
+ await maybe_await(f1.close())
316
+ except NameError:
317
+ # fail while opening f1 or f2
318
+ pass
319
+
320
+ async def _make_many_dirs(self, urls, exist_ok=True):
321
+ fs = _resolve_fs(urls[0], self.method)
322
+ if fs.async_impl:
323
+ coros = [fs._makedirs(u, exist_ok=exist_ok) for u in urls]
324
+ await _run_coros_in_chunks(coros)
325
+ else:
326
+ for u in urls:
327
+ fs.makedirs(u, exist_ok=exist_ok)
328
+
329
+ make_many_dirs = sync_wrapper(_make_many_dirs)
330
+
331
+ async def _copy(
332
+ self,
333
+ path1: list[str],
334
+ path2: list[str],
335
+ recursive: bool = False,
336
+ on_error: str = "ignore",
337
+ maxdepth: Optional[int] = None,
338
+ batch_size: Optional[int] = None,
339
+ tempdir: Optional[str] = None,
340
+ **kwargs,
341
+ ):
342
+ if recursive:
343
+ raise NotImplementedError
344
+ fs = _resolve_fs(path1[0], self.method)
345
+ fs2 = _resolve_fs(path2[0], self.method)
346
+ # not expanding paths atm., assume call is from rsync()
347
+ if fs is fs2:
348
+ # pure remote
349
+ if fs.async_impl:
350
+ return await fs._copy(path1, path2, **kwargs)
351
+ else:
352
+ return fs.copy(path1, path2, **kwargs)
353
+ await copy_file_op(
354
+ fs, path1, fs2, path2, tempdir, batch_size, on_error=on_error
355
+ )
356
+
357
+
358
+ async def copy_file_op(
359
+ fs1, url1, fs2, url2, tempdir=None, batch_size=20, on_error="ignore"
360
+ ):
361
+ import tempfile
362
+
363
+ tempdir = tempdir or tempfile.mkdtemp()
364
+ try:
365
+ coros = [
366
+ _copy_file_op(
367
+ fs1,
368
+ u1,
369
+ fs2,
370
+ u2,
371
+ os.path.join(tempdir, uuid.uuid4().hex),
372
+ on_error=on_error,
373
+ )
374
+ for u1, u2 in zip(url1, url2)
375
+ ]
376
+ await _run_coros_in_chunks(coros, batch_size=batch_size)
377
+ finally:
378
+ shutil.rmtree(tempdir)
379
+
380
+
381
+ async def _copy_file_op(fs1, url1, fs2, url2, local, on_error="ignore"):
382
+ ex = () if on_error == "raise" else Exception
383
+ logger.debug("Copy %s -> %s", url1, url2)
384
+ try:
385
+ if fs1.async_impl:
386
+ await fs1._get_file(url1, local)
387
+ else:
388
+ fs1.get_file(url1, local)
389
+ if fs2.async_impl:
390
+ await fs2._put_file(local, url2)
391
+ else:
392
+ fs2.put_file(local, url2)
393
+ os.unlink(local)
394
+ logger.debug("Copy %s -> %s; done", url1, url2)
395
+ except ex as e:
396
+ logger.debug("ignoring cp exception for %s: %s", url1, e)
397
+
398
+
399
+ async def maybe_await(cor):
400
+ if inspect.iscoroutine(cor):
401
+ return await cor
402
+ else:
403
+ return cor
tuning-competition-baseline/.venv/lib/python3.11/site-packages/fsspec/gui.py ADDED
@@ -0,0 +1,414 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import ast
2
+ import contextlib
3
+ import logging
4
+ import os
5
+ import re
6
+ from typing import ClassVar, Sequence
7
+
8
+ import panel as pn
9
+
10
+ from .core import OpenFile, get_filesystem_class, split_protocol
11
+ from .registry import known_implementations
12
+
13
+ pn.extension()
14
+ logger = logging.getLogger("fsspec.gui")
15
+
16
+
17
+ class SigSlot:
18
+ """Signal-slot mixin, for Panel event passing
19
+
20
+ Include this class in a widget manager's superclasses to be able to
21
+ register events and callbacks on Panel widgets managed by that class.
22
+
23
+ The method ``_register`` should be called as widgets are added, and external
24
+ code should call ``connect`` to associate callbacks.
25
+
26
+ By default, all signals emit a DEBUG logging statement.
27
+ """
28
+
29
+ # names of signals that this class may emit each of which must be
30
+ # set by _register for any new instance
31
+ signals: ClassVar[Sequence[str]] = []
32
+ # names of actions that this class may respond to
33
+ slots: ClassVar[Sequence[str]] = []
34
+
35
+ # each of which must be a method name
36
+
37
+ def __init__(self):
38
+ self._ignoring_events = False
39
+ self._sigs = {}
40
+ self._map = {}
41
+ self._setup()
42
+
43
+ def _setup(self):
44
+ """Create GUI elements and register signals"""
45
+ self.panel = pn.pane.PaneBase()
46
+ # no signals to set up in the base class
47
+
48
+ def _register(
49
+ self, widget, name, thing="value", log_level=logging.DEBUG, auto=False
50
+ ):
51
+ """Watch the given attribute of a widget and assign it a named event
52
+
53
+ This is normally called at the time a widget is instantiated, in the
54
+ class which owns it.
55
+
56
+ Parameters
57
+ ----------
58
+ widget : pn.layout.Panel or None
59
+ Widget to watch. If None, an anonymous signal not associated with
60
+ any widget.
61
+ name : str
62
+ Name of this event
63
+ thing : str
64
+ Attribute of the given widget to watch
65
+ log_level : int
66
+ When the signal is triggered, a logging event of the given level
67
+ will be fired in the dfviz logger.
68
+ auto : bool
69
+ If True, automatically connects with a method in this class of the
70
+ same name.
71
+ """
72
+ if name not in self.signals:
73
+ raise ValueError(f"Attempt to assign an undeclared signal: {name}")
74
+ self._sigs[name] = {
75
+ "widget": widget,
76
+ "callbacks": [],
77
+ "thing": thing,
78
+ "log": log_level,
79
+ }
80
+ wn = "-".join(
81
+ [
82
+ getattr(widget, "name", str(widget)) if widget is not None else "none",
83
+ thing,
84
+ ]
85
+ )
86
+ self._map[wn] = name
87
+ if widget is not None:
88
+ widget.param.watch(self._signal, thing, onlychanged=True)
89
+ if auto and hasattr(self, name):
90
+ self.connect(name, getattr(self, name))
91
+
92
+ def _repr_mimebundle_(self, *args, **kwargs):
93
+ """Display in a notebook or a server"""
94
+ try:
95
+ return self.panel._repr_mimebundle_(*args, **kwargs)
96
+ except (ValueError, AttributeError):
97
+ raise NotImplementedError("Panel does not seem to be set " "up properly")
98
+
99
+ def connect(self, signal, slot):
100
+ """Associate call back with given event
101
+
102
+ The callback must be a function which takes the "new" value of the
103
+ watched attribute as the only parameter. If the callback return False,
104
+ this cancels any further processing of the given event.
105
+
106
+ Alternatively, the callback can be a string, in which case it means
107
+ emitting the correspondingly-named event (i.e., connect to self)
108
+ """
109
+ self._sigs[signal]["callbacks"].append(slot)
110
+
111
+ def _signal(self, event):
112
+ """This is called by a an action on a widget
113
+
114
+ Within an self.ignore_events context, nothing happens.
115
+
116
+ Tests can execute this method by directly changing the values of
117
+ widget components.
118
+ """
119
+ if not self._ignoring_events:
120
+ wn = "-".join([event.obj.name, event.name])
121
+ if wn in self._map and self._map[wn] in self._sigs:
122
+ self._emit(self._map[wn], event.new)
123
+
124
+ @contextlib.contextmanager
125
+ def ignore_events(self):
126
+ """Temporarily turn off events processing in this instance
127
+
128
+ (does not propagate to children)
129
+ """
130
+ self._ignoring_events = True
131
+ try:
132
+ yield
133
+ finally:
134
+ self._ignoring_events = False
135
+
136
+ def _emit(self, sig, value=None):
137
+ """An event happened, call its callbacks
138
+
139
+ This method can be used in tests to simulate message passing without
140
+ directly changing visual elements.
141
+
142
+ Calling of callbacks will halt whenever one returns False.
143
+ """
144
+ logger.log(self._sigs[sig]["log"], f"{sig}: {value}")
145
+ for callback in self._sigs[sig]["callbacks"]:
146
+ if isinstance(callback, str):
147
+ self._emit(callback)
148
+ else:
149
+ try:
150
+ # running callbacks should not break the interface
151
+ ret = callback(value)
152
+ if ret is False:
153
+ break
154
+ except Exception as e:
155
+ logger.exception(
156
+ "Exception (%s) while executing callback for signal: %s",
157
+ e,
158
+ sig,
159
+ )
160
+
161
+ def show(self, threads=False):
162
+ """Open a new browser tab and display this instance's interface"""
163
+ self.panel.show(threads=threads, verbose=False)
164
+ return self
165
+
166
+
167
+ class SingleSelect(SigSlot):
168
+ """A multiselect which only allows you to select one item for an event"""
169
+
170
+ signals = ["_selected", "selected"] # the first is internal
171
+ slots = ["set_options", "set_selection", "add", "clear", "select"]
172
+
173
+ def __init__(self, **kwargs):
174
+ self.kwargs = kwargs
175
+ super().__init__()
176
+
177
+ def _setup(self):
178
+ self.panel = pn.widgets.MultiSelect(**self.kwargs)
179
+ self._register(self.panel, "_selected", "value")
180
+ self._register(None, "selected")
181
+ self.connect("_selected", self.select_one)
182
+
183
+ def _signal(self, *args, **kwargs):
184
+ super()._signal(*args, **kwargs)
185
+
186
+ def select_one(self, *_):
187
+ with self.ignore_events():
188
+ val = [self.panel.value[-1]] if self.panel.value else []
189
+ self.panel.value = val
190
+ self._emit("selected", self.panel.value)
191
+
192
+ def set_options(self, options):
193
+ self.panel.options = options
194
+
195
+ def clear(self):
196
+ self.panel.options = []
197
+
198
+ @property
199
+ def value(self):
200
+ return self.panel.value
201
+
202
+ def set_selection(self, selection):
203
+ self.panel.value = [selection]
204
+
205
+
206
+ class FileSelector(SigSlot):
207
+ """Panel-based graphical file selector widget
208
+
209
+ Instances of this widget are interactive and can be displayed in jupyter by having
210
+ them as the output of a cell, or in a separate browser tab using ``.show()``.
211
+ """
212
+
213
+ signals = [
214
+ "protocol_changed",
215
+ "selection_changed",
216
+ "directory_entered",
217
+ "home_clicked",
218
+ "up_clicked",
219
+ "go_clicked",
220
+ "filters_changed",
221
+ ]
222
+ slots = ["set_filters", "go_home"]
223
+
224
+ def __init__(self, url=None, filters=None, ignore=None, kwargs=None):
225
+ """
226
+
227
+ Parameters
228
+ ----------
229
+ url : str (optional)
230
+ Initial value of the URL to populate the dialog; should include protocol
231
+ filters : list(str) (optional)
232
+ File endings to include in the listings. If not included, all files are
233
+ allowed. Does not affect directories.
234
+ If given, the endings will appear as checkboxes in the interface
235
+ ignore : list(str) (optional)
236
+ Regex(s) of file basename patterns to ignore, e.g., "\\." for typical
237
+ hidden files on posix
238
+ kwargs : dict (optional)
239
+ To pass to file system instance
240
+ """
241
+ if url:
242
+ self.init_protocol, url = split_protocol(url)
243
+ else:
244
+ self.init_protocol, url = "file", os.getcwd()
245
+ self.init_url = url
246
+ self.init_kwargs = (kwargs if isinstance(kwargs, str) else str(kwargs)) or "{}"
247
+ self.filters = filters
248
+ self.ignore = [re.compile(i) for i in ignore or []]
249
+ self._fs = None
250
+ super().__init__()
251
+
252
+ def _setup(self):
253
+ self.url = pn.widgets.TextInput(
254
+ name="url",
255
+ value=self.init_url,
256
+ align="end",
257
+ sizing_mode="stretch_width",
258
+ width_policy="max",
259
+ )
260
+ self.protocol = pn.widgets.Select(
261
+ options=sorted(known_implementations),
262
+ value=self.init_protocol,
263
+ name="protocol",
264
+ align="center",
265
+ )
266
+ self.kwargs = pn.widgets.TextInput(
267
+ name="kwargs", value=self.init_kwargs, align="center"
268
+ )
269
+ self.go = pn.widgets.Button(name="⇨", align="end", width=45)
270
+ self.main = SingleSelect(size=10)
271
+ self.home = pn.widgets.Button(name="🏠", width=40, height=30, align="end")
272
+ self.up = pn.widgets.Button(name="‹", width=30, height=30, align="end")
273
+
274
+ self._register(self.protocol, "protocol_changed", auto=True)
275
+ self._register(self.go, "go_clicked", "clicks", auto=True)
276
+ self._register(self.up, "up_clicked", "clicks", auto=True)
277
+ self._register(self.home, "home_clicked", "clicks", auto=True)
278
+ self._register(None, "selection_changed")
279
+ self.main.connect("selected", self.selection_changed)
280
+ self._register(None, "directory_entered")
281
+ self.prev_protocol = self.protocol.value
282
+ self.prev_kwargs = self.storage_options
283
+
284
+ self.filter_sel = pn.widgets.CheckBoxGroup(
285
+ value=[], options=[], inline=False, align="end", width_policy="min"
286
+ )
287
+ self._register(self.filter_sel, "filters_changed", auto=True)
288
+
289
+ self.panel = pn.Column(
290
+ pn.Row(self.protocol, self.kwargs),
291
+ pn.Row(self.home, self.up, self.url, self.go, self.filter_sel),
292
+ self.main.panel,
293
+ )
294
+ self.set_filters(self.filters)
295
+ self.go_clicked()
296
+
297
+ def set_filters(self, filters=None):
298
+ self.filters = filters
299
+ if filters:
300
+ self.filter_sel.options = filters
301
+ self.filter_sel.value = filters
302
+ else:
303
+ self.filter_sel.options = []
304
+ self.filter_sel.value = []
305
+
306
+ @property
307
+ def storage_options(self):
308
+ """Value of the kwargs box as a dictionary"""
309
+ return ast.literal_eval(self.kwargs.value) or {}
310
+
311
+ @property
312
+ def fs(self):
313
+ """Current filesystem instance"""
314
+ if self._fs is None:
315
+ cls = get_filesystem_class(self.protocol.value)
316
+ self._fs = cls(**self.storage_options)
317
+ return self._fs
318
+
319
+ @property
320
+ def urlpath(self):
321
+ """URL of currently selected item"""
322
+ return (
323
+ (f"{self.protocol.value}://{self.main.value[0]}")
324
+ if self.main.value
325
+ else None
326
+ )
327
+
328
+ def open_file(self, mode="rb", compression=None, encoding=None):
329
+ """Create OpenFile instance for the currently selected item
330
+
331
+ For example, in a notebook you might do something like
332
+
333
+ .. code-block::
334
+
335
+ [ ]: sel = FileSelector(); sel
336
+
337
+ # user selects their file
338
+
339
+ [ ]: with sel.open_file('rb') as f:
340
+ ... out = f.read()
341
+
342
+ Parameters
343
+ ----------
344
+ mode: str (optional)
345
+ Open mode for the file.
346
+ compression: str (optional)
347
+ The interact with the file as compressed. Set to 'infer' to guess
348
+ compression from the file ending
349
+ encoding: str (optional)
350
+ If using text mode, use this encoding; defaults to UTF8.
351
+ """
352
+ if self.urlpath is None:
353
+ raise ValueError("No file selected")
354
+ return OpenFile(self.fs, self.urlpath, mode, compression, encoding)
355
+
356
+ def filters_changed(self, values):
357
+ self.filters = values
358
+ self.go_clicked()
359
+
360
+ def selection_changed(self, *_):
361
+ if self.urlpath is None:
362
+ return
363
+ if self.fs.isdir(self.urlpath):
364
+ self.url.value = self.fs._strip_protocol(self.urlpath)
365
+ self.go_clicked()
366
+
367
+ def go_clicked(self, *_):
368
+ if (
369
+ self.prev_protocol != self.protocol.value
370
+ or self.prev_kwargs != self.storage_options
371
+ ):
372
+ self._fs = None # causes fs to be recreated
373
+ self.prev_protocol = self.protocol.value
374
+ self.prev_kwargs = self.storage_options
375
+ listing = sorted(
376
+ self.fs.ls(self.url.value, detail=True), key=lambda x: x["name"]
377
+ )
378
+ listing = [
379
+ l
380
+ for l in listing
381
+ if not any(i.match(l["name"].rsplit("/", 1)[-1]) for i in self.ignore)
382
+ ]
383
+ folders = {
384
+ "📁 " + o["name"].rsplit("/", 1)[-1]: o["name"]
385
+ for o in listing
386
+ if o["type"] == "directory"
387
+ }
388
+ files = {
389
+ "📄 " + o["name"].rsplit("/", 1)[-1]: o["name"]
390
+ for o in listing
391
+ if o["type"] == "file"
392
+ }
393
+ if self.filters:
394
+ files = {
395
+ k: v
396
+ for k, v in files.items()
397
+ if any(v.endswith(ext) for ext in self.filters)
398
+ }
399
+ self.main.set_options(dict(**folders, **files))
400
+
401
+ def protocol_changed(self, *_):
402
+ self._fs = None
403
+ self.main.options = []
404
+ self.url.value = ""
405
+
406
+ def home_clicked(self, *_):
407
+ self.protocol.value = self.init_protocol
408
+ self.kwargs.value = self.init_kwargs
409
+ self.url.value = self.init_url
410
+ self.go_clicked()
411
+
412
+ def up_clicked(self, *_):
413
+ self.url.value = self.fs._parent(self.url.value)
414
+ self.go_clicked()
tuning-competition-baseline/.venv/lib/python3.11/site-packages/fsspec/parquet.py ADDED
@@ -0,0 +1,549 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import io
2
+ import json
3
+ import warnings
4
+
5
+ from .core import url_to_fs
6
+ from .utils import merge_offset_ranges
7
+
8
+ # Parquet-Specific Utilities for fsspec
9
+ #
10
+ # Most of the functions defined in this module are NOT
11
+ # intended for public consumption. The only exception
12
+ # to this is `open_parquet_file`, which should be used
13
+ # place of `fs.open()` to open parquet-formatted files
14
+ # on remote file systems.
15
+
16
+
17
+ def open_parquet_file(
18
+ path,
19
+ mode="rb",
20
+ fs=None,
21
+ metadata=None,
22
+ columns=None,
23
+ row_groups=None,
24
+ storage_options=None,
25
+ strict=False,
26
+ engine="auto",
27
+ max_gap=64_000,
28
+ max_block=256_000_000,
29
+ footer_sample_size=1_000_000,
30
+ **kwargs,
31
+ ):
32
+ """
33
+ Return a file-like object for a single Parquet file.
34
+
35
+ The specified parquet `engine` will be used to parse the
36
+ footer metadata, and determine the required byte ranges
37
+ from the file. The target path will then be opened with
38
+ the "parts" (`KnownPartsOfAFile`) caching strategy.
39
+
40
+ Note that this method is intended for usage with remote
41
+ file systems, and is unlikely to improve parquet-read
42
+ performance on local file systems.
43
+
44
+ Parameters
45
+ ----------
46
+ path: str
47
+ Target file path.
48
+ mode: str, optional
49
+ Mode option to be passed through to `fs.open`. Default is "rb".
50
+ metadata: Any, optional
51
+ Parquet metadata object. Object type must be supported
52
+ by the backend parquet engine. For now, only the "fastparquet"
53
+ engine supports an explicit `ParquetFile` metadata object.
54
+ If a metadata object is supplied, the remote footer metadata
55
+ will not need to be transferred into local memory.
56
+ fs: AbstractFileSystem, optional
57
+ Filesystem object to use for opening the file. If nothing is
58
+ specified, an `AbstractFileSystem` object will be inferred.
59
+ engine : str, default "auto"
60
+ Parquet engine to use for metadata parsing. Allowed options
61
+ include "fastparquet", "pyarrow", and "auto". The specified
62
+ engine must be installed in the current environment. If
63
+ "auto" is specified, and both engines are installed,
64
+ "fastparquet" will take precedence over "pyarrow".
65
+ columns: list, optional
66
+ List of all column names that may be read from the file.
67
+ row_groups : list, optional
68
+ List of all row-groups that may be read from the file. This
69
+ may be a list of row-group indices (integers), or it may be
70
+ a list of `RowGroup` metadata objects (if the "fastparquet"
71
+ engine is used).
72
+ storage_options : dict, optional
73
+ Used to generate an `AbstractFileSystem` object if `fs` was
74
+ not specified.
75
+ strict : bool, optional
76
+ Whether the resulting `KnownPartsOfAFile` cache should
77
+ fetch reads that go beyond a known byte-range boundary.
78
+ If `False` (the default), any read that ends outside a
79
+ known part will be zero padded. Note that using
80
+ `strict=True` may be useful for debugging.
81
+ max_gap : int, optional
82
+ Neighboring byte ranges will only be merged when their
83
+ inter-range gap is <= `max_gap`. Default is 64KB.
84
+ max_block : int, optional
85
+ Neighboring byte ranges will only be merged when the size of
86
+ the aggregated range is <= `max_block`. Default is 256MB.
87
+ footer_sample_size : int, optional
88
+ Number of bytes to read from the end of the path to look
89
+ for the footer metadata. If the sampled bytes do not contain
90
+ the footer, a second read request will be required, and
91
+ performance will suffer. Default is 1MB.
92
+ **kwargs :
93
+ Optional key-word arguments to pass to `fs.open`
94
+ """
95
+
96
+ # Make sure we have an `AbstractFileSystem` object
97
+ # to work with
98
+ if fs is None:
99
+ fs = url_to_fs(path, **(storage_options or {}))[0]
100
+
101
+ # For now, `columns == []` not supported. Just use
102
+ # default `open` command with `path` input
103
+ if columns is not None and len(columns) == 0:
104
+ return fs.open(path, mode=mode)
105
+
106
+ # Set the engine
107
+ engine = _set_engine(engine)
108
+
109
+ # Fetch the known byte ranges needed to read
110
+ # `columns` and/or `row_groups`
111
+ data = _get_parquet_byte_ranges(
112
+ [path],
113
+ fs,
114
+ metadata=metadata,
115
+ columns=columns,
116
+ row_groups=row_groups,
117
+ engine=engine,
118
+ max_gap=max_gap,
119
+ max_block=max_block,
120
+ footer_sample_size=footer_sample_size,
121
+ )
122
+
123
+ # Extract file name from `data`
124
+ fn = next(iter(data)) if data else path
125
+
126
+ # Call self.open with "parts" caching
127
+ options = kwargs.pop("cache_options", {}).copy()
128
+ return fs.open(
129
+ fn,
130
+ mode=mode,
131
+ cache_type="parts",
132
+ cache_options={
133
+ **options,
134
+ "data": data.get(fn, {}),
135
+ "strict": strict,
136
+ },
137
+ **kwargs,
138
+ )
139
+
140
+
141
+ def _get_parquet_byte_ranges(
142
+ paths,
143
+ fs,
144
+ metadata=None,
145
+ columns=None,
146
+ row_groups=None,
147
+ max_gap=64_000,
148
+ max_block=256_000_000,
149
+ footer_sample_size=1_000_000,
150
+ engine="auto",
151
+ ):
152
+ """Get a dictionary of the known byte ranges needed
153
+ to read a specific column/row-group selection from a
154
+ Parquet dataset. Each value in the output dictionary
155
+ is intended for use as the `data` argument for the
156
+ `KnownPartsOfAFile` caching strategy of a single path.
157
+ """
158
+
159
+ # Set engine if necessary
160
+ if isinstance(engine, str):
161
+ engine = _set_engine(engine)
162
+
163
+ # Pass to specialized function if metadata is defined
164
+ if metadata is not None:
165
+
166
+ # Use the provided parquet metadata object
167
+ # to avoid transferring/parsing footer metadata
168
+ return _get_parquet_byte_ranges_from_metadata(
169
+ metadata,
170
+ fs,
171
+ engine,
172
+ columns=columns,
173
+ row_groups=row_groups,
174
+ max_gap=max_gap,
175
+ max_block=max_block,
176
+ )
177
+
178
+ # Get file sizes asynchronously
179
+ file_sizes = fs.sizes(paths)
180
+
181
+ # Populate global paths, starts, & ends
182
+ result = {}
183
+ data_paths = []
184
+ data_starts = []
185
+ data_ends = []
186
+ add_header_magic = True
187
+ if columns is None and row_groups is None:
188
+ # We are NOT selecting specific columns or row-groups.
189
+ #
190
+ # We can avoid sampling the footers, and just transfer
191
+ # all file data with cat_ranges
192
+ for i, path in enumerate(paths):
193
+ result[path] = {}
194
+ for b in range(0, file_sizes[i], max_block):
195
+ data_paths.append(path)
196
+ data_starts.append(b)
197
+ data_ends.append(min(b + max_block, file_sizes[i]))
198
+ add_header_magic = False # "Magic" should already be included
199
+ else:
200
+ # We ARE selecting specific columns or row-groups.
201
+ #
202
+ # Gather file footers.
203
+ # We just take the last `footer_sample_size` bytes of each
204
+ # file (or the entire file if it is smaller than that)
205
+ footer_starts = []
206
+ footer_ends = []
207
+ for i, path in enumerate(paths):
208
+ footer_ends.append(file_sizes[i])
209
+ sample_size = max(0, file_sizes[i] - footer_sample_size)
210
+ footer_starts.append(sample_size)
211
+ footer_samples = fs.cat_ranges(paths, footer_starts, footer_ends)
212
+
213
+ # Check our footer samples and re-sample if necessary.
214
+ missing_footer_starts = footer_starts.copy()
215
+ large_footer = 0
216
+ for i, path in enumerate(paths):
217
+ footer_size = int.from_bytes(footer_samples[i][-8:-4], "little")
218
+ real_footer_start = file_sizes[i] - (footer_size + 8)
219
+ if real_footer_start < footer_starts[i]:
220
+ missing_footer_starts[i] = real_footer_start
221
+ large_footer = max(large_footer, (footer_size + 8))
222
+ if large_footer:
223
+ warnings.warn(
224
+ f"Not enough data was used to sample the parquet footer. "
225
+ f"Try setting footer_sample_size >= {large_footer}."
226
+ )
227
+ for i, block in enumerate(
228
+ fs.cat_ranges(
229
+ paths,
230
+ missing_footer_starts,
231
+ footer_starts,
232
+ )
233
+ ):
234
+ footer_samples[i] = block + footer_samples[i]
235
+ footer_starts[i] = missing_footer_starts[i]
236
+
237
+ # Calculate required byte ranges for each path
238
+ for i, path in enumerate(paths):
239
+
240
+ # Deal with small-file case.
241
+ # Just include all remaining bytes of the file
242
+ # in a single range.
243
+ if file_sizes[i] < max_block:
244
+ if footer_starts[i] > 0:
245
+ # Only need to transfer the data if the
246
+ # footer sample isn't already the whole file
247
+ data_paths.append(path)
248
+ data_starts.append(0)
249
+ data_ends.append(footer_starts[i])
250
+ continue
251
+
252
+ # Use "engine" to collect data byte ranges
253
+ path_data_starts, path_data_ends = engine._parquet_byte_ranges(
254
+ columns,
255
+ row_groups=row_groups,
256
+ footer=footer_samples[i],
257
+ footer_start=footer_starts[i],
258
+ )
259
+
260
+ data_paths += [path] * len(path_data_starts)
261
+ data_starts += path_data_starts
262
+ data_ends += path_data_ends
263
+
264
+ # Merge adjacent offset ranges
265
+ data_paths, data_starts, data_ends = merge_offset_ranges(
266
+ data_paths,
267
+ data_starts,
268
+ data_ends,
269
+ max_gap=max_gap,
270
+ max_block=max_block,
271
+ sort=False, # Should already be sorted
272
+ )
273
+
274
+ # Start by populating `result` with footer samples
275
+ for i, path in enumerate(paths):
276
+ result[path] = {(footer_starts[i], footer_ends[i]): footer_samples[i]}
277
+
278
+ # Transfer the data byte-ranges into local memory
279
+ _transfer_ranges(fs, result, data_paths, data_starts, data_ends)
280
+
281
+ # Add b"PAR1" to header if necessary
282
+ if add_header_magic:
283
+ _add_header_magic(result)
284
+
285
+ return result
286
+
287
+
288
+ def _get_parquet_byte_ranges_from_metadata(
289
+ metadata,
290
+ fs,
291
+ engine,
292
+ columns=None,
293
+ row_groups=None,
294
+ max_gap=64_000,
295
+ max_block=256_000_000,
296
+ ):
297
+ """Simplified version of `_get_parquet_byte_ranges` for
298
+ the case that an engine-specific `metadata` object is
299
+ provided, and the remote footer metadata does not need to
300
+ be transferred before calculating the required byte ranges.
301
+ """
302
+
303
+ # Use "engine" to collect data byte ranges
304
+ data_paths, data_starts, data_ends = engine._parquet_byte_ranges(
305
+ columns,
306
+ row_groups=row_groups,
307
+ metadata=metadata,
308
+ )
309
+
310
+ # Merge adjacent offset ranges
311
+ data_paths, data_starts, data_ends = merge_offset_ranges(
312
+ data_paths,
313
+ data_starts,
314
+ data_ends,
315
+ max_gap=max_gap,
316
+ max_block=max_block,
317
+ sort=False, # Should be sorted
318
+ )
319
+
320
+ # Transfer the data byte-ranges into local memory
321
+ result = {fn: {} for fn in list(set(data_paths))}
322
+ _transfer_ranges(fs, result, data_paths, data_starts, data_ends)
323
+
324
+ # Add b"PAR1" to header
325
+ _add_header_magic(result)
326
+
327
+ return result
328
+
329
+
330
+ def _transfer_ranges(fs, blocks, paths, starts, ends):
331
+ # Use cat_ranges to gather the data byte_ranges
332
+ ranges = (paths, starts, ends)
333
+ for path, start, stop, data in zip(*ranges, fs.cat_ranges(*ranges)):
334
+ blocks[path][(start, stop)] = data
335
+
336
+
337
+ def _add_header_magic(data):
338
+ # Add b"PAR1" to file headers
339
+ for path in list(data.keys()):
340
+ add_magic = True
341
+ for k in data[path].keys():
342
+ if k[0] == 0 and k[1] >= 4:
343
+ add_magic = False
344
+ break
345
+ if add_magic:
346
+ data[path][(0, 4)] = b"PAR1"
347
+
348
+
349
+ def _set_engine(engine_str):
350
+
351
+ # Define a list of parquet engines to try
352
+ if engine_str == "auto":
353
+ try_engines = ("fastparquet", "pyarrow")
354
+ elif not isinstance(engine_str, str):
355
+ raise ValueError(
356
+ "Failed to set parquet engine! "
357
+ "Please pass 'fastparquet', 'pyarrow', or 'auto'"
358
+ )
359
+ elif engine_str not in ("fastparquet", "pyarrow"):
360
+ raise ValueError(f"{engine_str} engine not supported by `fsspec.parquet`")
361
+ else:
362
+ try_engines = [engine_str]
363
+
364
+ # Try importing the engines in `try_engines`,
365
+ # and choose the first one that succeeds
366
+ for engine in try_engines:
367
+ try:
368
+ if engine == "fastparquet":
369
+ return FastparquetEngine()
370
+ elif engine == "pyarrow":
371
+ return PyarrowEngine()
372
+ except ImportError:
373
+ pass
374
+
375
+ # Raise an error if a supported parquet engine
376
+ # was not found
377
+ raise ImportError(
378
+ f"The following parquet engines are not installed "
379
+ f"in your python environment: {try_engines}."
380
+ f"Please install 'fastparquert' or 'pyarrow' to "
381
+ f"utilize the `fsspec.parquet` module."
382
+ )
383
+
384
+
385
+ class FastparquetEngine:
386
+
387
+ # The purpose of the FastparquetEngine class is
388
+ # to check if fastparquet can be imported (on initialization)
389
+ # and to define a `_parquet_byte_ranges` method. In the
390
+ # future, this class may also be used to define other
391
+ # methods/logic that are specific to fastparquet.
392
+
393
+ def __init__(self):
394
+ import fastparquet as fp
395
+
396
+ self.fp = fp
397
+
398
+ def _row_group_filename(self, row_group, pf):
399
+ return pf.row_group_filename(row_group)
400
+
401
+ def _parquet_byte_ranges(
402
+ self,
403
+ columns,
404
+ row_groups=None,
405
+ metadata=None,
406
+ footer=None,
407
+ footer_start=None,
408
+ ):
409
+
410
+ # Initialize offset ranges and define ParqetFile metadata
411
+ pf = metadata
412
+ data_paths, data_starts, data_ends = [], [], []
413
+ if pf is None:
414
+ pf = self.fp.ParquetFile(io.BytesIO(footer))
415
+
416
+ # Convert columns to a set and add any index columns
417
+ # specified in the pandas metadata (just in case)
418
+ column_set = None if columns is None else set(columns)
419
+ if column_set is not None and hasattr(pf, "pandas_metadata"):
420
+ md_index = [
421
+ ind
422
+ for ind in pf.pandas_metadata.get("index_columns", [])
423
+ # Ignore RangeIndex information
424
+ if not isinstance(ind, dict)
425
+ ]
426
+ column_set |= set(md_index)
427
+
428
+ # Check if row_groups is a list of integers
429
+ # or a list of row-group metadata
430
+ if row_groups and not isinstance(row_groups[0], int):
431
+ # Input row_groups contains row-group metadata
432
+ row_group_indices = None
433
+ else:
434
+ # Input row_groups contains row-group indices
435
+ row_group_indices = row_groups
436
+ row_groups = pf.row_groups
437
+
438
+ # Loop through column chunks to add required byte ranges
439
+ for r, row_group in enumerate(row_groups):
440
+ # Skip this row-group if we are targeting
441
+ # specific row-groups
442
+ if row_group_indices is None or r in row_group_indices:
443
+
444
+ # Find the target parquet-file path for `row_group`
445
+ fn = self._row_group_filename(row_group, pf)
446
+
447
+ for column in row_group.columns:
448
+ name = column.meta_data.path_in_schema[0]
449
+ # Skip this column if we are targeting a
450
+ # specific columns
451
+ if column_set is None or name in column_set:
452
+ file_offset0 = column.meta_data.dictionary_page_offset
453
+ if file_offset0 is None:
454
+ file_offset0 = column.meta_data.data_page_offset
455
+ num_bytes = column.meta_data.total_compressed_size
456
+ if footer_start is None or file_offset0 < footer_start:
457
+ data_paths.append(fn)
458
+ data_starts.append(file_offset0)
459
+ data_ends.append(
460
+ min(
461
+ file_offset0 + num_bytes,
462
+ footer_start or (file_offset0 + num_bytes),
463
+ )
464
+ )
465
+
466
+ if metadata:
467
+ # The metadata in this call may map to multiple
468
+ # file paths. Need to include `data_paths`
469
+ return data_paths, data_starts, data_ends
470
+ return data_starts, data_ends
471
+
472
+
473
+ class PyarrowEngine:
474
+
475
+ # The purpose of the PyarrowEngine class is
476
+ # to check if pyarrow can be imported (on initialization)
477
+ # and to define a `_parquet_byte_ranges` method. In the
478
+ # future, this class may also be used to define other
479
+ # methods/logic that are specific to pyarrow.
480
+
481
+ def __init__(self):
482
+ import pyarrow.parquet as pq
483
+
484
+ self.pq = pq
485
+
486
+ def _row_group_filename(self, row_group, metadata):
487
+ raise NotImplementedError
488
+
489
+ def _parquet_byte_ranges(
490
+ self,
491
+ columns,
492
+ row_groups=None,
493
+ metadata=None,
494
+ footer=None,
495
+ footer_start=None,
496
+ ):
497
+
498
+ if metadata is not None:
499
+ raise ValueError("metadata input not supported for PyarrowEngine")
500
+
501
+ data_starts, data_ends = [], []
502
+ md = self.pq.ParquetFile(io.BytesIO(footer)).metadata
503
+
504
+ # Convert columns to a set and add any index columns
505
+ # specified in the pandas metadata (just in case)
506
+ column_set = None if columns is None else set(columns)
507
+ if column_set is not None:
508
+ schema = md.schema.to_arrow_schema()
509
+ has_pandas_metadata = (
510
+ schema.metadata is not None and b"pandas" in schema.metadata
511
+ )
512
+ if has_pandas_metadata:
513
+ md_index = [
514
+ ind
515
+ for ind in json.loads(
516
+ schema.metadata[b"pandas"].decode("utf8")
517
+ ).get("index_columns", [])
518
+ # Ignore RangeIndex information
519
+ if not isinstance(ind, dict)
520
+ ]
521
+ column_set |= set(md_index)
522
+
523
+ # Loop through column chunks to add required byte ranges
524
+ for r in range(md.num_row_groups):
525
+ # Skip this row-group if we are targeting
526
+ # specific row-groups
527
+ if row_groups is None or r in row_groups:
528
+ row_group = md.row_group(r)
529
+ for c in range(row_group.num_columns):
530
+ column = row_group.column(c)
531
+ name = column.path_in_schema
532
+ # Skip this column if we are targeting a
533
+ # specific columns
534
+ split_name = name.split(".")[0]
535
+ if (
536
+ column_set is None
537
+ or name in column_set
538
+ or split_name in column_set
539
+ ):
540
+ file_offset0 = column.dictionary_page_offset
541
+ if file_offset0 is None:
542
+ file_offset0 = column.data_page_offset
543
+ num_bytes = column.total_compressed_size
544
+ if file_offset0 < footer_start:
545
+ data_starts.append(file_offset0)
546
+ data_ends.append(
547
+ min(file_offset0 + num_bytes, footer_start)
548
+ )
549
+ return data_starts, data_ends
tuning-competition-baseline/.venv/lib/python3.11/site-packages/fsspec/spec.py ADDED
@@ -0,0 +1,1965 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import io
4
+ import logging
5
+ import os
6
+ import threading
7
+ import warnings
8
+ import weakref
9
+ from errno import ESPIPE
10
+ from glob import has_magic
11
+ from hashlib import sha256
12
+ from typing import ClassVar
13
+
14
+ from .callbacks import DEFAULT_CALLBACK
15
+ from .config import apply_config, conf
16
+ from .dircache import DirCache
17
+ from .transaction import Transaction
18
+ from .utils import (
19
+ _unstrip_protocol,
20
+ glob_translate,
21
+ isfilelike,
22
+ other_paths,
23
+ read_block,
24
+ stringify_path,
25
+ tokenize,
26
+ )
27
+
28
+ logger = logging.getLogger("fsspec")
29
+
30
+
31
+ def make_instance(cls, args, kwargs):
32
+ return cls(*args, **kwargs)
33
+
34
+
35
+ class _Cached(type):
36
+ """
37
+ Metaclass for caching file system instances.
38
+
39
+ Notes
40
+ -----
41
+ Instances are cached according to
42
+
43
+ * The values of the class attributes listed in `_extra_tokenize_attributes`
44
+ * The arguments passed to ``__init__``.
45
+
46
+ This creates an additional reference to the filesystem, which prevents the
47
+ filesystem from being garbage collected when all *user* references go away.
48
+ A call to the :meth:`AbstractFileSystem.clear_instance_cache` must *also*
49
+ be made for a filesystem instance to be garbage collected.
50
+ """
51
+
52
+ def __init__(cls, *args, **kwargs):
53
+ super().__init__(*args, **kwargs)
54
+ # Note: we intentionally create a reference here, to avoid garbage
55
+ # collecting instances when all other references are gone. To really
56
+ # delete a FileSystem, the cache must be cleared.
57
+ if conf.get("weakref_instance_cache"): # pragma: no cover
58
+ # debug option for analysing fork/spawn conditions
59
+ cls._cache = weakref.WeakValueDictionary()
60
+ else:
61
+ cls._cache = {}
62
+ cls._pid = os.getpid()
63
+
64
+ def __call__(cls, *args, **kwargs):
65
+ kwargs = apply_config(cls, kwargs)
66
+ extra_tokens = tuple(
67
+ getattr(cls, attr, None) for attr in cls._extra_tokenize_attributes
68
+ )
69
+ token = tokenize(
70
+ cls, cls._pid, threading.get_ident(), *args, *extra_tokens, **kwargs
71
+ )
72
+ skip = kwargs.pop("skip_instance_cache", False)
73
+ if os.getpid() != cls._pid:
74
+ cls._cache.clear()
75
+ cls._pid = os.getpid()
76
+ if not skip and cls.cachable and token in cls._cache:
77
+ cls._latest = token
78
+ return cls._cache[token]
79
+ else:
80
+ obj = super().__call__(*args, **kwargs)
81
+ # Setting _fs_token here causes some static linters to complain.
82
+ obj._fs_token_ = token
83
+ obj.storage_args = args
84
+ obj.storage_options = kwargs
85
+ if obj.async_impl and obj.mirror_sync_methods:
86
+ from .asyn import mirror_sync_methods
87
+
88
+ mirror_sync_methods(obj)
89
+
90
+ if cls.cachable and not skip:
91
+ cls._latest = token
92
+ cls._cache[token] = obj
93
+ return obj
94
+
95
+
96
+ class AbstractFileSystem(metaclass=_Cached):
97
+ """
98
+ An abstract super-class for pythonic file-systems
99
+
100
+ Implementations are expected to be compatible with or, better, subclass
101
+ from here.
102
+ """
103
+
104
+ cachable = True # this class can be cached, instances reused
105
+ _cached = False
106
+ blocksize = 2**22
107
+ sep = "/"
108
+ protocol: ClassVar[str | tuple[str, ...]] = "abstract"
109
+ _latest = None
110
+ async_impl = False
111
+ mirror_sync_methods = False
112
+ root_marker = "" # For some FSs, may require leading '/' or other character
113
+ transaction_type = Transaction
114
+
115
+ #: Extra *class attributes* that should be considered when hashing.
116
+ _extra_tokenize_attributes = ()
117
+
118
+ def __init__(self, *args, **storage_options):
119
+ """Create and configure file-system instance
120
+
121
+ Instances may be cachable, so if similar enough arguments are seen
122
+ a new instance is not required. The token attribute exists to allow
123
+ implementations to cache instances if they wish.
124
+
125
+ A reasonable default should be provided if there are no arguments.
126
+
127
+ Subclasses should call this method.
128
+
129
+ Parameters
130
+ ----------
131
+ use_listings_cache, listings_expiry_time, max_paths:
132
+ passed to ``DirCache``, if the implementation supports
133
+ directory listing caching. Pass use_listings_cache=False
134
+ to disable such caching.
135
+ skip_instance_cache: bool
136
+ If this is a cachable implementation, pass True here to force
137
+ creating a new instance even if a matching instance exists, and prevent
138
+ storing this instance.
139
+ asynchronous: bool
140
+ loop: asyncio-compatible IOLoop or None
141
+ """
142
+ if self._cached:
143
+ # reusing instance, don't change
144
+ return
145
+ self._cached = True
146
+ self._intrans = False
147
+ self._transaction = None
148
+ self._invalidated_caches_in_transaction = []
149
+ self.dircache = DirCache(**storage_options)
150
+
151
+ if storage_options.pop("add_docs", None):
152
+ warnings.warn("add_docs is no longer supported.", FutureWarning)
153
+
154
+ if storage_options.pop("add_aliases", None):
155
+ warnings.warn("add_aliases has been removed.", FutureWarning)
156
+ # This is set in _Cached
157
+ self._fs_token_ = None
158
+
159
+ @property
160
+ def fsid(self):
161
+ """Persistent filesystem id that can be used to compare filesystems
162
+ across sessions.
163
+ """
164
+ raise NotImplementedError
165
+
166
+ @property
167
+ def _fs_token(self):
168
+ return self._fs_token_
169
+
170
+ def __dask_tokenize__(self):
171
+ return self._fs_token
172
+
173
+ def __hash__(self):
174
+ return int(self._fs_token, 16)
175
+
176
+ def __eq__(self, other):
177
+ return isinstance(other, type(self)) and self._fs_token == other._fs_token
178
+
179
+ def __reduce__(self):
180
+ return make_instance, (type(self), self.storage_args, self.storage_options)
181
+
182
+ @classmethod
183
+ def _strip_protocol(cls, path):
184
+ """Turn path from fully-qualified to file-system-specific
185
+
186
+ May require FS-specific handling, e.g., for relative paths or links.
187
+ """
188
+ if isinstance(path, list):
189
+ return [cls._strip_protocol(p) for p in path]
190
+ path = stringify_path(path)
191
+ protos = (cls.protocol,) if isinstance(cls.protocol, str) else cls.protocol
192
+ for protocol in protos:
193
+ if path.startswith(protocol + "://"):
194
+ path = path[len(protocol) + 3 :]
195
+ elif path.startswith(protocol + "::"):
196
+ path = path[len(protocol) + 2 :]
197
+ path = path.rstrip("/")
198
+ # use of root_marker to make minimum required path, e.g., "/"
199
+ return path or cls.root_marker
200
+
201
+ def unstrip_protocol(self, name: str) -> str:
202
+ """Format FS-specific path to generic, including protocol"""
203
+ protos = (self.protocol,) if isinstance(self.protocol, str) else self.protocol
204
+ for protocol in protos:
205
+ if name.startswith(f"{protocol}://"):
206
+ return name
207
+ return f"{protos[0]}://{name}"
208
+
209
+ @staticmethod
210
+ def _get_kwargs_from_urls(path):
211
+ """If kwargs can be encoded in the paths, extract them here
212
+
213
+ This should happen before instantiation of the class; incoming paths
214
+ then should be amended to strip the options in methods.
215
+
216
+ Examples may look like an sftp path "sftp://user@host:/my/path", where
217
+ the user and host should become kwargs and later get stripped.
218
+ """
219
+ # by default, nothing happens
220
+ return {}
221
+
222
+ @classmethod
223
+ def current(cls):
224
+ """Return the most recently instantiated FileSystem
225
+
226
+ If no instance has been created, then create one with defaults
227
+ """
228
+ if cls._latest in cls._cache:
229
+ return cls._cache[cls._latest]
230
+ return cls()
231
+
232
+ @property
233
+ def transaction(self):
234
+ """A context within which files are committed together upon exit
235
+
236
+ Requires the file class to implement `.commit()` and `.discard()`
237
+ for the normal and exception cases.
238
+ """
239
+ if self._transaction is None:
240
+ self._transaction = self.transaction_type(self)
241
+ return self._transaction
242
+
243
+ def start_transaction(self):
244
+ """Begin write transaction for deferring files, non-context version"""
245
+ self._intrans = True
246
+ self._transaction = self.transaction_type(self)
247
+ return self.transaction
248
+
249
+ def end_transaction(self):
250
+ """Finish write transaction, non-context version"""
251
+ self.transaction.complete()
252
+ self._transaction = None
253
+ # The invalid cache must be cleared after the transaction is completed.
254
+ for path in self._invalidated_caches_in_transaction:
255
+ self.invalidate_cache(path)
256
+ self._invalidated_caches_in_transaction.clear()
257
+
258
+ def invalidate_cache(self, path=None):
259
+ """
260
+ Discard any cached directory information
261
+
262
+ Parameters
263
+ ----------
264
+ path: string or None
265
+ If None, clear all listings cached else listings at or under given
266
+ path.
267
+ """
268
+ # Not necessary to implement invalidation mechanism, may have no cache.
269
+ # But if have, you should call this method of parent class from your
270
+ # subclass to ensure expiring caches after transacations correctly.
271
+ # See the implementation of FTPFileSystem in ftp.py
272
+ if self._intrans:
273
+ self._invalidated_caches_in_transaction.append(path)
274
+
275
+ def mkdir(self, path, create_parents=True, **kwargs):
276
+ """
277
+ Create directory entry at path
278
+
279
+ For systems that don't have true directories, may create an for
280
+ this instance only and not touch the real filesystem
281
+
282
+ Parameters
283
+ ----------
284
+ path: str
285
+ location
286
+ create_parents: bool
287
+ if True, this is equivalent to ``makedirs``
288
+ kwargs:
289
+ may be permissions, etc.
290
+ """
291
+ pass # not necessary to implement, may not have directories
292
+
293
+ def makedirs(self, path, exist_ok=False):
294
+ """Recursively make directories
295
+
296
+ Creates directory at path and any intervening required directories.
297
+ Raises exception if, for instance, the path already exists but is a
298
+ file.
299
+
300
+ Parameters
301
+ ----------
302
+ path: str
303
+ leaf directory name
304
+ exist_ok: bool (False)
305
+ If False, will error if the target already exists
306
+ """
307
+ pass # not necessary to implement, may not have directories
308
+
309
+ def rmdir(self, path):
310
+ """Remove a directory, if empty"""
311
+ pass # not necessary to implement, may not have directories
312
+
313
+ def ls(self, path, detail=True, **kwargs):
314
+ """List objects at path.
315
+
316
+ This should include subdirectories and files at that location. The
317
+ difference between a file and a directory must be clear when details
318
+ are requested.
319
+
320
+ The specific keys, or perhaps a FileInfo class, or similar, is TBD,
321
+ but must be consistent across implementations.
322
+ Must include:
323
+
324
+ - full path to the entry (without protocol)
325
+ - size of the entry, in bytes. If the value cannot be determined, will
326
+ be ``None``.
327
+ - type of entry, "file", "directory" or other
328
+
329
+ Additional information
330
+ may be present, appropriate to the file-system, e.g., generation,
331
+ checksum, etc.
332
+
333
+ May use refresh=True|False to allow use of self._ls_from_cache to
334
+ check for a saved listing and avoid calling the backend. This would be
335
+ common where listing may be expensive.
336
+
337
+ Parameters
338
+ ----------
339
+ path: str
340
+ detail: bool
341
+ if True, gives a list of dictionaries, where each is the same as
342
+ the result of ``info(path)``. If False, gives a list of paths
343
+ (str).
344
+ kwargs: may have additional backend-specific options, such as version
345
+ information
346
+
347
+ Returns
348
+ -------
349
+ List of strings if detail is False, or list of directory information
350
+ dicts if detail is True.
351
+ """
352
+ raise NotImplementedError
353
+
354
+ def _ls_from_cache(self, path):
355
+ """Check cache for listing
356
+
357
+ Returns listing, if found (may be empty list for a directly that exists
358
+ but contains nothing), None if not in cache.
359
+ """
360
+ parent = self._parent(path)
361
+ if path.rstrip("/") in self.dircache:
362
+ return self.dircache[path.rstrip("/")]
363
+ try:
364
+ files = [
365
+ f
366
+ for f in self.dircache[parent]
367
+ if f["name"] == path
368
+ or (f["name"] == path.rstrip("/") and f["type"] == "directory")
369
+ ]
370
+ if len(files) == 0:
371
+ # parent dir was listed but did not contain this file
372
+ raise FileNotFoundError(path)
373
+ return files
374
+ except KeyError:
375
+ pass
376
+
377
+ def walk(self, path, maxdepth=None, topdown=True, on_error="omit", **kwargs):
378
+ """Return all files belows path
379
+
380
+ List all files, recursing into subdirectories; output is iterator-style,
381
+ like ``os.walk()``. For a simple list of files, ``find()`` is available.
382
+
383
+ When topdown is True, the caller can modify the dirnames list in-place (perhaps
384
+ using del or slice assignment), and walk() will
385
+ only recurse into the subdirectories whose names remain in dirnames;
386
+ this can be used to prune the search, impose a specific order of visiting,
387
+ or even to inform walk() about directories the caller creates or renames before
388
+ it resumes walk() again.
389
+ Modifying dirnames when topdown is False has no effect. (see os.walk)
390
+
391
+ Note that the "files" outputted will include anything that is not
392
+ a directory, such as links.
393
+
394
+ Parameters
395
+ ----------
396
+ path: str
397
+ Root to recurse into
398
+ maxdepth: int
399
+ Maximum recursion depth. None means limitless, but not recommended
400
+ on link-based file-systems.
401
+ topdown: bool (True)
402
+ Whether to walk the directory tree from the top downwards or from
403
+ the bottom upwards.
404
+ on_error: "omit", "raise", a collable
405
+ if omit (default), path with exception will simply be empty;
406
+ If raise, an underlying exception will be raised;
407
+ if callable, it will be called with a single OSError instance as argument
408
+ kwargs: passed to ``ls``
409
+ """
410
+ if maxdepth is not None and maxdepth < 1:
411
+ raise ValueError("maxdepth must be at least 1")
412
+
413
+ path = self._strip_protocol(path)
414
+ full_dirs = {}
415
+ dirs = {}
416
+ files = {}
417
+
418
+ detail = kwargs.pop("detail", False)
419
+ try:
420
+ listing = self.ls(path, detail=True, **kwargs)
421
+ except (FileNotFoundError, OSError) as e:
422
+ if on_error == "raise":
423
+ raise
424
+ elif callable(on_error):
425
+ on_error(e)
426
+ if detail:
427
+ return path, {}, {}
428
+ return path, [], []
429
+
430
+ for info in listing:
431
+ # each info name must be at least [path]/part , but here
432
+ # we check also for names like [path]/part/
433
+ pathname = info["name"].rstrip("/")
434
+ name = pathname.rsplit("/", 1)[-1]
435
+ if info["type"] == "directory" and pathname != path:
436
+ # do not include "self" path
437
+ full_dirs[name] = pathname
438
+ dirs[name] = info
439
+ elif pathname == path:
440
+ # file-like with same name as give path
441
+ files[""] = info
442
+ else:
443
+ files[name] = info
444
+
445
+ if not detail:
446
+ dirs = list(dirs)
447
+ files = list(files)
448
+
449
+ if topdown:
450
+ # Yield before recursion if walking top down
451
+ yield path, dirs, files
452
+
453
+ if maxdepth is not None:
454
+ maxdepth -= 1
455
+ if maxdepth < 1:
456
+ if not topdown:
457
+ yield path, dirs, files
458
+ return
459
+
460
+ for d in dirs:
461
+ yield from self.walk(
462
+ full_dirs[d],
463
+ maxdepth=maxdepth,
464
+ detail=detail,
465
+ topdown=topdown,
466
+ **kwargs,
467
+ )
468
+
469
+ if not topdown:
470
+ # Yield after recursion if walking bottom up
471
+ yield path, dirs, files
472
+
473
+ def find(self, path, maxdepth=None, withdirs=False, detail=False, **kwargs):
474
+ """List all files below path.
475
+
476
+ Like posix ``find`` command without conditions
477
+
478
+ Parameters
479
+ ----------
480
+ path : str
481
+ maxdepth: int or None
482
+ If not None, the maximum number of levels to descend
483
+ withdirs: bool
484
+ Whether to include directory paths in the output. This is True
485
+ when used by glob, but users usually only want files.
486
+ kwargs are passed to ``ls``.
487
+ """
488
+ # TODO: allow equivalent of -name parameter
489
+ path = self._strip_protocol(path)
490
+ out = {}
491
+
492
+ # Add the root directory if withdirs is requested
493
+ # This is needed for posix glob compliance
494
+ if withdirs and path != "" and self.isdir(path):
495
+ out[path] = self.info(path)
496
+
497
+ for _, dirs, files in self.walk(path, maxdepth, detail=True, **kwargs):
498
+ if withdirs:
499
+ files.update(dirs)
500
+ out.update({info["name"]: info for name, info in files.items()})
501
+ if not out and self.isfile(path):
502
+ # walk works on directories, but find should also return [path]
503
+ # when path happens to be a file
504
+ out[path] = {}
505
+ names = sorted(out)
506
+ if not detail:
507
+ return names
508
+ else:
509
+ return {name: out[name] for name in names}
510
+
511
+ def du(self, path, total=True, maxdepth=None, withdirs=False, **kwargs):
512
+ """Space used by files and optionally directories within a path
513
+
514
+ Directory size does not include the size of its contents.
515
+
516
+ Parameters
517
+ ----------
518
+ path: str
519
+ total: bool
520
+ Whether to sum all the file sizes
521
+ maxdepth: int or None
522
+ Maximum number of directory levels to descend, None for unlimited.
523
+ withdirs: bool
524
+ Whether to include directory paths in the output.
525
+ kwargs: passed to ``find``
526
+
527
+ Returns
528
+ -------
529
+ Dict of {path: size} if total=False, or int otherwise, where numbers
530
+ refer to bytes used.
531
+ """
532
+ sizes = {}
533
+ if withdirs and self.isdir(path):
534
+ # Include top-level directory in output
535
+ info = self.info(path)
536
+ sizes[info["name"]] = info["size"]
537
+ for f in self.find(path, maxdepth=maxdepth, withdirs=withdirs, **kwargs):
538
+ info = self.info(f)
539
+ sizes[info["name"]] = info["size"]
540
+ if total:
541
+ return sum(sizes.values())
542
+ else:
543
+ return sizes
544
+
545
+ def glob(self, path, maxdepth=None, **kwargs):
546
+ """
547
+ Find files by glob-matching.
548
+
549
+ If the path ends with '/', only folders are returned.
550
+
551
+ We support ``"**"``,
552
+ ``"?"`` and ``"[..]"``. We do not support ^ for pattern negation.
553
+
554
+ The `maxdepth` option is applied on the first `**` found in the path.
555
+
556
+ kwargs are passed to ``ls``.
557
+ """
558
+ if maxdepth is not None and maxdepth < 1:
559
+ raise ValueError("maxdepth must be at least 1")
560
+
561
+ import re
562
+
563
+ seps = (os.path.sep, os.path.altsep) if os.path.altsep else (os.path.sep,)
564
+ ends_with_sep = path.endswith(seps) # _strip_protocol strips trailing slash
565
+ path = self._strip_protocol(path)
566
+ append_slash_to_dirname = ends_with_sep or path.endswith(
567
+ tuple(sep + "**" for sep in seps)
568
+ )
569
+ idx_star = path.find("*") if path.find("*") >= 0 else len(path)
570
+ idx_qmark = path.find("?") if path.find("?") >= 0 else len(path)
571
+ idx_brace = path.find("[") if path.find("[") >= 0 else len(path)
572
+
573
+ min_idx = min(idx_star, idx_qmark, idx_brace)
574
+
575
+ detail = kwargs.pop("detail", False)
576
+
577
+ if not has_magic(path):
578
+ if self.exists(path, **kwargs):
579
+ if not detail:
580
+ return [path]
581
+ else:
582
+ return {path: self.info(path, **kwargs)}
583
+ else:
584
+ if not detail:
585
+ return [] # glob of non-existent returns empty
586
+ else:
587
+ return {}
588
+ elif "/" in path[:min_idx]:
589
+ min_idx = path[:min_idx].rindex("/")
590
+ root = path[: min_idx + 1]
591
+ depth = path[min_idx + 1 :].count("/") + 1
592
+ else:
593
+ root = ""
594
+ depth = path[min_idx + 1 :].count("/") + 1
595
+
596
+ if "**" in path:
597
+ if maxdepth is not None:
598
+ idx_double_stars = path.find("**")
599
+ depth_double_stars = path[idx_double_stars:].count("/") + 1
600
+ depth = depth - depth_double_stars + maxdepth
601
+ else:
602
+ depth = None
603
+
604
+ allpaths = self.find(root, maxdepth=depth, withdirs=True, detail=True, **kwargs)
605
+
606
+ pattern = glob_translate(path + ("/" if ends_with_sep else ""))
607
+ pattern = re.compile(pattern)
608
+
609
+ out = {
610
+ p: info
611
+ for p, info in sorted(allpaths.items())
612
+ if pattern.match(
613
+ (
614
+ p + "/"
615
+ if append_slash_to_dirname and info["type"] == "directory"
616
+ else p
617
+ )
618
+ )
619
+ }
620
+
621
+ if detail:
622
+ return out
623
+ else:
624
+ return list(out)
625
+
626
+ def exists(self, path, **kwargs):
627
+ """Is there a file at the given path"""
628
+ try:
629
+ self.info(path, **kwargs)
630
+ return True
631
+ except: # noqa: E722
632
+ # any exception allowed bar FileNotFoundError?
633
+ return False
634
+
635
+ def lexists(self, path, **kwargs):
636
+ """If there is a file at the given path (including
637
+ broken links)"""
638
+ return self.exists(path)
639
+
640
+ def info(self, path, **kwargs):
641
+ """Give details of entry at path
642
+
643
+ Returns a single dictionary, with exactly the same information as ``ls``
644
+ would with ``detail=True``.
645
+
646
+ The default implementation should calls ls and could be overridden by a
647
+ shortcut. kwargs are passed on to ```ls()``.
648
+
649
+ Some file systems might not be able to measure the file's size, in
650
+ which case, the returned dict will include ``'size': None``.
651
+
652
+ Returns
653
+ -------
654
+ dict with keys: name (full path in the FS), size (in bytes), type (file,
655
+ directory, or something else) and other FS-specific keys.
656
+ """
657
+ path = self._strip_protocol(path)
658
+ out = self.ls(self._parent(path), detail=True, **kwargs)
659
+ out = [o for o in out if o["name"].rstrip("/") == path]
660
+ if out:
661
+ return out[0]
662
+ out = self.ls(path, detail=True, **kwargs)
663
+ path = path.rstrip("/")
664
+ out1 = [o for o in out if o["name"].rstrip("/") == path]
665
+ if len(out1) == 1:
666
+ if "size" not in out1[0]:
667
+ out1[0]["size"] = None
668
+ return out1[0]
669
+ elif len(out1) > 1 or out:
670
+ return {"name": path, "size": 0, "type": "directory"}
671
+ else:
672
+ raise FileNotFoundError(path)
673
+
674
+ def checksum(self, path):
675
+ """Unique value for current version of file
676
+
677
+ If the checksum is the same from one moment to another, the contents
678
+ are guaranteed to be the same. If the checksum changes, the contents
679
+ *might* have changed.
680
+
681
+ This should normally be overridden; default will probably capture
682
+ creation/modification timestamp (which would be good) or maybe
683
+ access timestamp (which would be bad)
684
+ """
685
+ return int(tokenize(self.info(path)), 16)
686
+
687
+ def size(self, path):
688
+ """Size in bytes of file"""
689
+ return self.info(path).get("size", None)
690
+
691
+ def sizes(self, paths):
692
+ """Size in bytes of each file in a list of paths"""
693
+ return [self.size(p) for p in paths]
694
+
695
+ def isdir(self, path):
696
+ """Is this entry directory-like?"""
697
+ try:
698
+ return self.info(path)["type"] == "directory"
699
+ except OSError:
700
+ return False
701
+
702
+ def isfile(self, path):
703
+ """Is this entry file-like?"""
704
+ try:
705
+ return self.info(path)["type"] == "file"
706
+ except: # noqa: E722
707
+ return False
708
+
709
+ def read_text(self, path, encoding=None, errors=None, newline=None, **kwargs):
710
+ """Get the contents of the file as a string.
711
+
712
+ Parameters
713
+ ----------
714
+ path: str
715
+ URL of file on this filesystems
716
+ encoding, errors, newline: same as `open`.
717
+ """
718
+ with self.open(
719
+ path,
720
+ mode="r",
721
+ encoding=encoding,
722
+ errors=errors,
723
+ newline=newline,
724
+ **kwargs,
725
+ ) as f:
726
+ return f.read()
727
+
728
+ def write_text(
729
+ self, path, value, encoding=None, errors=None, newline=None, **kwargs
730
+ ):
731
+ """Write the text to the given file.
732
+
733
+ An existing file will be overwritten.
734
+
735
+ Parameters
736
+ ----------
737
+ path: str
738
+ URL of file on this filesystems
739
+ value: str
740
+ Text to write.
741
+ encoding, errors, newline: same as `open`.
742
+ """
743
+ with self.open(
744
+ path,
745
+ mode="w",
746
+ encoding=encoding,
747
+ errors=errors,
748
+ newline=newline,
749
+ **kwargs,
750
+ ) as f:
751
+ return f.write(value)
752
+
753
+ def cat_file(self, path, start=None, end=None, **kwargs):
754
+ """Get the content of a file
755
+
756
+ Parameters
757
+ ----------
758
+ path: URL of file on this filesystems
759
+ start, end: int
760
+ Bytes limits of the read. If negative, backwards from end,
761
+ like usual python slices. Either can be None for start or
762
+ end of file, respectively
763
+ kwargs: passed to ``open()``.
764
+ """
765
+ # explicitly set buffering off?
766
+ with self.open(path, "rb", **kwargs) as f:
767
+ if start is not None:
768
+ if start >= 0:
769
+ f.seek(start)
770
+ else:
771
+ f.seek(max(0, f.size + start))
772
+ if end is not None:
773
+ if end < 0:
774
+ end = f.size + end
775
+ return f.read(end - f.tell())
776
+ return f.read()
777
+
778
+ def pipe_file(self, path, value, **kwargs):
779
+ """Set the bytes of given file"""
780
+ with self.open(path, "wb", **kwargs) as f:
781
+ f.write(value)
782
+
783
+ def pipe(self, path, value=None, **kwargs):
784
+ """Put value into path
785
+
786
+ (counterpart to ``cat``)
787
+
788
+ Parameters
789
+ ----------
790
+ path: string or dict(str, bytes)
791
+ If a string, a single remote location to put ``value`` bytes; if a dict,
792
+ a mapping of {path: bytesvalue}.
793
+ value: bytes, optional
794
+ If using a single path, these are the bytes to put there. Ignored if
795
+ ``path`` is a dict
796
+ """
797
+ if isinstance(path, str):
798
+ self.pipe_file(self._strip_protocol(path), value, **kwargs)
799
+ elif isinstance(path, dict):
800
+ for k, v in path.items():
801
+ self.pipe_file(self._strip_protocol(k), v, **kwargs)
802
+ else:
803
+ raise ValueError("path must be str or dict")
804
+
805
+ def cat_ranges(
806
+ self, paths, starts, ends, max_gap=None, on_error="return", **kwargs
807
+ ):
808
+ """Get the contents of byte ranges from one or more files
809
+
810
+ Parameters
811
+ ----------
812
+ paths: list
813
+ A list of of filepaths on this filesystems
814
+ starts, ends: int or list
815
+ Bytes limits of the read. If using a single int, the same value will be
816
+ used to read all the specified files.
817
+ """
818
+ if max_gap is not None:
819
+ raise NotImplementedError
820
+ if not isinstance(paths, list):
821
+ raise TypeError
822
+ if not isinstance(starts, list):
823
+ starts = [starts] * len(paths)
824
+ if not isinstance(ends, list):
825
+ ends = [ends] * len(paths)
826
+ if len(starts) != len(paths) or len(ends) != len(paths):
827
+ raise ValueError
828
+ out = []
829
+ for p, s, e in zip(paths, starts, ends):
830
+ try:
831
+ out.append(self.cat_file(p, s, e))
832
+ except Exception as e:
833
+ if on_error == "return":
834
+ out.append(e)
835
+ else:
836
+ raise
837
+ return out
838
+
839
+ def cat(self, path, recursive=False, on_error="raise", **kwargs):
840
+ """Fetch (potentially multiple) paths' contents
841
+
842
+ Parameters
843
+ ----------
844
+ recursive: bool
845
+ If True, assume the path(s) are directories, and get all the
846
+ contained files
847
+ on_error : "raise", "omit", "return"
848
+ If raise, an underlying exception will be raised (converted to KeyError
849
+ if the type is in self.missing_exceptions); if omit, keys with exception
850
+ will simply not be included in the output; if "return", all keys are
851
+ included in the output, but the value will be bytes or an exception
852
+ instance.
853
+ kwargs: passed to cat_file
854
+
855
+ Returns
856
+ -------
857
+ dict of {path: contents} if there are multiple paths
858
+ or the path has been otherwise expanded
859
+ """
860
+ paths = self.expand_path(path, recursive=recursive)
861
+ if (
862
+ len(paths) > 1
863
+ or isinstance(path, list)
864
+ or paths[0] != self._strip_protocol(path)
865
+ ):
866
+ out = {}
867
+ for path in paths:
868
+ try:
869
+ out[path] = self.cat_file(path, **kwargs)
870
+ except Exception as e:
871
+ if on_error == "raise":
872
+ raise
873
+ if on_error == "return":
874
+ out[path] = e
875
+ return out
876
+ else:
877
+ return self.cat_file(paths[0], **kwargs)
878
+
879
+ def get_file(self, rpath, lpath, callback=DEFAULT_CALLBACK, outfile=None, **kwargs):
880
+ """Copy single remote file to local"""
881
+ from .implementations.local import LocalFileSystem
882
+
883
+ if isfilelike(lpath):
884
+ outfile = lpath
885
+ elif self.isdir(rpath):
886
+ os.makedirs(lpath, exist_ok=True)
887
+ return None
888
+
889
+ fs = LocalFileSystem(auto_mkdir=True)
890
+ fs.makedirs(fs._parent(lpath), exist_ok=True)
891
+
892
+ with self.open(rpath, "rb", **kwargs) as f1:
893
+ if outfile is None:
894
+ outfile = open(lpath, "wb")
895
+
896
+ try:
897
+ callback.set_size(getattr(f1, "size", None))
898
+ data = True
899
+ while data:
900
+ data = f1.read(self.blocksize)
901
+ segment_len = outfile.write(data)
902
+ if segment_len is None:
903
+ segment_len = len(data)
904
+ callback.relative_update(segment_len)
905
+ finally:
906
+ if not isfilelike(lpath):
907
+ outfile.close()
908
+
909
+ def get(
910
+ self,
911
+ rpath,
912
+ lpath,
913
+ recursive=False,
914
+ callback=DEFAULT_CALLBACK,
915
+ maxdepth=None,
916
+ **kwargs,
917
+ ):
918
+ """Copy file(s) to local.
919
+
920
+ Copies a specific file or tree of files (if recursive=True). If lpath
921
+ ends with a "/", it will be assumed to be a directory, and target files
922
+ will go within. Can submit a list of paths, which may be glob-patterns
923
+ and will be expanded.
924
+
925
+ Calls get_file for each source.
926
+ """
927
+ if isinstance(lpath, list) and isinstance(rpath, list):
928
+ # No need to expand paths when both source and destination
929
+ # are provided as lists
930
+ rpaths = rpath
931
+ lpaths = lpath
932
+ else:
933
+ from .implementations.local import (
934
+ LocalFileSystem,
935
+ make_path_posix,
936
+ trailing_sep,
937
+ )
938
+
939
+ source_is_str = isinstance(rpath, str)
940
+ rpaths = self.expand_path(rpath, recursive=recursive, maxdepth=maxdepth)
941
+ if source_is_str and (not recursive or maxdepth is not None):
942
+ # Non-recursive glob does not copy directories
943
+ rpaths = [p for p in rpaths if not (trailing_sep(p) or self.isdir(p))]
944
+ if not rpaths:
945
+ return
946
+
947
+ if isinstance(lpath, str):
948
+ lpath = make_path_posix(lpath)
949
+
950
+ source_is_file = len(rpaths) == 1
951
+ dest_is_dir = isinstance(lpath, str) and (
952
+ trailing_sep(lpath) or LocalFileSystem().isdir(lpath)
953
+ )
954
+
955
+ exists = source_is_str and (
956
+ (has_magic(rpath) and source_is_file)
957
+ or (not has_magic(rpath) and dest_is_dir and not trailing_sep(rpath))
958
+ )
959
+ lpaths = other_paths(
960
+ rpaths,
961
+ lpath,
962
+ exists=exists,
963
+ flatten=not source_is_str,
964
+ )
965
+
966
+ callback.set_size(len(lpaths))
967
+ for lpath, rpath in callback.wrap(zip(lpaths, rpaths)):
968
+ with callback.branched(rpath, lpath) as child:
969
+ self.get_file(rpath, lpath, callback=child, **kwargs)
970
+
971
+ def put_file(self, lpath, rpath, callback=DEFAULT_CALLBACK, **kwargs):
972
+ """Copy single file to remote"""
973
+ if os.path.isdir(lpath):
974
+ self.makedirs(rpath, exist_ok=True)
975
+ return None
976
+
977
+ with open(lpath, "rb") as f1:
978
+ size = f1.seek(0, 2)
979
+ callback.set_size(size)
980
+ f1.seek(0)
981
+
982
+ self.mkdirs(self._parent(os.fspath(rpath)), exist_ok=True)
983
+ with self.open(rpath, "wb", **kwargs) as f2:
984
+ while f1.tell() < size:
985
+ data = f1.read(self.blocksize)
986
+ segment_len = f2.write(data)
987
+ if segment_len is None:
988
+ segment_len = len(data)
989
+ callback.relative_update(segment_len)
990
+
991
+ def put(
992
+ self,
993
+ lpath,
994
+ rpath,
995
+ recursive=False,
996
+ callback=DEFAULT_CALLBACK,
997
+ maxdepth=None,
998
+ **kwargs,
999
+ ):
1000
+ """Copy file(s) from local.
1001
+
1002
+ Copies a specific file or tree of files (if recursive=True). If rpath
1003
+ ends with a "/", it will be assumed to be a directory, and target files
1004
+ will go within.
1005
+
1006
+ Calls put_file for each source.
1007
+ """
1008
+ if isinstance(lpath, list) and isinstance(rpath, list):
1009
+ # No need to expand paths when both source and destination
1010
+ # are provided as lists
1011
+ rpaths = rpath
1012
+ lpaths = lpath
1013
+ else:
1014
+ from .implementations.local import (
1015
+ LocalFileSystem,
1016
+ make_path_posix,
1017
+ trailing_sep,
1018
+ )
1019
+
1020
+ source_is_str = isinstance(lpath, str)
1021
+ if source_is_str:
1022
+ lpath = make_path_posix(lpath)
1023
+ fs = LocalFileSystem()
1024
+ lpaths = fs.expand_path(lpath, recursive=recursive, maxdepth=maxdepth)
1025
+ if source_is_str and (not recursive or maxdepth is not None):
1026
+ # Non-recursive glob does not copy directories
1027
+ lpaths = [p for p in lpaths if not (trailing_sep(p) or fs.isdir(p))]
1028
+ if not lpaths:
1029
+ return
1030
+
1031
+ source_is_file = len(lpaths) == 1
1032
+ dest_is_dir = isinstance(rpath, str) and (
1033
+ trailing_sep(rpath) or self.isdir(rpath)
1034
+ )
1035
+
1036
+ rpath = (
1037
+ self._strip_protocol(rpath)
1038
+ if isinstance(rpath, str)
1039
+ else [self._strip_protocol(p) for p in rpath]
1040
+ )
1041
+ exists = source_is_str and (
1042
+ (has_magic(lpath) and source_is_file)
1043
+ or (not has_magic(lpath) and dest_is_dir and not trailing_sep(lpath))
1044
+ )
1045
+ rpaths = other_paths(
1046
+ lpaths,
1047
+ rpath,
1048
+ exists=exists,
1049
+ flatten=not source_is_str,
1050
+ )
1051
+
1052
+ callback.set_size(len(rpaths))
1053
+ for lpath, rpath in callback.wrap(zip(lpaths, rpaths)):
1054
+ with callback.branched(lpath, rpath) as child:
1055
+ self.put_file(lpath, rpath, callback=child, **kwargs)
1056
+
1057
+ def head(self, path, size=1024):
1058
+ """Get the first ``size`` bytes from file"""
1059
+ with self.open(path, "rb") as f:
1060
+ return f.read(size)
1061
+
1062
+ def tail(self, path, size=1024):
1063
+ """Get the last ``size`` bytes from file"""
1064
+ with self.open(path, "rb") as f:
1065
+ f.seek(max(-size, -f.size), 2)
1066
+ return f.read()
1067
+
1068
+ def cp_file(self, path1, path2, **kwargs):
1069
+ raise NotImplementedError
1070
+
1071
+ def copy(
1072
+ self, path1, path2, recursive=False, maxdepth=None, on_error=None, **kwargs
1073
+ ):
1074
+ """Copy within two locations in the filesystem
1075
+
1076
+ on_error : "raise", "ignore"
1077
+ If raise, any not-found exceptions will be raised; if ignore any
1078
+ not-found exceptions will cause the path to be skipped; defaults to
1079
+ raise unless recursive is true, where the default is ignore
1080
+ """
1081
+ if on_error is None and recursive:
1082
+ on_error = "ignore"
1083
+ elif on_error is None:
1084
+ on_error = "raise"
1085
+
1086
+ if isinstance(path1, list) and isinstance(path2, list):
1087
+ # No need to expand paths when both source and destination
1088
+ # are provided as lists
1089
+ paths1 = path1
1090
+ paths2 = path2
1091
+ else:
1092
+ from .implementations.local import trailing_sep
1093
+
1094
+ source_is_str = isinstance(path1, str)
1095
+ paths1 = self.expand_path(path1, recursive=recursive, maxdepth=maxdepth)
1096
+ if source_is_str and (not recursive or maxdepth is not None):
1097
+ # Non-recursive glob does not copy directories
1098
+ paths1 = [p for p in paths1 if not (trailing_sep(p) or self.isdir(p))]
1099
+ if not paths1:
1100
+ return
1101
+
1102
+ source_is_file = len(paths1) == 1
1103
+ dest_is_dir = isinstance(path2, str) and (
1104
+ trailing_sep(path2) or self.isdir(path2)
1105
+ )
1106
+
1107
+ exists = source_is_str and (
1108
+ (has_magic(path1) and source_is_file)
1109
+ or (not has_magic(path1) and dest_is_dir and not trailing_sep(path1))
1110
+ )
1111
+ paths2 = other_paths(
1112
+ paths1,
1113
+ path2,
1114
+ exists=exists,
1115
+ flatten=not source_is_str,
1116
+ )
1117
+
1118
+ for p1, p2 in zip(paths1, paths2):
1119
+ try:
1120
+ self.cp_file(p1, p2, **kwargs)
1121
+ except FileNotFoundError:
1122
+ if on_error == "raise":
1123
+ raise
1124
+
1125
+ def expand_path(self, path, recursive=False, maxdepth=None, **kwargs):
1126
+ """Turn one or more globs or directories into a list of all matching paths
1127
+ to files or directories.
1128
+
1129
+ kwargs are passed to ``glob`` or ``find``, which may in turn call ``ls``
1130
+ """
1131
+
1132
+ if maxdepth is not None and maxdepth < 1:
1133
+ raise ValueError("maxdepth must be at least 1")
1134
+
1135
+ if isinstance(path, (str, os.PathLike)):
1136
+ out = self.expand_path([path], recursive, maxdepth)
1137
+ else:
1138
+ out = set()
1139
+ path = [self._strip_protocol(p) for p in path]
1140
+ for p in path:
1141
+ if has_magic(p):
1142
+ bit = set(self.glob(p, maxdepth=maxdepth, **kwargs))
1143
+ out |= bit
1144
+ if recursive:
1145
+ # glob call above expanded one depth so if maxdepth is defined
1146
+ # then decrement it in expand_path call below. If it is zero
1147
+ # after decrementing then avoid expand_path call.
1148
+ if maxdepth is not None and maxdepth <= 1:
1149
+ continue
1150
+ out |= set(
1151
+ self.expand_path(
1152
+ list(bit),
1153
+ recursive=recursive,
1154
+ maxdepth=maxdepth - 1 if maxdepth is not None else None,
1155
+ **kwargs,
1156
+ )
1157
+ )
1158
+ continue
1159
+ elif recursive:
1160
+ rec = set(
1161
+ self.find(
1162
+ p, maxdepth=maxdepth, withdirs=True, detail=False, **kwargs
1163
+ )
1164
+ )
1165
+ out |= rec
1166
+ if p not in out and (recursive is False or self.exists(p)):
1167
+ # should only check once, for the root
1168
+ out.add(p)
1169
+ if not out:
1170
+ raise FileNotFoundError(path)
1171
+ return sorted(out)
1172
+
1173
+ def mv(self, path1, path2, recursive=False, maxdepth=None, **kwargs):
1174
+ """Move file(s) from one location to another"""
1175
+ if path1 == path2:
1176
+ logger.debug("%s mv: The paths are the same, so no files were moved.", self)
1177
+ else:
1178
+ self.copy(path1, path2, recursive=recursive, maxdepth=maxdepth)
1179
+ self.rm(path1, recursive=recursive)
1180
+
1181
+ def rm_file(self, path):
1182
+ """Delete a file"""
1183
+ self._rm(path)
1184
+
1185
+ def _rm(self, path):
1186
+ """Delete one file"""
1187
+ # this is the old name for the method, prefer rm_file
1188
+ raise NotImplementedError
1189
+
1190
+ def rm(self, path, recursive=False, maxdepth=None):
1191
+ """Delete files.
1192
+
1193
+ Parameters
1194
+ ----------
1195
+ path: str or list of str
1196
+ File(s) to delete.
1197
+ recursive: bool
1198
+ If file(s) are directories, recursively delete contents and then
1199
+ also remove the directory
1200
+ maxdepth: int or None
1201
+ Depth to pass to walk for finding files to delete, if recursive.
1202
+ If None, there will be no limit and infinite recursion may be
1203
+ possible.
1204
+ """
1205
+ path = self.expand_path(path, recursive=recursive, maxdepth=maxdepth)
1206
+ for p in reversed(path):
1207
+ self.rm_file(p)
1208
+
1209
+ @classmethod
1210
+ def _parent(cls, path):
1211
+ path = cls._strip_protocol(path)
1212
+ if "/" in path:
1213
+ parent = path.rsplit("/", 1)[0].lstrip(cls.root_marker)
1214
+ return cls.root_marker + parent
1215
+ else:
1216
+ return cls.root_marker
1217
+
1218
+ def _open(
1219
+ self,
1220
+ path,
1221
+ mode="rb",
1222
+ block_size=None,
1223
+ autocommit=True,
1224
+ cache_options=None,
1225
+ **kwargs,
1226
+ ):
1227
+ """Return raw bytes-mode file-like from the file-system"""
1228
+ return AbstractBufferedFile(
1229
+ self,
1230
+ path,
1231
+ mode,
1232
+ block_size,
1233
+ autocommit,
1234
+ cache_options=cache_options,
1235
+ **kwargs,
1236
+ )
1237
+
1238
+ def open(
1239
+ self,
1240
+ path,
1241
+ mode="rb",
1242
+ block_size=None,
1243
+ cache_options=None,
1244
+ compression=None,
1245
+ **kwargs,
1246
+ ):
1247
+ """
1248
+ Return a file-like object from the filesystem
1249
+
1250
+ The resultant instance must function correctly in a context ``with``
1251
+ block.
1252
+
1253
+ Parameters
1254
+ ----------
1255
+ path: str
1256
+ Target file
1257
+ mode: str like 'rb', 'w'
1258
+ See builtin ``open()``
1259
+ block_size: int
1260
+ Some indication of buffering - this is a value in bytes
1261
+ cache_options : dict, optional
1262
+ Extra arguments to pass through to the cache.
1263
+ compression: string or None
1264
+ If given, open file using compression codec. Can either be a compression
1265
+ name (a key in ``fsspec.compression.compr``) or "infer" to guess the
1266
+ compression from the filename suffix.
1267
+ encoding, errors, newline: passed on to TextIOWrapper for text mode
1268
+ """
1269
+ import io
1270
+
1271
+ path = self._strip_protocol(path)
1272
+ if "b" not in mode:
1273
+ mode = mode.replace("t", "") + "b"
1274
+
1275
+ text_kwargs = {
1276
+ k: kwargs.pop(k)
1277
+ for k in ["encoding", "errors", "newline"]
1278
+ if k in kwargs
1279
+ }
1280
+ return io.TextIOWrapper(
1281
+ self.open(
1282
+ path,
1283
+ mode,
1284
+ block_size=block_size,
1285
+ cache_options=cache_options,
1286
+ compression=compression,
1287
+ **kwargs,
1288
+ ),
1289
+ **text_kwargs,
1290
+ )
1291
+ else:
1292
+ ac = kwargs.pop("autocommit", not self._intrans)
1293
+ f = self._open(
1294
+ path,
1295
+ mode=mode,
1296
+ block_size=block_size,
1297
+ autocommit=ac,
1298
+ cache_options=cache_options,
1299
+ **kwargs,
1300
+ )
1301
+ if compression is not None:
1302
+ from fsspec.compression import compr
1303
+ from fsspec.core import get_compression
1304
+
1305
+ compression = get_compression(path, compression)
1306
+ compress = compr[compression]
1307
+ f = compress(f, mode=mode[0])
1308
+
1309
+ if not ac and "r" not in mode:
1310
+ self.transaction.files.append(f)
1311
+ return f
1312
+
1313
+ def touch(self, path, truncate=True, **kwargs):
1314
+ """Create empty file, or update timestamp
1315
+
1316
+ Parameters
1317
+ ----------
1318
+ path: str
1319
+ file location
1320
+ truncate: bool
1321
+ If True, always set file size to 0; if False, update timestamp and
1322
+ leave file unchanged, if backend allows this
1323
+ """
1324
+ if truncate or not self.exists(path):
1325
+ with self.open(path, "wb", **kwargs):
1326
+ pass
1327
+ else:
1328
+ raise NotImplementedError # update timestamp, if possible
1329
+
1330
+ def ukey(self, path):
1331
+ """Hash of file properties, to tell if it has changed"""
1332
+ return sha256(str(self.info(path)).encode()).hexdigest()
1333
+
1334
+ def read_block(self, fn, offset, length, delimiter=None):
1335
+ """Read a block of bytes from
1336
+
1337
+ Starting at ``offset`` of the file, read ``length`` bytes. If
1338
+ ``delimiter`` is set then we ensure that the read starts and stops at
1339
+ delimiter boundaries that follow the locations ``offset`` and ``offset
1340
+ + length``. If ``offset`` is zero then we start at zero. The
1341
+ bytestring returned WILL include the end delimiter string.
1342
+
1343
+ If offset+length is beyond the eof, reads to eof.
1344
+
1345
+ Parameters
1346
+ ----------
1347
+ fn: string
1348
+ Path to filename
1349
+ offset: int
1350
+ Byte offset to start read
1351
+ length: int
1352
+ Number of bytes to read. If None, read to end.
1353
+ delimiter: bytes (optional)
1354
+ Ensure reading starts and stops at delimiter bytestring
1355
+
1356
+ Examples
1357
+ --------
1358
+ >>> fs.read_block('data/file.csv', 0, 13) # doctest: +SKIP
1359
+ b'Alice, 100\\nBo'
1360
+ >>> fs.read_block('data/file.csv', 0, 13, delimiter=b'\\n') # doctest: +SKIP
1361
+ b'Alice, 100\\nBob, 200\\n'
1362
+
1363
+ Use ``length=None`` to read to the end of the file.
1364
+ >>> fs.read_block('data/file.csv', 0, None, delimiter=b'\\n') # doctest: +SKIP
1365
+ b'Alice, 100\\nBob, 200\\nCharlie, 300'
1366
+
1367
+ See Also
1368
+ --------
1369
+ :func:`fsspec.utils.read_block`
1370
+ """
1371
+ with self.open(fn, "rb") as f:
1372
+ size = f.size
1373
+ if length is None:
1374
+ length = size
1375
+ if size is not None and offset + length > size:
1376
+ length = size - offset
1377
+ return read_block(f, offset, length, delimiter)
1378
+
1379
+ def to_json(self):
1380
+ """
1381
+ JSON representation of this filesystem instance
1382
+
1383
+ Returns
1384
+ -------
1385
+ str: JSON structure with keys cls (the python location of this class),
1386
+ protocol (text name of this class's protocol, first one in case of
1387
+ multiple), args (positional args, usually empty), and all other
1388
+ kwargs as their own keys.
1389
+ """
1390
+ import json
1391
+
1392
+ cls = type(self)
1393
+ cls = ".".join((cls.__module__, cls.__name__))
1394
+ proto = (
1395
+ self.protocol[0]
1396
+ if isinstance(self.protocol, (tuple, list))
1397
+ else self.protocol
1398
+ )
1399
+ return json.dumps(
1400
+ dict(
1401
+ cls=cls,
1402
+ protocol=proto,
1403
+ args=self.storage_args,
1404
+ **self.storage_options,
1405
+ )
1406
+ )
1407
+
1408
+ @staticmethod
1409
+ def from_json(blob):
1410
+ """
1411
+ Recreate a filesystem instance from JSON representation
1412
+
1413
+ See ``.to_json()`` for the expected structure of the input
1414
+
1415
+ Parameters
1416
+ ----------
1417
+ blob: str
1418
+
1419
+ Returns
1420
+ -------
1421
+ file system instance, not necessarily of this particular class.
1422
+ """
1423
+ import json
1424
+
1425
+ from .registry import _import_class, get_filesystem_class
1426
+
1427
+ dic = json.loads(blob)
1428
+ protocol = dic.pop("protocol")
1429
+ try:
1430
+ cls = _import_class(dic.pop("cls"))
1431
+ except (ImportError, ValueError, RuntimeError, KeyError):
1432
+ cls = get_filesystem_class(protocol)
1433
+ return cls(*dic.pop("args", ()), **dic)
1434
+
1435
+ def _get_pyarrow_filesystem(self):
1436
+ """
1437
+ Make a version of the FS instance which will be acceptable to pyarrow
1438
+ """
1439
+ # all instances already also derive from pyarrow
1440
+ return self
1441
+
1442
+ def get_mapper(self, root="", check=False, create=False, missing_exceptions=None):
1443
+ """Create key/value store based on this file-system
1444
+
1445
+ Makes a MutableMapping interface to the FS at the given root path.
1446
+ See ``fsspec.mapping.FSMap`` for further details.
1447
+ """
1448
+ from .mapping import FSMap
1449
+
1450
+ return FSMap(
1451
+ root,
1452
+ self,
1453
+ check=check,
1454
+ create=create,
1455
+ missing_exceptions=missing_exceptions,
1456
+ )
1457
+
1458
+ @classmethod
1459
+ def clear_instance_cache(cls):
1460
+ """
1461
+ Clear the cache of filesystem instances.
1462
+
1463
+ Notes
1464
+ -----
1465
+ Unless overridden by setting the ``cachable`` class attribute to False,
1466
+ the filesystem class stores a reference to newly created instances. This
1467
+ prevents Python's normal rules around garbage collection from working,
1468
+ since the instances refcount will not drop to zero until
1469
+ ``clear_instance_cache`` is called.
1470
+ """
1471
+ cls._cache.clear()
1472
+
1473
+ def created(self, path):
1474
+ """Return the created timestamp of a file as a datetime.datetime"""
1475
+ raise NotImplementedError
1476
+
1477
+ def modified(self, path):
1478
+ """Return the modified timestamp of a file as a datetime.datetime"""
1479
+ raise NotImplementedError
1480
+
1481
+ # ------------------------------------------------------------------------
1482
+ # Aliases
1483
+
1484
+ def read_bytes(self, path, start=None, end=None, **kwargs):
1485
+ """Alias of `AbstractFileSystem.cat_file`."""
1486
+ return self.cat_file(path, start=start, end=end, **kwargs)
1487
+
1488
+ def write_bytes(self, path, value, **kwargs):
1489
+ """Alias of `AbstractFileSystem.pipe_file`."""
1490
+ self.pipe_file(path, value, **kwargs)
1491
+
1492
+ def makedir(self, path, create_parents=True, **kwargs):
1493
+ """Alias of `AbstractFileSystem.mkdir`."""
1494
+ return self.mkdir(path, create_parents=create_parents, **kwargs)
1495
+
1496
+ def mkdirs(self, path, exist_ok=False):
1497
+ """Alias of `AbstractFileSystem.makedirs`."""
1498
+ return self.makedirs(path, exist_ok=exist_ok)
1499
+
1500
+ def listdir(self, path, detail=True, **kwargs):
1501
+ """Alias of `AbstractFileSystem.ls`."""
1502
+ return self.ls(path, detail=detail, **kwargs)
1503
+
1504
+ def cp(self, path1, path2, **kwargs):
1505
+ """Alias of `AbstractFileSystem.copy`."""
1506
+ return self.copy(path1, path2, **kwargs)
1507
+
1508
+ def move(self, path1, path2, **kwargs):
1509
+ """Alias of `AbstractFileSystem.mv`."""
1510
+ return self.mv(path1, path2, **kwargs)
1511
+
1512
+ def stat(self, path, **kwargs):
1513
+ """Alias of `AbstractFileSystem.info`."""
1514
+ return self.info(path, **kwargs)
1515
+
1516
+ def disk_usage(self, path, total=True, maxdepth=None, **kwargs):
1517
+ """Alias of `AbstractFileSystem.du`."""
1518
+ return self.du(path, total=total, maxdepth=maxdepth, **kwargs)
1519
+
1520
+ def rename(self, path1, path2, **kwargs):
1521
+ """Alias of `AbstractFileSystem.mv`."""
1522
+ return self.mv(path1, path2, **kwargs)
1523
+
1524
+ def delete(self, path, recursive=False, maxdepth=None):
1525
+ """Alias of `AbstractFileSystem.rm`."""
1526
+ return self.rm(path, recursive=recursive, maxdepth=maxdepth)
1527
+
1528
+ def upload(self, lpath, rpath, recursive=False, **kwargs):
1529
+ """Alias of `AbstractFileSystem.put`."""
1530
+ return self.put(lpath, rpath, recursive=recursive, **kwargs)
1531
+
1532
+ def download(self, rpath, lpath, recursive=False, **kwargs):
1533
+ """Alias of `AbstractFileSystem.get`."""
1534
+ return self.get(rpath, lpath, recursive=recursive, **kwargs)
1535
+
1536
+ def sign(self, path, expiration=100, **kwargs):
1537
+ """Create a signed URL representing the given path
1538
+
1539
+ Some implementations allow temporary URLs to be generated, as a
1540
+ way of delegating credentials.
1541
+
1542
+ Parameters
1543
+ ----------
1544
+ path : str
1545
+ The path on the filesystem
1546
+ expiration : int
1547
+ Number of seconds to enable the URL for (if supported)
1548
+
1549
+ Returns
1550
+ -------
1551
+ URL : str
1552
+ The signed URL
1553
+
1554
+ Raises
1555
+ ------
1556
+ NotImplementedError : if method is not implemented for a filesystem
1557
+ """
1558
+ raise NotImplementedError("Sign is not implemented for this filesystem")
1559
+
1560
+ def _isfilestore(self):
1561
+ # Originally inherited from pyarrow DaskFileSystem. Keeping this
1562
+ # here for backwards compatibility as long as pyarrow uses its
1563
+ # legacy fsspec-compatible filesystems and thus accepts fsspec
1564
+ # filesystems as well
1565
+ return False
1566
+
1567
+
1568
+ class AbstractBufferedFile(io.IOBase):
1569
+ """Convenient class to derive from to provide buffering
1570
+
1571
+ In the case that the backend does not provide a pythonic file-like object
1572
+ already, this class contains much of the logic to build one. The only
1573
+ methods that need to be overridden are ``_upload_chunk``,
1574
+ ``_initiate_upload`` and ``_fetch_range``.
1575
+ """
1576
+
1577
+ DEFAULT_BLOCK_SIZE = 5 * 2**20
1578
+ _details = None
1579
+
1580
+ def __init__(
1581
+ self,
1582
+ fs,
1583
+ path,
1584
+ mode="rb",
1585
+ block_size="default",
1586
+ autocommit=True,
1587
+ cache_type="readahead",
1588
+ cache_options=None,
1589
+ size=None,
1590
+ **kwargs,
1591
+ ):
1592
+ """
1593
+ Template for files with buffered reading and writing
1594
+
1595
+ Parameters
1596
+ ----------
1597
+ fs: instance of FileSystem
1598
+ path: str
1599
+ location in file-system
1600
+ mode: str
1601
+ Normal file modes. Currently only 'wb', 'ab' or 'rb'. Some file
1602
+ systems may be read-only, and some may not support append.
1603
+ block_size: int
1604
+ Buffer size for reading or writing, 'default' for class default
1605
+ autocommit: bool
1606
+ Whether to write to final destination; may only impact what
1607
+ happens when file is being closed.
1608
+ cache_type: {"readahead", "none", "mmap", "bytes"}, default "readahead"
1609
+ Caching policy in read mode. See the definitions in ``core``.
1610
+ cache_options : dict
1611
+ Additional options passed to the constructor for the cache specified
1612
+ by `cache_type`.
1613
+ size: int
1614
+ If given and in read mode, suppressed having to look up the file size
1615
+ kwargs:
1616
+ Gets stored as self.kwargs
1617
+ """
1618
+ from .core import caches
1619
+
1620
+ self.path = path
1621
+ self.fs = fs
1622
+ self.mode = mode
1623
+ self.blocksize = (
1624
+ self.DEFAULT_BLOCK_SIZE if block_size in ["default", None] else block_size
1625
+ )
1626
+ self.loc = 0
1627
+ self.autocommit = autocommit
1628
+ self.end = None
1629
+ self.start = None
1630
+ self.closed = False
1631
+
1632
+ if cache_options is None:
1633
+ cache_options = {}
1634
+
1635
+ if "trim" in kwargs:
1636
+ warnings.warn(
1637
+ "Passing 'trim' to control the cache behavior has been deprecated. "
1638
+ "Specify it within the 'cache_options' argument instead.",
1639
+ FutureWarning,
1640
+ )
1641
+ cache_options["trim"] = kwargs.pop("trim")
1642
+
1643
+ self.kwargs = kwargs
1644
+
1645
+ if mode not in {"ab", "rb", "wb"}:
1646
+ raise NotImplementedError("File mode not supported")
1647
+ if mode == "rb":
1648
+ if size is not None:
1649
+ self.size = size
1650
+ else:
1651
+ self.size = self.details["size"]
1652
+ self.cache = caches[cache_type](
1653
+ self.blocksize, self._fetch_range, self.size, **cache_options
1654
+ )
1655
+ else:
1656
+ self.buffer = io.BytesIO()
1657
+ self.offset = None
1658
+ self.forced = False
1659
+ self.location = None
1660
+
1661
+ @property
1662
+ def details(self):
1663
+ if self._details is None:
1664
+ self._details = self.fs.info(self.path)
1665
+ return self._details
1666
+
1667
+ @details.setter
1668
+ def details(self, value):
1669
+ self._details = value
1670
+ self.size = value["size"]
1671
+
1672
+ @property
1673
+ def full_name(self):
1674
+ return _unstrip_protocol(self.path, self.fs)
1675
+
1676
+ @property
1677
+ def closed(self):
1678
+ # get around this attr being read-only in IOBase
1679
+ # use getattr here, since this can be called during del
1680
+ return getattr(self, "_closed", True)
1681
+
1682
+ @closed.setter
1683
+ def closed(self, c):
1684
+ self._closed = c
1685
+
1686
+ def __hash__(self):
1687
+ if "w" in self.mode:
1688
+ return id(self)
1689
+ else:
1690
+ return int(tokenize(self.details), 16)
1691
+
1692
+ def __eq__(self, other):
1693
+ """Files are equal if they have the same checksum, only in read mode"""
1694
+ if self is other:
1695
+ return True
1696
+ return self.mode == "rb" and other.mode == "rb" and hash(self) == hash(other)
1697
+
1698
+ def commit(self):
1699
+ """Move from temp to final destination"""
1700
+
1701
+ def discard(self):
1702
+ """Throw away temporary file"""
1703
+
1704
+ def info(self):
1705
+ """File information about this path"""
1706
+ if "r" in self.mode:
1707
+ return self.details
1708
+ else:
1709
+ raise ValueError("Info not available while writing")
1710
+
1711
+ def tell(self):
1712
+ """Current file location"""
1713
+ return self.loc
1714
+
1715
+ def seek(self, loc, whence=0):
1716
+ """Set current file location
1717
+
1718
+ Parameters
1719
+ ----------
1720
+ loc: int
1721
+ byte location
1722
+ whence: {0, 1, 2}
1723
+ from start of file, current location or end of file, resp.
1724
+ """
1725
+ loc = int(loc)
1726
+ if not self.mode == "rb":
1727
+ raise OSError(ESPIPE, "Seek only available in read mode")
1728
+ if whence == 0:
1729
+ nloc = loc
1730
+ elif whence == 1:
1731
+ nloc = self.loc + loc
1732
+ elif whence == 2:
1733
+ nloc = self.size + loc
1734
+ else:
1735
+ raise ValueError(f"invalid whence ({whence}, should be 0, 1 or 2)")
1736
+ if nloc < 0:
1737
+ raise ValueError("Seek before start of file")
1738
+ self.loc = nloc
1739
+ return self.loc
1740
+
1741
+ def write(self, data):
1742
+ """
1743
+ Write data to buffer.
1744
+
1745
+ Buffer only sent on flush() or if buffer is greater than
1746
+ or equal to blocksize.
1747
+
1748
+ Parameters
1749
+ ----------
1750
+ data: bytes
1751
+ Set of bytes to be written.
1752
+ """
1753
+ if self.mode not in {"wb", "ab"}:
1754
+ raise ValueError("File not in write mode")
1755
+ if self.closed:
1756
+ raise ValueError("I/O operation on closed file.")
1757
+ if self.forced:
1758
+ raise ValueError("This file has been force-flushed, can only close")
1759
+ out = self.buffer.write(data)
1760
+ self.loc += out
1761
+ if self.buffer.tell() >= self.blocksize:
1762
+ self.flush()
1763
+ return out
1764
+
1765
+ def flush(self, force=False):
1766
+ """
1767
+ Write buffered data to backend store.
1768
+
1769
+ Writes the current buffer, if it is larger than the block-size, or if
1770
+ the file is being closed.
1771
+
1772
+ Parameters
1773
+ ----------
1774
+ force: bool
1775
+ When closing, write the last block even if it is smaller than
1776
+ blocks are allowed to be. Disallows further writing to this file.
1777
+ """
1778
+
1779
+ if self.closed:
1780
+ raise ValueError("Flush on closed file")
1781
+ if force and self.forced:
1782
+ raise ValueError("Force flush cannot be called more than once")
1783
+ if force:
1784
+ self.forced = True
1785
+
1786
+ if self.mode not in {"wb", "ab"}:
1787
+ # no-op to flush on read-mode
1788
+ return
1789
+
1790
+ if not force and self.buffer.tell() < self.blocksize:
1791
+ # Defer write on small block
1792
+ return
1793
+
1794
+ if self.offset is None:
1795
+ # Initialize a multipart upload
1796
+ self.offset = 0
1797
+ try:
1798
+ self._initiate_upload()
1799
+ except: # noqa: E722
1800
+ self.closed = True
1801
+ raise
1802
+
1803
+ if self._upload_chunk(final=force) is not False:
1804
+ self.offset += self.buffer.seek(0, 2)
1805
+ self.buffer = io.BytesIO()
1806
+
1807
+ def _upload_chunk(self, final=False):
1808
+ """Write one part of a multi-block file upload
1809
+
1810
+ Parameters
1811
+ ==========
1812
+ final: bool
1813
+ This is the last block, so should complete file, if
1814
+ self.autocommit is True.
1815
+ """
1816
+ # may not yet have been initialized, may need to call _initialize_upload
1817
+
1818
+ def _initiate_upload(self):
1819
+ """Create remote file/upload"""
1820
+ pass
1821
+
1822
+ def _fetch_range(self, start, end):
1823
+ """Get the specified set of bytes from remote"""
1824
+ raise NotImplementedError
1825
+
1826
+ def read(self, length=-1):
1827
+ """
1828
+ Return data from cache, or fetch pieces as necessary
1829
+
1830
+ Parameters
1831
+ ----------
1832
+ length: int (-1)
1833
+ Number of bytes to read; if <0, all remaining bytes.
1834
+ """
1835
+ length = -1 if length is None else int(length)
1836
+ if self.mode != "rb":
1837
+ raise ValueError("File not in read mode")
1838
+ if length < 0:
1839
+ length = self.size - self.loc
1840
+ if self.closed:
1841
+ raise ValueError("I/O operation on closed file.")
1842
+ logger.debug("%s read: %i - %i", self, self.loc, self.loc + length)
1843
+ if length == 0:
1844
+ # don't even bother calling fetch
1845
+ return b""
1846
+ out = self.cache._fetch(self.loc, self.loc + length)
1847
+ self.loc += len(out)
1848
+ return out
1849
+
1850
+ def readinto(self, b):
1851
+ """mirrors builtin file's readinto method
1852
+
1853
+ https://docs.python.org/3/library/io.html#io.RawIOBase.readinto
1854
+ """
1855
+ out = memoryview(b).cast("B")
1856
+ data = self.read(out.nbytes)
1857
+ out[: len(data)] = data
1858
+ return len(data)
1859
+
1860
+ def readuntil(self, char=b"\n", blocks=None):
1861
+ """Return data between current position and first occurrence of char
1862
+
1863
+ char is included in the output, except if the end of the tile is
1864
+ encountered first.
1865
+
1866
+ Parameters
1867
+ ----------
1868
+ char: bytes
1869
+ Thing to find
1870
+ blocks: None or int
1871
+ How much to read in each go. Defaults to file blocksize - which may
1872
+ mean a new read on every call.
1873
+ """
1874
+ out = []
1875
+ while True:
1876
+ start = self.tell()
1877
+ part = self.read(blocks or self.blocksize)
1878
+ if len(part) == 0:
1879
+ break
1880
+ found = part.find(char)
1881
+ if found > -1:
1882
+ out.append(part[: found + len(char)])
1883
+ self.seek(start + found + len(char))
1884
+ break
1885
+ out.append(part)
1886
+ return b"".join(out)
1887
+
1888
+ def readline(self):
1889
+ """Read until first occurrence of newline character
1890
+
1891
+ Note that, because of character encoding, this is not necessarily a
1892
+ true line ending.
1893
+ """
1894
+ return self.readuntil(b"\n")
1895
+
1896
+ def __next__(self):
1897
+ out = self.readline()
1898
+ if out:
1899
+ return out
1900
+ raise StopIteration
1901
+
1902
+ def __iter__(self):
1903
+ return self
1904
+
1905
+ def readlines(self):
1906
+ """Return all data, split by the newline character"""
1907
+ data = self.read()
1908
+ lines = data.split(b"\n")
1909
+ out = [l + b"\n" for l in lines[:-1]]
1910
+ if data.endswith(b"\n"):
1911
+ return out
1912
+ else:
1913
+ return out + [lines[-1]]
1914
+ # return list(self) ???
1915
+
1916
+ def readinto1(self, b):
1917
+ return self.readinto(b)
1918
+
1919
+ def close(self):
1920
+ """Close file
1921
+
1922
+ Finalizes writes, discards cache
1923
+ """
1924
+ if getattr(self, "_unclosable", False):
1925
+ return
1926
+ if self.closed:
1927
+ return
1928
+ if self.mode == "rb":
1929
+ self.cache = None
1930
+ else:
1931
+ if not self.forced:
1932
+ self.flush(force=True)
1933
+
1934
+ if self.fs is not None:
1935
+ self.fs.invalidate_cache(self.path)
1936
+ self.fs.invalidate_cache(self.fs._parent(self.path))
1937
+
1938
+ self.closed = True
1939
+
1940
+ def readable(self):
1941
+ """Whether opened for reading"""
1942
+ return self.mode == "rb" and not self.closed
1943
+
1944
+ def seekable(self):
1945
+ """Whether is seekable (only in read mode)"""
1946
+ return self.readable()
1947
+
1948
+ def writable(self):
1949
+ """Whether opened for writing"""
1950
+ return self.mode in {"wb", "ab"} and not self.closed
1951
+
1952
+ def __del__(self):
1953
+ if not self.closed:
1954
+ self.close()
1955
+
1956
+ def __str__(self):
1957
+ return f"<File-like object {type(self.fs).__name__}, {self.path}>"
1958
+
1959
+ __repr__ = __str__
1960
+
1961
+ def __enter__(self):
1962
+ return self
1963
+
1964
+ def __exit__(self, *args):
1965
+ self.close()
tuning-competition-baseline/.venv/lib/python3.11/site-packages/functorch/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (933 Bytes). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/functorch/_src/__init__.py ADDED
File without changes
tuning-competition-baseline/.venv/lib/python3.11/site-packages/functorch/_src/eager_transforms/__init__.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ # This file has moved to under torch/_functorch. It is not public API.
2
+ # If you are not a PyTorch developer and you are relying on the following
3
+ # imports, please file an issue.
4
+ from torch._functorch.eager_transforms import (
5
+ _assert_wrapped_functional,
6
+ _unwrap_functional_tensor,
7
+ )
tuning-competition-baseline/.venv/lib/python3.11/site-packages/functorch/_src/eager_transforms/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (392 Bytes). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/functorch/compile/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (1.45 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/functorch/dim/__pycache__/dim.cpython-311.pyc ADDED
Binary file (7.09 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/functorch/dim/dim.py ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ # All rights reserved.
3
+ #
4
+ # This source code is licensed under the BSD-style license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+ import dis
7
+ import inspect
8
+
9
+ from dataclasses import dataclass
10
+ from typing import Union
11
+
12
+ from . import DimList
13
+
14
+ _vmap_levels = []
15
+
16
+
17
+ @dataclass
18
+ class LevelInfo:
19
+ level: int
20
+ alive: bool = True
21
+
22
+
23
+ class Dim:
24
+ def __init__(self, name: str, size: Union[None, int] = None):
25
+ self.name = name
26
+ self._size = None
27
+ self._vmap_level = None
28
+ if size is not None:
29
+ self.size = size
30
+
31
+ def __del__(self):
32
+ if self._vmap_level is not None:
33
+ _vmap_active_levels[self._vmap_stack].alive = False # noqa: F821
34
+ while (
35
+ not _vmap_levels[-1].alive
36
+ and current_level() == _vmap_levels[-1].level # noqa: F821
37
+ ):
38
+ _vmap_decrement_nesting() # noqa: F821
39
+ _vmap_levels.pop()
40
+
41
+ @property
42
+ def size(self):
43
+ assert self.is_bound
44
+ return self._size
45
+
46
+ @size.setter
47
+ def size(self, size: int):
48
+ from . import DimensionBindError
49
+
50
+ if self._size is None:
51
+ self._size = size
52
+ self._vmap_level = _vmap_increment_nesting(size, "same") # noqa: F821
53
+ self._vmap_stack = len(_vmap_levels)
54
+ _vmap_levels.append(LevelInfo(self._vmap_level))
55
+
56
+ elif self._size != size:
57
+ raise DimensionBindError(
58
+ f"Dim '{self}' previously bound to a dimension of size {self._size} cannot bind to a dimension of size {size}"
59
+ )
60
+
61
+ @property
62
+ def is_bound(self):
63
+ return self._size is not None
64
+
65
+ def __repr__(self):
66
+ return self.name
67
+
68
+
69
+ def extract_name(inst):
70
+ assert inst.opname == "STORE_FAST" or inst.opname == "STORE_NAME"
71
+ return inst.argval
72
+
73
+
74
+ _cache = {}
75
+
76
+
77
+ def dims(lists=0):
78
+ frame = inspect.currentframe()
79
+ assert frame is not None
80
+ calling_frame = frame.f_back
81
+ assert calling_frame is not None
82
+ code, lasti = calling_frame.f_code, calling_frame.f_lasti
83
+ key = (code, lasti)
84
+ if key not in _cache:
85
+ first = lasti // 2 + 1
86
+ instructions = list(dis.get_instructions(calling_frame.f_code))
87
+ unpack = instructions[first]
88
+
89
+ if unpack.opname == "STORE_FAST" or unpack.opname == "STORE_NAME":
90
+ # just a single dim, not a list
91
+ name = unpack.argval
92
+ ctor = Dim if lists == 0 else DimList
93
+ _cache[key] = lambda: ctor(name=name)
94
+ else:
95
+ assert unpack.opname == "UNPACK_SEQUENCE"
96
+ ndims = unpack.argval
97
+ names = tuple(
98
+ extract_name(instructions[first + 1 + i]) for i in range(ndims)
99
+ )
100
+ first_list = len(names) - lists
101
+ _cache[key] = lambda: tuple(
102
+ Dim(n) if i < first_list else DimList(name=n)
103
+ for i, n in enumerate(names)
104
+ )
105
+ return _cache[key]()
106
+
107
+
108
+ def _dim_set(positional, arg):
109
+ def convert(a):
110
+ if isinstance(a, Dim):
111
+ return a
112
+ else:
113
+ assert isinstance(a, int)
114
+ return positional[a]
115
+
116
+ if arg is None:
117
+ return positional
118
+ elif not isinstance(arg, (Dim, int)):
119
+ return tuple(convert(a) for a in arg)
120
+ else:
121
+ return (convert(arg),)
tuning-competition-baseline/.venv/lib/python3.11/site-packages/functorch/dim/op_properties.py ADDED
@@ -0,0 +1,311 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ # All rights reserved.
3
+ #
4
+ # This source code is licensed under the BSD-style license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+ import torch
7
+
8
+ # pointwise operators can go through a faster pathway
9
+
10
+ tensor_magic_methods = ["add", ""]
11
+ pointwise_magic_methods_with_reverse = (
12
+ "add",
13
+ "sub",
14
+ "mul",
15
+ "floordiv",
16
+ "div",
17
+ "truediv",
18
+ "mod",
19
+ "pow",
20
+ "lshift",
21
+ "rshift",
22
+ "and",
23
+ "or",
24
+ "xor",
25
+ )
26
+ pointwise_magic_methods = (
27
+ *(x for m in pointwise_magic_methods_with_reverse for x in (m, "r" + m)),
28
+ "eq",
29
+ "gt",
30
+ "le",
31
+ "lt",
32
+ "ge",
33
+ "gt",
34
+ "ne",
35
+ "neg",
36
+ "pos",
37
+ "abs",
38
+ "invert",
39
+ "iadd",
40
+ "isub",
41
+ "imul",
42
+ "ifloordiv",
43
+ "idiv",
44
+ "itruediv",
45
+ "imod",
46
+ "ipow",
47
+ "ilshift",
48
+ "irshift",
49
+ "iand",
50
+ "ior",
51
+ "ixor",
52
+ "int",
53
+ "long",
54
+ "float",
55
+ "complex",
56
+ )
57
+
58
+ pointwise_methods = (*(f"__{m}__" for m in pointwise_magic_methods),)
59
+
60
+ pointwise = (
61
+ *(getattr(torch.Tensor, m) for m in pointwise_methods),
62
+ torch.nn.functional.dropout,
63
+ torch.where,
64
+ torch.Tensor.abs,
65
+ torch.abs,
66
+ torch.Tensor.acos,
67
+ torch.acos,
68
+ torch.Tensor.acosh,
69
+ torch.acosh,
70
+ torch.Tensor.add,
71
+ torch.add,
72
+ torch.Tensor.addcdiv,
73
+ torch.addcdiv,
74
+ torch.Tensor.addcmul,
75
+ torch.addcmul,
76
+ torch.Tensor.addr,
77
+ torch.addr,
78
+ torch.Tensor.angle,
79
+ torch.angle,
80
+ torch.Tensor.asin,
81
+ torch.asin,
82
+ torch.Tensor.asinh,
83
+ torch.asinh,
84
+ torch.Tensor.atan,
85
+ torch.atan,
86
+ torch.Tensor.atan2,
87
+ torch.atan2,
88
+ torch.Tensor.atanh,
89
+ torch.atanh,
90
+ torch.Tensor.bitwise_and,
91
+ torch.bitwise_and,
92
+ torch.Tensor.bitwise_left_shift,
93
+ torch.bitwise_left_shift,
94
+ torch.Tensor.bitwise_not,
95
+ torch.bitwise_not,
96
+ torch.Tensor.bitwise_or,
97
+ torch.bitwise_or,
98
+ torch.Tensor.bitwise_right_shift,
99
+ torch.bitwise_right_shift,
100
+ torch.Tensor.bitwise_xor,
101
+ torch.bitwise_xor,
102
+ torch.Tensor.ceil,
103
+ torch.ceil,
104
+ torch.celu,
105
+ torch.nn.functional.celu,
106
+ torch.Tensor.clamp,
107
+ torch.clamp,
108
+ torch.Tensor.clamp_max,
109
+ torch.clamp_max,
110
+ torch.Tensor.clamp_min,
111
+ torch.clamp_min,
112
+ torch.Tensor.copysign,
113
+ torch.copysign,
114
+ torch.Tensor.cos,
115
+ torch.cos,
116
+ torch.Tensor.cosh,
117
+ torch.cosh,
118
+ torch.Tensor.deg2rad,
119
+ torch.deg2rad,
120
+ torch.Tensor.digamma,
121
+ torch.digamma,
122
+ torch.Tensor.div,
123
+ torch.div,
124
+ torch.dropout,
125
+ torch.nn.functional.dropout,
126
+ torch.nn.functional.elu,
127
+ torch.Tensor.eq,
128
+ torch.eq,
129
+ torch.Tensor.erf,
130
+ torch.erf,
131
+ torch.Tensor.erfc,
132
+ torch.erfc,
133
+ torch.Tensor.erfinv,
134
+ torch.erfinv,
135
+ torch.Tensor.exp,
136
+ torch.exp,
137
+ torch.Tensor.exp2,
138
+ torch.exp2,
139
+ torch.Tensor.expm1,
140
+ torch.expm1,
141
+ torch.feature_dropout,
142
+ torch.Tensor.float_power,
143
+ torch.float_power,
144
+ torch.Tensor.floor,
145
+ torch.floor,
146
+ torch.Tensor.floor_divide,
147
+ torch.floor_divide,
148
+ torch.Tensor.fmod,
149
+ torch.fmod,
150
+ torch.Tensor.frac,
151
+ torch.frac,
152
+ torch.Tensor.frexp,
153
+ torch.frexp,
154
+ torch.Tensor.gcd,
155
+ torch.gcd,
156
+ torch.Tensor.ge,
157
+ torch.ge,
158
+ torch.nn.functional.gelu,
159
+ torch.nn.functional.glu,
160
+ torch.Tensor.gt,
161
+ torch.gt,
162
+ torch.Tensor.hardshrink,
163
+ torch.hardshrink,
164
+ torch.nn.functional.hardshrink,
165
+ torch.nn.functional.hardsigmoid,
166
+ torch.nn.functional.hardswish,
167
+ torch.nn.functional.hardtanh,
168
+ torch.Tensor.heaviside,
169
+ torch.heaviside,
170
+ torch.Tensor.hypot,
171
+ torch.hypot,
172
+ torch.Tensor.i0,
173
+ torch.i0,
174
+ torch.Tensor.igamma,
175
+ torch.igamma,
176
+ torch.Tensor.igammac,
177
+ torch.igammac,
178
+ torch.Tensor.isclose,
179
+ torch.isclose,
180
+ torch.Tensor.isfinite,
181
+ torch.isfinite,
182
+ torch.Tensor.isinf,
183
+ torch.isinf,
184
+ torch.Tensor.isnan,
185
+ torch.isnan,
186
+ torch.Tensor.isneginf,
187
+ torch.isneginf,
188
+ torch.Tensor.isposinf,
189
+ torch.isposinf,
190
+ torch.Tensor.isreal,
191
+ torch.isreal,
192
+ torch.Tensor.kron,
193
+ torch.kron,
194
+ torch.Tensor.lcm,
195
+ torch.lcm,
196
+ torch.Tensor.ldexp,
197
+ torch.ldexp,
198
+ torch.Tensor.le,
199
+ torch.le,
200
+ torch.nn.functional.leaky_relu,
201
+ torch.Tensor.lerp,
202
+ torch.lerp,
203
+ torch.Tensor.lgamma,
204
+ torch.lgamma,
205
+ torch.Tensor.log,
206
+ torch.log,
207
+ torch.Tensor.log10,
208
+ torch.log10,
209
+ torch.Tensor.log1p,
210
+ torch.log1p,
211
+ torch.Tensor.log2,
212
+ torch.log2,
213
+ torch.nn.functional.logsigmoid,
214
+ torch.Tensor.logical_and,
215
+ torch.logical_and,
216
+ torch.Tensor.logical_not,
217
+ torch.logical_not,
218
+ torch.Tensor.logical_or,
219
+ torch.logical_or,
220
+ torch.Tensor.logical_xor,
221
+ torch.logical_xor,
222
+ torch.Tensor.logit,
223
+ torch.logit,
224
+ torch.Tensor.lt,
225
+ torch.lt,
226
+ torch.Tensor.maximum,
227
+ torch.maximum,
228
+ torch.Tensor.minimum,
229
+ torch.minimum,
230
+ torch.nn.functional.mish,
231
+ torch.Tensor.mvlgamma,
232
+ torch.mvlgamma,
233
+ torch.Tensor.nan_to_num,
234
+ torch.nan_to_num,
235
+ torch.Tensor.ne,
236
+ torch.ne,
237
+ torch.Tensor.neg,
238
+ torch.neg,
239
+ torch.Tensor.nextafter,
240
+ torch.nextafter,
241
+ torch.Tensor.outer,
242
+ torch.outer,
243
+ torch.polar,
244
+ torch.Tensor.polygamma,
245
+ torch.polygamma,
246
+ torch.Tensor.positive,
247
+ torch.positive,
248
+ torch.Tensor.pow,
249
+ torch.pow,
250
+ torch.Tensor.prelu,
251
+ torch.prelu,
252
+ torch.nn.functional.prelu,
253
+ torch.Tensor.rad2deg,
254
+ torch.rad2deg,
255
+ torch.Tensor.reciprocal,
256
+ torch.reciprocal,
257
+ torch.Tensor.relu,
258
+ torch.relu,
259
+ torch.nn.functional.relu,
260
+ torch.nn.functional.relu6,
261
+ torch.Tensor.remainder,
262
+ torch.remainder,
263
+ torch.Tensor.round,
264
+ torch.round,
265
+ torch.rrelu,
266
+ torch.nn.functional.rrelu,
267
+ torch.Tensor.rsqrt,
268
+ torch.rsqrt,
269
+ torch.rsub,
270
+ torch.selu,
271
+ torch.nn.functional.selu,
272
+ torch.Tensor.sgn,
273
+ torch.sgn,
274
+ torch.Tensor.sigmoid,
275
+ torch.sigmoid,
276
+ torch.nn.functional.sigmoid,
277
+ torch.Tensor.sign,
278
+ torch.sign,
279
+ torch.Tensor.signbit,
280
+ torch.signbit,
281
+ torch.nn.functional.silu,
282
+ torch.Tensor.sin,
283
+ torch.sin,
284
+ torch.Tensor.sinc,
285
+ torch.sinc,
286
+ torch.Tensor.sinh,
287
+ torch.sinh,
288
+ torch.nn.functional.softplus,
289
+ torch.nn.functional.softshrink,
290
+ torch.Tensor.sqrt,
291
+ torch.sqrt,
292
+ torch.Tensor.square,
293
+ torch.square,
294
+ torch.Tensor.sub,
295
+ torch.sub,
296
+ torch.Tensor.tan,
297
+ torch.tan,
298
+ torch.Tensor.tanh,
299
+ torch.tanh,
300
+ torch.nn.functional.tanh,
301
+ torch.threshold,
302
+ torch.nn.functional.threshold,
303
+ torch.trapz,
304
+ torch.Tensor.true_divide,
305
+ torch.true_divide,
306
+ torch.Tensor.trunc,
307
+ torch.trunc,
308
+ torch.Tensor.xlogy,
309
+ torch.xlogy,
310
+ torch.rand_like,
311
+ )
tuning-competition-baseline/.venv/lib/python3.11/site-packages/functorch/dim/reference.py ADDED
@@ -0,0 +1,645 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ # All rights reserved.
3
+ #
4
+ # This source code is licensed under the BSD-style license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ # reference python implementations for C ops
8
+ import torch
9
+
10
+ from functorch._C import dim as _C
11
+ from . import op_properties
12
+ from .batch_tensor import _enable_layers
13
+ from .tree_map import tree_flatten, tree_map
14
+
15
+ DimList = _C.DimList
16
+ import operator
17
+ from functools import reduce
18
+
19
+
20
+ # use dict to avoid writing C++ bindings for set
21
+ pointwise = set(op_properties.pointwise)
22
+
23
+
24
+ def prod(x):
25
+ return reduce(operator.mul, x, 1)
26
+
27
+
28
+ def _wrap_dim(d, N, keepdim):
29
+ from . import Dim
30
+
31
+ if isinstance(d, Dim):
32
+ assert not keepdim, "cannot preserve first-class dimensions with keepdim=True"
33
+ return d
34
+ elif d >= 0:
35
+ return d - N
36
+ else:
37
+ return d
38
+
39
+
40
+ def _dims(d, N, keepdim, single_dim):
41
+ from . import Dim
42
+
43
+ if isinstance(d, (Dim, int)):
44
+ return ltuple((_wrap_dim(d, N, keepdim),))
45
+ assert not single_dim, f"expected a single dimension or int but found: {d}"
46
+ return ltuple(_wrap_dim(x, N, keepdim) for x in d)
47
+
48
+
49
+ def _bind_dims_to_size(lhs_size, rhs, lhs_debug):
50
+ from . import DimensionMismatchError
51
+
52
+ not_bound = tuple((i, r) for i, r in enumerate(rhs) if not r.is_bound)
53
+ if len(not_bound) == 1:
54
+ idx, d = not_bound[0]
55
+ rhs_so_far = prod(r.size for r in rhs if r.is_bound)
56
+ if lhs_size % rhs_so_far != 0:
57
+ rhs_s = tuple("?" if not r.is_bound else str(r.size) for r in rhs)
58
+ raise DimensionMismatchError(
59
+ f"inferred dimension does not evenly fit into larger dimension: {lhs_size} vs {rhs_s}"
60
+ )
61
+ new_size = lhs_size // rhs_so_far
62
+ d.size = new_size
63
+ elif len(not_bound) > 1:
64
+ rhs_s = tuple("?" if not r.is_bound else str(r.size) for r in rhs)
65
+ raise DimensionMismatchError(
66
+ f"cannot infer the size of two dimensions at once: {rhs} with sizes {rhs_s}"
67
+ )
68
+ else:
69
+ rhs_size = prod(r.size for r in rhs)
70
+ if lhs_size != rhs_size:
71
+ raise DimensionMismatchError(
72
+ f"Dimension sizes to do not match ({lhs_size} != {rhs_size}) when matching {lhs_debug} to {rhs}"
73
+ )
74
+
75
+
76
+ def _tensor_levels(inp):
77
+ from . import _Tensor
78
+
79
+ if isinstance(inp, _Tensor):
80
+ return inp._tensor, llist(inp._levels), inp._has_device
81
+ else:
82
+ return inp, llist(range(-inp.ndim, 0)), True
83
+
84
+
85
+ def _match_levels(v, from_levels, to_levels):
86
+ view = []
87
+ permute = []
88
+ requires_view = False
89
+ size = v.size()
90
+ for t in to_levels:
91
+ try:
92
+ idx = from_levels.index(t)
93
+ permute.append(idx)
94
+ view.append(size[idx])
95
+ except ValueError:
96
+ view.append(1)
97
+ requires_view = True
98
+ if permute != list(range(len(permute))):
99
+ v = v.permute(*permute)
100
+ if requires_view:
101
+ v = v.view(*view)
102
+ return v
103
+
104
+
105
+ # make a single dimension positional but do not permute it,
106
+ # used to do multi-tensor operators where the dim being acted on
107
+ # should not physically move if possible
108
+ def _positional_no_permute(self, dim, expand_dim=False):
109
+ from . import Tensor
110
+
111
+ ptensor, levels = self._tensor, llist(self._levels)
112
+ try:
113
+ idx = levels.index(dim)
114
+ except ValueError:
115
+ if not expand_dim:
116
+ raise
117
+ idx = 0
118
+ ptensor = ptensor.expand(dim.size, *ptensor.size())
119
+ levels.insert(0, 0)
120
+ idx_batched = 0
121
+ for i in range(idx):
122
+ if isinstance(levels[i], int):
123
+ levels[i] -= 1
124
+ idx_batched += 1
125
+ levels[idx] = -idx_batched - 1
126
+ return Tensor.from_positional(ptensor, levels, self._has_device), idx_batched
127
+
128
+
129
+ def seq(a, b):
130
+ from . import Dim
131
+
132
+ if isinstance(a, Dim) != isinstance(b, Dim):
133
+ return False
134
+ if isinstance(a, Dim):
135
+ return a is b
136
+ else:
137
+ return a == b
138
+
139
+
140
+ class isin:
141
+ def __contains__(self, item):
142
+ for x in self:
143
+ if seq(item, x):
144
+ return True
145
+ return False
146
+
147
+ def index(self, item):
148
+ for i, x in enumerate(self):
149
+ if seq(item, x):
150
+ return i
151
+ raise ValueError
152
+
153
+
154
+ class llist(isin, list):
155
+ pass
156
+
157
+
158
+ class ltuple(isin, tuple):
159
+ pass
160
+
161
+
162
+ empty_dict = {}
163
+
164
+
165
+ @classmethod
166
+ def __torch_function__(self, orig, cls, args, kwargs=empty_dict):
167
+ from . import _Tensor, Tensor, TensorLike
168
+ from .delayed_mul_tensor import DelayedMulTensor
169
+
170
+ if orig is torch.Tensor.__mul__:
171
+ lhs, rhs = args
172
+ if (
173
+ isinstance(lhs, _Tensor)
174
+ and isinstance(rhs, _Tensor)
175
+ and lhs.ndim == 0
176
+ and rhs.ndim == 0
177
+ ):
178
+ return DelayedMulTensor(lhs, rhs)
179
+ all_dims = llist()
180
+ flat_args, unflatten = tree_flatten((args, kwargs))
181
+ device_holding_tensor = None
182
+ for f in flat_args:
183
+ if isinstance(f, _Tensor):
184
+ if f._has_device:
185
+ device_holding_tensor = f._batchtensor
186
+ for d in f.dims:
187
+ if d not in all_dims:
188
+ all_dims.append(d)
189
+
190
+ def unwrap(t):
191
+ if isinstance(t, _Tensor):
192
+ r = t._batchtensor
193
+ if device_holding_tensor is not None and not t._has_device:
194
+ r = r.to(device=device_holding_tensor.device)
195
+ return r
196
+ return t
197
+
198
+ if orig in pointwise:
199
+ result_levels = llist()
200
+ arg_levels = llist()
201
+ to_expand = []
202
+ for i, f in enumerate(flat_args):
203
+ if isinstance(f, TensorLike):
204
+ ptensor, levels, _ = _tensor_levels(f)
205
+ if (
206
+ isinstance(f, _Tensor)
207
+ and not f._has_device
208
+ and device_holding_tensor is not None
209
+ ):
210
+ ptensor = ptensor.to(device=device_holding_tensor.device)
211
+ flat_args[i] = ptensor
212
+ for l in levels:
213
+ if l not in result_levels:
214
+ result_levels.append(l)
215
+ to_expand.append((i, levels))
216
+
217
+ for i, levels in to_expand:
218
+ flat_args[i] = _match_levels(flat_args[i], levels, result_levels)
219
+ args, kwargs = unflatten(flat_args)
220
+ result = orig(*args, **kwargs)
221
+
222
+ def wrap(t):
223
+ if isinstance(t, TensorLike):
224
+ return Tensor.from_positional(
225
+ t, result_levels, device_holding_tensor is not None
226
+ )
227
+ return t
228
+
229
+ return tree_map(wrap, result)
230
+ else:
231
+
232
+ def wrap(t):
233
+ if isinstance(t, TensorLike):
234
+ return Tensor.from_batched(t, device_holding_tensor is not None)
235
+ return t
236
+
237
+ with _enable_layers(all_dims):
238
+ print(f"batch_tensor for {orig}")
239
+ args, kwargs = unflatten(unwrap(f) for f in flat_args)
240
+ result = orig(*args, **kwargs)
241
+ # print("END", orig)
242
+ return tree_map(wrap, result)
243
+
244
+
245
+ def positional(self, *dims):
246
+ from . import Dim, DimensionBindError, Tensor
247
+
248
+ ptensor, levels = self._tensor, llist(self._levels)
249
+ flat_dims = llist()
250
+ view = []
251
+ needs_view = False
252
+ ndim = self.ndim
253
+ for d in dims:
254
+ if isinstance(d, DimList):
255
+ flat_dims.extend(d)
256
+ view.extend(e.size for e in d)
257
+ elif isinstance(d, Dim):
258
+ flat_dims.append(d)
259
+ view.append(d.size)
260
+ elif isinstance(d, int):
261
+ d = _wrap_dim(d, ndim, False)
262
+ flat_dims.append(d)
263
+ view.append(ptensor.size(d))
264
+ else:
265
+ flat_dims.extend(d)
266
+ view.append(prod(e.size for e in d))
267
+ needs_view = True
268
+
269
+ permute = list(range(len(levels)))
270
+ nflat = len(flat_dims)
271
+ for i, d in enumerate(flat_dims):
272
+ try:
273
+ idx = levels.index(d)
274
+ except ValueError as e:
275
+ raise DimensionBindError(
276
+ f"tensor of dimensions {self.dims} does not contain dim {d}"
277
+ ) from e
278
+ p = permute[idx]
279
+ del levels[idx]
280
+ del permute[idx]
281
+ levels.insert(i, 0)
282
+ permute.insert(i, p)
283
+ ptensor = ptensor.permute(*permute)
284
+ seen = 0
285
+ for i in range(len(levels) - 1, -1, -1):
286
+ if isinstance(levels[i], int):
287
+ seen += 1
288
+ levels[i] = -seen
289
+ result = Tensor.from_positional(ptensor, levels, self._has_device)
290
+ if needs_view:
291
+ result = result.reshape(*view, *result.size()[len(flat_dims) :])
292
+ return result
293
+
294
+
295
+ def _contains_dim(input):
296
+ from . import Dim
297
+
298
+ for i in input:
299
+ if isinstance(i, Dim):
300
+ return True
301
+
302
+
303
+ def expand(self, *sizes):
304
+ if not _contains_dim(sizes):
305
+ return self.__torch_function__(torch.Tensor.expand, None, (self, *sizes))
306
+ dims = sizes
307
+ sizes = [d.size for d in dims] + [-1] * self.ndim
308
+ self = self.expand(*sizes)
309
+ return self[dims]
310
+
311
+
312
+ _not_present = object()
313
+
314
+
315
+ def _getarg(name, offset, args, kwargs, default):
316
+ if len(args) > offset:
317
+ return args[offset]
318
+ return kwargs.get(name, default)
319
+
320
+
321
+ def _patcharg(name, offset, args, kwargs, value):
322
+ if len(args) > offset:
323
+ args[offset] = value
324
+ else:
325
+ kwargs[name] = value
326
+
327
+
328
+ def _wrap(
329
+ orig, dim_offset=0, keepdim_offset=1, dim_name="dim", single_dim=False, reduce=True
330
+ ):
331
+ from . import Dim, Tensor, TensorLike
332
+
333
+ def fn(self, *args, **kwargs):
334
+ dim = _getarg(dim_name, dim_offset, args, kwargs, _not_present)
335
+ if dim is _not_present or (single_dim and not isinstance(dim, Dim)):
336
+ with _enable_layers(self.dims):
337
+ print(f"dim fallback batch_tensor for {orig}")
338
+ return Tensor.from_batched(
339
+ orig(self._batchtensor, *args, **kwargs), self._has_device
340
+ )
341
+ keepdim = (
342
+ _getarg("keepdim", keepdim_offset, args, kwargs, False) if reduce else False
343
+ )
344
+ t, levels = self._tensor, llist(self._levels)
345
+ dims = _dims(dim, self._batchtensor.ndim, keepdim, single_dim)
346
+ dim_indices = tuple(levels.index(d) for d in dims)
347
+ if reduce and not keepdim:
348
+ new_levels = [l for i, l in enumerate(levels) if i not in dim_indices]
349
+ else:
350
+ new_levels = levels
351
+
352
+ if len(dim_indices) == 1:
353
+ dim_indices = dim_indices[
354
+ 0
355
+ ] # so that dims that really only take a single argument work...
356
+ args = list(args)
357
+ _patcharg(dim_name, dim_offset, args, kwargs, dim_indices)
358
+
359
+ def wrap(t):
360
+ if isinstance(t, TensorLike):
361
+ return Tensor.from_positional(t, new_levels, self._has_device)
362
+ return t
363
+
364
+ with _enable_layers(new_levels):
365
+ print(f"dim used batch_tensor for {orig}")
366
+ r = orig(t, *args, **kwargs)
367
+ return tree_map(wrap, r)
368
+
369
+ return fn
370
+
371
+
372
+ def _def(name, *args, **kwargs):
373
+ from . import _Tensor
374
+
375
+ orig = getattr(torch.Tensor, name)
376
+ setattr(_Tensor, name, _wrap(orig, *args, **kwargs))
377
+
378
+
379
+ no_slice = slice(None)
380
+
381
+ _orig_getitem = torch.Tensor.__getitem__
382
+
383
+
384
+ class dim_tracker:
385
+ def __init__(self):
386
+ self.dims = llist()
387
+ self.count = []
388
+
389
+ def record(self, d):
390
+ if d not in self.dims:
391
+ self.dims.append(d)
392
+ self.count.append(1)
393
+
394
+ def __getitem__(self, d):
395
+ return self.count[self.dims.index(d)]
396
+
397
+
398
+ def t__getitem__(self, input):
399
+ from . import _Tensor, Dim, DimensionBindError, DimList, Tensor, TensorLike
400
+
401
+ # * bail to original example if we have a single non-Dim tensor, or a non-tensor
402
+ # * locate ... or an unbound tensor list, and determine its size, bind dim list
403
+ # (remember that None does not count to the total dim count)
404
+ # * bind simple dims and dim-packs to their sizes, count the number of uses of each dim,
405
+ # produce the re-view if needed
406
+ # * for each single-use dim index, replace with no_slice and mark that it will be added
407
+ # (keep track of whether we have to call super)
408
+ # * call super if needed
409
+ # * if we have dims to bind, bind them (it will help if we eliminated ... and None before)
410
+
411
+ # this handles bool indexing handling, as well as some other simple cases.
412
+
413
+ is_simple = (
414
+ not isinstance(input, Dim)
415
+ and not isinstance(input, (tuple, list))
416
+ and
417
+ # WAR for functorch bug where zero time tensors in getitem are not handled correctly.
418
+ not (isinstance(input, TensorLike) and input.ndim == 0)
419
+ )
420
+
421
+ if is_simple:
422
+ if isinstance(self, _Tensor):
423
+ return _Tensor.__torch_function__(_orig_getitem, None, (self, input))
424
+ else:
425
+ return _orig_getitem(self, input)
426
+
427
+ # can further optimize this case
428
+ if not isinstance(input, tuple):
429
+ input = [input]
430
+ else:
431
+ input = list(input)
432
+
433
+ dims_indexed = 0
434
+ expanding_object = None
435
+ dimlists = []
436
+ for i, s in enumerate(input):
437
+ if s is ... or isinstance(s, DimList) and not s.is_bound:
438
+ if expanding_object is not None:
439
+ msg = (
440
+ "at most one ... or unbound dimension list can exist in indexing list but"
441
+ f" found 2 at offsets {i} and {expanding_object}"
442
+ )
443
+ raise DimensionBindError(msg)
444
+ expanding_object = i
445
+
446
+ if isinstance(s, DimList):
447
+ dims_indexed += len(s) if s.is_bound else 0
448
+ dimlists.append(i)
449
+ elif s is not None and s is not ...:
450
+ dims_indexed += 1
451
+
452
+ ndim = self.ndim
453
+ if dims_indexed > ndim:
454
+ raise IndexError(
455
+ f"at least {dims_indexed} indices were supplied but the tensor only has {ndim} dimensions."
456
+ )
457
+ if expanding_object is not None:
458
+ expanding_ndims = ndim - dims_indexed
459
+ obj = input[expanding_object]
460
+ if obj is ...:
461
+ input[expanding_object : expanding_object + 1] = [
462
+ no_slice
463
+ ] * expanding_ndims
464
+ else:
465
+ obj.bind_len(expanding_ndims)
466
+ # flatten the dimslists into the indexing
467
+ for i in reversed(dimlists):
468
+ input[i : i + 1] = input[i]
469
+ dims_indexed = 0
470
+ requires_view = False
471
+ size = self.size()
472
+ view_sizes = []
473
+ dims_seen = dim_tracker()
474
+
475
+ def add_dims(t):
476
+ if not isinstance(t, _Tensor):
477
+ return
478
+ for d in t.dims:
479
+ dims_seen.record(d)
480
+
481
+ add_dims(self)
482
+ dim_packs = []
483
+ for i, idx in enumerate(input):
484
+ if idx is None:
485
+ input[i] = no_slice
486
+ view_sizes.append(1)
487
+ requires_view = True
488
+ else:
489
+ sz = size[dims_indexed]
490
+ if isinstance(idx, Dim):
491
+ idx.size = sz
492
+ dims_seen.record(idx)
493
+ view_sizes.append(sz)
494
+ elif isinstance(idx, (tuple, list)) and idx and isinstance(idx[0], Dim):
495
+ for d in idx:
496
+ dims_seen.record(idx)
497
+ _bind_dims_to_size(sz, idx, f"offset {i}")
498
+ view_sizes.extend(d.size for d in idx)
499
+ requires_view = True
500
+ dim_packs.append(i)
501
+ else:
502
+ add_dims(idx)
503
+ view_sizes.append(sz)
504
+ dims_indexed += 1
505
+ if requires_view:
506
+ self = self.view(*view_sizes)
507
+ for i in reversed(dim_packs):
508
+ input[i : i + 1] = input[i]
509
+
510
+ # currenty:
511
+ # input is flat, containing either Dim, or Tensor, or something valid for standard indexing
512
+ # self may have first-class dims as well.
513
+
514
+ # to index:
515
+ # drop the first class dims from self, they just become direct indices of their positions
516
+
517
+ # figure out the dimensions of the indexing tensors: union of all the dims in the tensors in the index.
518
+ # these dimensions will appear and need to be bound at the first place tensor occures
519
+
520
+ if isinstance(self, _Tensor):
521
+ ptensor_self, levels = self._tensor, list(self._levels)
522
+ # indices to ptensor rather than self which has first-class dimensions
523
+ input_it = iter(input)
524
+ flat_inputs = [next(input_it) if isinstance(l, int) else l for l in levels]
525
+ has_device = self._has_device
526
+ to_pad = 0
527
+ else:
528
+ ptensor_self, flat_inputs = self, input
529
+ to_pad = ptensor_self.ndim - len(flat_inputs)
530
+ has_device = True
531
+
532
+ result_levels = []
533
+ index_levels = []
534
+ tensor_insert_point = None
535
+ to_expand = {}
536
+ requires_getindex = False
537
+ for i, inp in enumerate(flat_inputs):
538
+ if isinstance(inp, Dim) and dims_seen[inp] == 1:
539
+ flat_inputs[i] = no_slice
540
+ result_levels.append(inp)
541
+ elif isinstance(inp, TensorLike):
542
+ requires_getindex = True
543
+ if tensor_insert_point is None:
544
+ tensor_insert_point = len(result_levels)
545
+ ptensor, levels, _ = _tensor_levels(inp)
546
+ to_expand[i] = levels
547
+ flat_inputs[i] = ptensor
548
+ for l in levels:
549
+ if l not in index_levels:
550
+ index_levels.append(l)
551
+ else:
552
+ requires_getindex = True
553
+ result_levels.append(0)
554
+
555
+ if tensor_insert_point is not None:
556
+ result_levels[tensor_insert_point:tensor_insert_point] = index_levels
557
+
558
+ for i, levels in to_expand.items():
559
+ flat_inputs[i] = _match_levels(flat_inputs[i], levels, index_levels)
560
+
561
+ if requires_getindex:
562
+ result = _orig_getitem(ptensor_self, flat_inputs)
563
+ else:
564
+ result = ptensor_self
565
+
566
+ next_positional = -1
567
+ if to_pad > 0:
568
+ result_levels.extend([0] * to_pad)
569
+ for i, r in enumerate(reversed(result_levels)):
570
+ if isinstance(r, int):
571
+ result_levels[-1 - i] = next_positional
572
+ next_positional -= 1
573
+
574
+ return Tensor.from_positional(result, result_levels, has_device)
575
+
576
+
577
+ # XXX - dim is optional and can be the outer-most dimension...
578
+ def stack(tensors, new_dim, dim=0, out=None):
579
+ if isinstance(dim, int):
580
+ return torch.stack(tensors, dim, out).index(dim, new_dim)
581
+ index = None
582
+ if out is not None:
583
+ out, index = _positional_no_permute(out, dim, expand_dim=True)
584
+ ptensors = []
585
+ for t in tensors:
586
+ pt, pi = _positional_no_permute(t, dim, expand_dim=True)
587
+ if index is not None and pi != index:
588
+ pt = pt.move_dim(pi, index)
589
+ else:
590
+ index = pi
591
+ ptensors.append(pt)
592
+ pr = torch.stack(ptensors, index, out=out)
593
+ return pr.index((index, index + 1), (new_dim, dim))
594
+
595
+
596
+ _orig_split = torch.Tensor.split
597
+
598
+
599
+ def split(self, split_size_or_sections, dim=0):
600
+ from . import _Tensor, Dim
601
+
602
+ if isinstance(split_size_or_sections, int) or any(
603
+ isinstance(t, int) for t in split_size_or_sections
604
+ ):
605
+ if isinstance(dim, Dim):
606
+ raise ValueError(
607
+ "when dim is specified as a Dim object, split sizes must also be dimensions."
608
+ )
609
+ return _orig_split(self, split_size_or_sections, dim=dim)
610
+
611
+ if isinstance(dim, Dim):
612
+ assert isinstance(self, _Tensor), f"Tensor does not have dimension {dim}"
613
+ self, dim = _positional_no_permute(self, dim)
614
+
615
+ size = self.size(dim)
616
+ total_bound_size = 0
617
+ unbound = []
618
+ sizes = []
619
+ for i, d in enumerate(split_size_or_sections):
620
+ if d.is_bound:
621
+ sizes.append(d.size)
622
+ total_bound_size += d.size
623
+ else:
624
+ sizes.append(0)
625
+ unbound.append(i)
626
+
627
+ if unbound:
628
+ assert (
629
+ total_bound_size <= size
630
+ ), f"result dimensions are larger than original: {total_bound_size} vs {size} ({split_size_or_sections})"
631
+ remaining_size = size - total_bound_size
632
+ chunk_size = -(-remaining_size // len(unbound))
633
+ for u in unbound:
634
+ sz = min(chunk_size, remaining_size)
635
+ split_size_or_sections[u].size = sz
636
+ sizes[u] = sz
637
+ remaining_size -= sz
638
+ else:
639
+ assert (
640
+ total_bound_size == size
641
+ ), f"result dimensions do not match original: {total_bound_size} vs {size} ({split_size_or_sections})"
642
+ return tuple(
643
+ t.index(dim, d)
644
+ for d, t in zip(split_size_or_sections, _orig_split(self, sizes, dim=dim))
645
+ )
tuning-competition-baseline/.venv/lib/python3.11/site-packages/functorch/dim/tree_map.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ # All rights reserved.
3
+ #
4
+ # This source code is licensed under the BSD-style license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ from functorch._C import dim
8
+
9
+ tree_flatten = dim.tree_flatten
10
+
11
+
12
+ def tree_map(fn, tree):
13
+ vs, unflatten = tree_flatten(tree)
14
+ return unflatten(fn(v) for v in vs)
tuning-competition-baseline/.venv/lib/python3.11/site-packages/functorch/experimental/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (591 Bytes). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/functorch/experimental/ops.py ADDED
@@ -0,0 +1 @@
 
 
1
+ from torch._ops import HigherOrderOperator # noqa: F401
tuning-competition-baseline/.venv/lib/python3.11/site-packages/jinja2/__init__.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Jinja is a template engine written in pure Python. It provides a
2
+ non-XML syntax that supports inline expressions and an optional
3
+ sandboxed environment.
4
+ """
5
+ from .bccache import BytecodeCache as BytecodeCache
6
+ from .bccache import FileSystemBytecodeCache as FileSystemBytecodeCache
7
+ from .bccache import MemcachedBytecodeCache as MemcachedBytecodeCache
8
+ from .environment import Environment as Environment
9
+ from .environment import Template as Template
10
+ from .exceptions import TemplateAssertionError as TemplateAssertionError
11
+ from .exceptions import TemplateError as TemplateError
12
+ from .exceptions import TemplateNotFound as TemplateNotFound
13
+ from .exceptions import TemplateRuntimeError as TemplateRuntimeError
14
+ from .exceptions import TemplatesNotFound as TemplatesNotFound
15
+ from .exceptions import TemplateSyntaxError as TemplateSyntaxError
16
+ from .exceptions import UndefinedError as UndefinedError
17
+ from .loaders import BaseLoader as BaseLoader
18
+ from .loaders import ChoiceLoader as ChoiceLoader
19
+ from .loaders import DictLoader as DictLoader
20
+ from .loaders import FileSystemLoader as FileSystemLoader
21
+ from .loaders import FunctionLoader as FunctionLoader
22
+ from .loaders import ModuleLoader as ModuleLoader
23
+ from .loaders import PackageLoader as PackageLoader
24
+ from .loaders import PrefixLoader as PrefixLoader
25
+ from .runtime import ChainableUndefined as ChainableUndefined
26
+ from .runtime import DebugUndefined as DebugUndefined
27
+ from .runtime import make_logging_undefined as make_logging_undefined
28
+ from .runtime import StrictUndefined as StrictUndefined
29
+ from .runtime import Undefined as Undefined
30
+ from .utils import clear_caches as clear_caches
31
+ from .utils import is_undefined as is_undefined
32
+ from .utils import pass_context as pass_context
33
+ from .utils import pass_environment as pass_environment
34
+ from .utils import pass_eval_context as pass_eval_context
35
+ from .utils import select_autoescape as select_autoescape
36
+
37
+ __version__ = "3.1.3"
tuning-competition-baseline/.venv/lib/python3.11/site-packages/jinja2/__pycache__/_identifier.cpython-311.pyc ADDED
Binary file (2.16 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/jinja2/__pycache__/loaders.cpython-311.pyc ADDED
Binary file (33.1 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/jinja2/__pycache__/runtime.cpython-311.pyc ADDED
Binary file (50.7 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/jinja2/environment.py ADDED
@@ -0,0 +1,1667 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Classes for managing templates and their runtime and compile time
2
+ options.
3
+ """
4
+ import os
5
+ import typing
6
+ import typing as t
7
+ import weakref
8
+ from collections import ChainMap
9
+ from functools import lru_cache
10
+ from functools import partial
11
+ from functools import reduce
12
+ from types import CodeType
13
+
14
+ from markupsafe import Markup
15
+
16
+ from . import nodes
17
+ from .compiler import CodeGenerator
18
+ from .compiler import generate
19
+ from .defaults import BLOCK_END_STRING
20
+ from .defaults import BLOCK_START_STRING
21
+ from .defaults import COMMENT_END_STRING
22
+ from .defaults import COMMENT_START_STRING
23
+ from .defaults import DEFAULT_FILTERS
24
+ from .defaults import DEFAULT_NAMESPACE
25
+ from .defaults import DEFAULT_POLICIES
26
+ from .defaults import DEFAULT_TESTS
27
+ from .defaults import KEEP_TRAILING_NEWLINE
28
+ from .defaults import LINE_COMMENT_PREFIX
29
+ from .defaults import LINE_STATEMENT_PREFIX
30
+ from .defaults import LSTRIP_BLOCKS
31
+ from .defaults import NEWLINE_SEQUENCE
32
+ from .defaults import TRIM_BLOCKS
33
+ from .defaults import VARIABLE_END_STRING
34
+ from .defaults import VARIABLE_START_STRING
35
+ from .exceptions import TemplateNotFound
36
+ from .exceptions import TemplateRuntimeError
37
+ from .exceptions import TemplatesNotFound
38
+ from .exceptions import TemplateSyntaxError
39
+ from .exceptions import UndefinedError
40
+ from .lexer import get_lexer
41
+ from .lexer import Lexer
42
+ from .lexer import TokenStream
43
+ from .nodes import EvalContext
44
+ from .parser import Parser
45
+ from .runtime import Context
46
+ from .runtime import new_context
47
+ from .runtime import Undefined
48
+ from .utils import _PassArg
49
+ from .utils import concat
50
+ from .utils import consume
51
+ from .utils import import_string
52
+ from .utils import internalcode
53
+ from .utils import LRUCache
54
+ from .utils import missing
55
+
56
+ if t.TYPE_CHECKING:
57
+ import typing_extensions as te
58
+ from .bccache import BytecodeCache
59
+ from .ext import Extension
60
+ from .loaders import BaseLoader
61
+
62
+ _env_bound = t.TypeVar("_env_bound", bound="Environment")
63
+
64
+
65
+ # for direct template usage we have up to ten living environments
66
+ @lru_cache(maxsize=10)
67
+ def get_spontaneous_environment(cls: t.Type[_env_bound], *args: t.Any) -> _env_bound:
68
+ """Return a new spontaneous environment. A spontaneous environment
69
+ is used for templates created directly rather than through an
70
+ existing environment.
71
+
72
+ :param cls: Environment class to create.
73
+ :param args: Positional arguments passed to environment.
74
+ """
75
+ env = cls(*args)
76
+ env.shared = True
77
+ return env
78
+
79
+
80
+ def create_cache(
81
+ size: int,
82
+ ) -> t.Optional[t.MutableMapping[t.Tuple[weakref.ref, str], "Template"]]:
83
+ """Return the cache class for the given size."""
84
+ if size == 0:
85
+ return None
86
+
87
+ if size < 0:
88
+ return {}
89
+
90
+ return LRUCache(size) # type: ignore
91
+
92
+
93
+ def copy_cache(
94
+ cache: t.Optional[t.MutableMapping],
95
+ ) -> t.Optional[t.MutableMapping[t.Tuple[weakref.ref, str], "Template"]]:
96
+ """Create an empty copy of the given cache."""
97
+ if cache is None:
98
+ return None
99
+
100
+ if type(cache) is dict:
101
+ return {}
102
+
103
+ return LRUCache(cache.capacity) # type: ignore
104
+
105
+
106
+ def load_extensions(
107
+ environment: "Environment",
108
+ extensions: t.Sequence[t.Union[str, t.Type["Extension"]]],
109
+ ) -> t.Dict[str, "Extension"]:
110
+ """Load the extensions from the list and bind it to the environment.
111
+ Returns a dict of instantiated extensions.
112
+ """
113
+ result = {}
114
+
115
+ for extension in extensions:
116
+ if isinstance(extension, str):
117
+ extension = t.cast(t.Type["Extension"], import_string(extension))
118
+
119
+ result[extension.identifier] = extension(environment)
120
+
121
+ return result
122
+
123
+
124
+ def _environment_config_check(environment: "Environment") -> "Environment":
125
+ """Perform a sanity check on the environment."""
126
+ assert issubclass(
127
+ environment.undefined, Undefined
128
+ ), "'undefined' must be a subclass of 'jinja2.Undefined'."
129
+ assert (
130
+ environment.block_start_string
131
+ != environment.variable_start_string
132
+ != environment.comment_start_string
133
+ ), "block, variable and comment start strings must be different."
134
+ assert environment.newline_sequence in {
135
+ "\r",
136
+ "\r\n",
137
+ "\n",
138
+ }, "'newline_sequence' must be one of '\\n', '\\r\\n', or '\\r'."
139
+ return environment
140
+
141
+
142
+ class Environment:
143
+ r"""The core component of Jinja is the `Environment`. It contains
144
+ important shared variables like configuration, filters, tests,
145
+ globals and others. Instances of this class may be modified if
146
+ they are not shared and if no template was loaded so far.
147
+ Modifications on environments after the first template was loaded
148
+ will lead to surprising effects and undefined behavior.
149
+
150
+ Here are the possible initialization parameters:
151
+
152
+ `block_start_string`
153
+ The string marking the beginning of a block. Defaults to ``'{%'``.
154
+
155
+ `block_end_string`
156
+ The string marking the end of a block. Defaults to ``'%}'``.
157
+
158
+ `variable_start_string`
159
+ The string marking the beginning of a print statement.
160
+ Defaults to ``'{{'``.
161
+
162
+ `variable_end_string`
163
+ The string marking the end of a print statement. Defaults to
164
+ ``'}}'``.
165
+
166
+ `comment_start_string`
167
+ The string marking the beginning of a comment. Defaults to ``'{#'``.
168
+
169
+ `comment_end_string`
170
+ The string marking the end of a comment. Defaults to ``'#}'``.
171
+
172
+ `line_statement_prefix`
173
+ If given and a string, this will be used as prefix for line based
174
+ statements. See also :ref:`line-statements`.
175
+
176
+ `line_comment_prefix`
177
+ If given and a string, this will be used as prefix for line based
178
+ comments. See also :ref:`line-statements`.
179
+
180
+ .. versionadded:: 2.2
181
+
182
+ `trim_blocks`
183
+ If this is set to ``True`` the first newline after a block is
184
+ removed (block, not variable tag!). Defaults to `False`.
185
+
186
+ `lstrip_blocks`
187
+ If this is set to ``True`` leading spaces and tabs are stripped
188
+ from the start of a line to a block. Defaults to `False`.
189
+
190
+ `newline_sequence`
191
+ The sequence that starts a newline. Must be one of ``'\r'``,
192
+ ``'\n'`` or ``'\r\n'``. The default is ``'\n'`` which is a
193
+ useful default for Linux and OS X systems as well as web
194
+ applications.
195
+
196
+ `keep_trailing_newline`
197
+ Preserve the trailing newline when rendering templates.
198
+ The default is ``False``, which causes a single newline,
199
+ if present, to be stripped from the end of the template.
200
+
201
+ .. versionadded:: 2.7
202
+
203
+ `extensions`
204
+ List of Jinja extensions to use. This can either be import paths
205
+ as strings or extension classes. For more information have a
206
+ look at :ref:`the extensions documentation <jinja-extensions>`.
207
+
208
+ `optimized`
209
+ should the optimizer be enabled? Default is ``True``.
210
+
211
+ `undefined`
212
+ :class:`Undefined` or a subclass of it that is used to represent
213
+ undefined values in the template.
214
+
215
+ `finalize`
216
+ A callable that can be used to process the result of a variable
217
+ expression before it is output. For example one can convert
218
+ ``None`` implicitly into an empty string here.
219
+
220
+ `autoescape`
221
+ If set to ``True`` the XML/HTML autoescaping feature is enabled by
222
+ default. For more details about autoescaping see
223
+ :class:`~markupsafe.Markup`. As of Jinja 2.4 this can also
224
+ be a callable that is passed the template name and has to
225
+ return ``True`` or ``False`` depending on autoescape should be
226
+ enabled by default.
227
+
228
+ .. versionchanged:: 2.4
229
+ `autoescape` can now be a function
230
+
231
+ `loader`
232
+ The template loader for this environment.
233
+
234
+ `cache_size`
235
+ The size of the cache. Per default this is ``400`` which means
236
+ that if more than 400 templates are loaded the loader will clean
237
+ out the least recently used template. If the cache size is set to
238
+ ``0`` templates are recompiled all the time, if the cache size is
239
+ ``-1`` the cache will not be cleaned.
240
+
241
+ .. versionchanged:: 2.8
242
+ The cache size was increased to 400 from a low 50.
243
+
244
+ `auto_reload`
245
+ Some loaders load templates from locations where the template
246
+ sources may change (ie: file system or database). If
247
+ ``auto_reload`` is set to ``True`` (default) every time a template is
248
+ requested the loader checks if the source changed and if yes, it
249
+ will reload the template. For higher performance it's possible to
250
+ disable that.
251
+
252
+ `bytecode_cache`
253
+ If set to a bytecode cache object, this object will provide a
254
+ cache for the internal Jinja bytecode so that templates don't
255
+ have to be parsed if they were not changed.
256
+
257
+ See :ref:`bytecode-cache` for more information.
258
+
259
+ `enable_async`
260
+ If set to true this enables async template execution which
261
+ allows using async functions and generators.
262
+ """
263
+
264
+ #: if this environment is sandboxed. Modifying this variable won't make
265
+ #: the environment sandboxed though. For a real sandboxed environment
266
+ #: have a look at jinja2.sandbox. This flag alone controls the code
267
+ #: generation by the compiler.
268
+ sandboxed = False
269
+
270
+ #: True if the environment is just an overlay
271
+ overlayed = False
272
+
273
+ #: the environment this environment is linked to if it is an overlay
274
+ linked_to: t.Optional["Environment"] = None
275
+
276
+ #: shared environments have this set to `True`. A shared environment
277
+ #: must not be modified
278
+ shared = False
279
+
280
+ #: the class that is used for code generation. See
281
+ #: :class:`~jinja2.compiler.CodeGenerator` for more information.
282
+ code_generator_class: t.Type["CodeGenerator"] = CodeGenerator
283
+
284
+ concat = "".join
285
+
286
+ #: the context class that is used for templates. See
287
+ #: :class:`~jinja2.runtime.Context` for more information.
288
+ context_class: t.Type[Context] = Context
289
+
290
+ template_class: t.Type["Template"]
291
+
292
+ def __init__(
293
+ self,
294
+ block_start_string: str = BLOCK_START_STRING,
295
+ block_end_string: str = BLOCK_END_STRING,
296
+ variable_start_string: str = VARIABLE_START_STRING,
297
+ variable_end_string: str = VARIABLE_END_STRING,
298
+ comment_start_string: str = COMMENT_START_STRING,
299
+ comment_end_string: str = COMMENT_END_STRING,
300
+ line_statement_prefix: t.Optional[str] = LINE_STATEMENT_PREFIX,
301
+ line_comment_prefix: t.Optional[str] = LINE_COMMENT_PREFIX,
302
+ trim_blocks: bool = TRIM_BLOCKS,
303
+ lstrip_blocks: bool = LSTRIP_BLOCKS,
304
+ newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = NEWLINE_SEQUENCE,
305
+ keep_trailing_newline: bool = KEEP_TRAILING_NEWLINE,
306
+ extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = (),
307
+ optimized: bool = True,
308
+ undefined: t.Type[Undefined] = Undefined,
309
+ finalize: t.Optional[t.Callable[..., t.Any]] = None,
310
+ autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = False,
311
+ loader: t.Optional["BaseLoader"] = None,
312
+ cache_size: int = 400,
313
+ auto_reload: bool = True,
314
+ bytecode_cache: t.Optional["BytecodeCache"] = None,
315
+ enable_async: bool = False,
316
+ ):
317
+ # !!Important notice!!
318
+ # The constructor accepts quite a few arguments that should be
319
+ # passed by keyword rather than position. However it's important to
320
+ # not change the order of arguments because it's used at least
321
+ # internally in those cases:
322
+ # - spontaneous environments (i18n extension and Template)
323
+ # - unittests
324
+ # If parameter changes are required only add parameters at the end
325
+ # and don't change the arguments (or the defaults!) of the arguments
326
+ # existing already.
327
+
328
+ # lexer / parser information
329
+ self.block_start_string = block_start_string
330
+ self.block_end_string = block_end_string
331
+ self.variable_start_string = variable_start_string
332
+ self.variable_end_string = variable_end_string
333
+ self.comment_start_string = comment_start_string
334
+ self.comment_end_string = comment_end_string
335
+ self.line_statement_prefix = line_statement_prefix
336
+ self.line_comment_prefix = line_comment_prefix
337
+ self.trim_blocks = trim_blocks
338
+ self.lstrip_blocks = lstrip_blocks
339
+ self.newline_sequence = newline_sequence
340
+ self.keep_trailing_newline = keep_trailing_newline
341
+
342
+ # runtime information
343
+ self.undefined: t.Type[Undefined] = undefined
344
+ self.optimized = optimized
345
+ self.finalize = finalize
346
+ self.autoescape = autoescape
347
+
348
+ # defaults
349
+ self.filters = DEFAULT_FILTERS.copy()
350
+ self.tests = DEFAULT_TESTS.copy()
351
+ self.globals = DEFAULT_NAMESPACE.copy()
352
+
353
+ # set the loader provided
354
+ self.loader = loader
355
+ self.cache = create_cache(cache_size)
356
+ self.bytecode_cache = bytecode_cache
357
+ self.auto_reload = auto_reload
358
+
359
+ # configurable policies
360
+ self.policies = DEFAULT_POLICIES.copy()
361
+
362
+ # load extensions
363
+ self.extensions = load_extensions(self, extensions)
364
+
365
+ self.is_async = enable_async
366
+ _environment_config_check(self)
367
+
368
+ def add_extension(self, extension: t.Union[str, t.Type["Extension"]]) -> None:
369
+ """Adds an extension after the environment was created.
370
+
371
+ .. versionadded:: 2.5
372
+ """
373
+ self.extensions.update(load_extensions(self, [extension]))
374
+
375
+ def extend(self, **attributes: t.Any) -> None:
376
+ """Add the items to the instance of the environment if they do not exist
377
+ yet. This is used by :ref:`extensions <writing-extensions>` to register
378
+ callbacks and configuration values without breaking inheritance.
379
+ """
380
+ for key, value in attributes.items():
381
+ if not hasattr(self, key):
382
+ setattr(self, key, value)
383
+
384
+ def overlay(
385
+ self,
386
+ block_start_string: str = missing,
387
+ block_end_string: str = missing,
388
+ variable_start_string: str = missing,
389
+ variable_end_string: str = missing,
390
+ comment_start_string: str = missing,
391
+ comment_end_string: str = missing,
392
+ line_statement_prefix: t.Optional[str] = missing,
393
+ line_comment_prefix: t.Optional[str] = missing,
394
+ trim_blocks: bool = missing,
395
+ lstrip_blocks: bool = missing,
396
+ newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = missing,
397
+ keep_trailing_newline: bool = missing,
398
+ extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = missing,
399
+ optimized: bool = missing,
400
+ undefined: t.Type[Undefined] = missing,
401
+ finalize: t.Optional[t.Callable[..., t.Any]] = missing,
402
+ autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = missing,
403
+ loader: t.Optional["BaseLoader"] = missing,
404
+ cache_size: int = missing,
405
+ auto_reload: bool = missing,
406
+ bytecode_cache: t.Optional["BytecodeCache"] = missing,
407
+ enable_async: bool = False,
408
+ ) -> "Environment":
409
+ """Create a new overlay environment that shares all the data with the
410
+ current environment except for cache and the overridden attributes.
411
+ Extensions cannot be removed for an overlayed environment. An overlayed
412
+ environment automatically gets all the extensions of the environment it
413
+ is linked to plus optional extra extensions.
414
+
415
+ Creating overlays should happen after the initial environment was set
416
+ up completely. Not all attributes are truly linked, some are just
417
+ copied over so modifications on the original environment may not shine
418
+ through.
419
+
420
+ .. versionchanged:: 3.1.2
421
+ Added the ``newline_sequence``,, ``keep_trailing_newline``,
422
+ and ``enable_async`` parameters to match ``__init__``.
423
+ """
424
+ args = dict(locals())
425
+ del args["self"], args["cache_size"], args["extensions"], args["enable_async"]
426
+
427
+ rv = object.__new__(self.__class__)
428
+ rv.__dict__.update(self.__dict__)
429
+ rv.overlayed = True
430
+ rv.linked_to = self
431
+
432
+ for key, value in args.items():
433
+ if value is not missing:
434
+ setattr(rv, key, value)
435
+
436
+ if cache_size is not missing:
437
+ rv.cache = create_cache(cache_size)
438
+ else:
439
+ rv.cache = copy_cache(self.cache)
440
+
441
+ rv.extensions = {}
442
+ for key, value in self.extensions.items():
443
+ rv.extensions[key] = value.bind(rv)
444
+ if extensions is not missing:
445
+ rv.extensions.update(load_extensions(rv, extensions))
446
+
447
+ if enable_async is not missing:
448
+ rv.is_async = enable_async
449
+
450
+ return _environment_config_check(rv)
451
+
452
+ @property
453
+ def lexer(self) -> Lexer:
454
+ """The lexer for this environment."""
455
+ return get_lexer(self)
456
+
457
+ def iter_extensions(self) -> t.Iterator["Extension"]:
458
+ """Iterates over the extensions by priority."""
459
+ return iter(sorted(self.extensions.values(), key=lambda x: x.priority))
460
+
461
+ def getitem(
462
+ self, obj: t.Any, argument: t.Union[str, t.Any]
463
+ ) -> t.Union[t.Any, Undefined]:
464
+ """Get an item or attribute of an object but prefer the item."""
465
+ try:
466
+ return obj[argument]
467
+ except (AttributeError, TypeError, LookupError):
468
+ if isinstance(argument, str):
469
+ try:
470
+ attr = str(argument)
471
+ except Exception:
472
+ pass
473
+ else:
474
+ try:
475
+ return getattr(obj, attr)
476
+ except AttributeError:
477
+ pass
478
+ return self.undefined(obj=obj, name=argument)
479
+
480
+ def getattr(self, obj: t.Any, attribute: str) -> t.Any:
481
+ """Get an item or attribute of an object but prefer the attribute.
482
+ Unlike :meth:`getitem` the attribute *must* be a string.
483
+ """
484
+ try:
485
+ return getattr(obj, attribute)
486
+ except AttributeError:
487
+ pass
488
+ try:
489
+ return obj[attribute]
490
+ except (TypeError, LookupError, AttributeError):
491
+ return self.undefined(obj=obj, name=attribute)
492
+
493
+ def _filter_test_common(
494
+ self,
495
+ name: t.Union[str, Undefined],
496
+ value: t.Any,
497
+ args: t.Optional[t.Sequence[t.Any]],
498
+ kwargs: t.Optional[t.Mapping[str, t.Any]],
499
+ context: t.Optional[Context],
500
+ eval_ctx: t.Optional[EvalContext],
501
+ is_filter: bool,
502
+ ) -> t.Any:
503
+ if is_filter:
504
+ env_map = self.filters
505
+ type_name = "filter"
506
+ else:
507
+ env_map = self.tests
508
+ type_name = "test"
509
+
510
+ func = env_map.get(name) # type: ignore
511
+
512
+ if func is None:
513
+ msg = f"No {type_name} named {name!r}."
514
+
515
+ if isinstance(name, Undefined):
516
+ try:
517
+ name._fail_with_undefined_error()
518
+ except Exception as e:
519
+ msg = f"{msg} ({e}; did you forget to quote the callable name?)"
520
+
521
+ raise TemplateRuntimeError(msg)
522
+
523
+ args = [value, *(args if args is not None else ())]
524
+ kwargs = kwargs if kwargs is not None else {}
525
+ pass_arg = _PassArg.from_obj(func)
526
+
527
+ if pass_arg is _PassArg.context:
528
+ if context is None:
529
+ raise TemplateRuntimeError(
530
+ f"Attempted to invoke a context {type_name} without context."
531
+ )
532
+
533
+ args.insert(0, context)
534
+ elif pass_arg is _PassArg.eval_context:
535
+ if eval_ctx is None:
536
+ if context is not None:
537
+ eval_ctx = context.eval_ctx
538
+ else:
539
+ eval_ctx = EvalContext(self)
540
+
541
+ args.insert(0, eval_ctx)
542
+ elif pass_arg is _PassArg.environment:
543
+ args.insert(0, self)
544
+
545
+ return func(*args, **kwargs)
546
+
547
+ def call_filter(
548
+ self,
549
+ name: str,
550
+ value: t.Any,
551
+ args: t.Optional[t.Sequence[t.Any]] = None,
552
+ kwargs: t.Optional[t.Mapping[str, t.Any]] = None,
553
+ context: t.Optional[Context] = None,
554
+ eval_ctx: t.Optional[EvalContext] = None,
555
+ ) -> t.Any:
556
+ """Invoke a filter on a value the same way the compiler does.
557
+
558
+ This might return a coroutine if the filter is running from an
559
+ environment in async mode and the filter supports async
560
+ execution. It's your responsibility to await this if needed.
561
+
562
+ .. versionadded:: 2.7
563
+ """
564
+ return self._filter_test_common(
565
+ name, value, args, kwargs, context, eval_ctx, True
566
+ )
567
+
568
+ def call_test(
569
+ self,
570
+ name: str,
571
+ value: t.Any,
572
+ args: t.Optional[t.Sequence[t.Any]] = None,
573
+ kwargs: t.Optional[t.Mapping[str, t.Any]] = None,
574
+ context: t.Optional[Context] = None,
575
+ eval_ctx: t.Optional[EvalContext] = None,
576
+ ) -> t.Any:
577
+ """Invoke a test on a value the same way the compiler does.
578
+
579
+ This might return a coroutine if the test is running from an
580
+ environment in async mode and the test supports async execution.
581
+ It's your responsibility to await this if needed.
582
+
583
+ .. versionchanged:: 3.0
584
+ Tests support ``@pass_context``, etc. decorators. Added
585
+ the ``context`` and ``eval_ctx`` parameters.
586
+
587
+ .. versionadded:: 2.7
588
+ """
589
+ return self._filter_test_common(
590
+ name, value, args, kwargs, context, eval_ctx, False
591
+ )
592
+
593
+ @internalcode
594
+ def parse(
595
+ self,
596
+ source: str,
597
+ name: t.Optional[str] = None,
598
+ filename: t.Optional[str] = None,
599
+ ) -> nodes.Template:
600
+ """Parse the sourcecode and return the abstract syntax tree. This
601
+ tree of nodes is used by the compiler to convert the template into
602
+ executable source- or bytecode. This is useful for debugging or to
603
+ extract information from templates.
604
+
605
+ If you are :ref:`developing Jinja extensions <writing-extensions>`
606
+ this gives you a good overview of the node tree generated.
607
+ """
608
+ try:
609
+ return self._parse(source, name, filename)
610
+ except TemplateSyntaxError:
611
+ self.handle_exception(source=source)
612
+
613
+ def _parse(
614
+ self, source: str, name: t.Optional[str], filename: t.Optional[str]
615
+ ) -> nodes.Template:
616
+ """Internal parsing function used by `parse` and `compile`."""
617
+ return Parser(self, source, name, filename).parse()
618
+
619
+ def lex(
620
+ self,
621
+ source: str,
622
+ name: t.Optional[str] = None,
623
+ filename: t.Optional[str] = None,
624
+ ) -> t.Iterator[t.Tuple[int, str, str]]:
625
+ """Lex the given sourcecode and return a generator that yields
626
+ tokens as tuples in the form ``(lineno, token_type, value)``.
627
+ This can be useful for :ref:`extension development <writing-extensions>`
628
+ and debugging templates.
629
+
630
+ This does not perform preprocessing. If you want the preprocessing
631
+ of the extensions to be applied you have to filter source through
632
+ the :meth:`preprocess` method.
633
+ """
634
+ source = str(source)
635
+ try:
636
+ return self.lexer.tokeniter(source, name, filename)
637
+ except TemplateSyntaxError:
638
+ self.handle_exception(source=source)
639
+
640
+ def preprocess(
641
+ self,
642
+ source: str,
643
+ name: t.Optional[str] = None,
644
+ filename: t.Optional[str] = None,
645
+ ) -> str:
646
+ """Preprocesses the source with all extensions. This is automatically
647
+ called for all parsing and compiling methods but *not* for :meth:`lex`
648
+ because there you usually only want the actual source tokenized.
649
+ """
650
+ return reduce(
651
+ lambda s, e: e.preprocess(s, name, filename),
652
+ self.iter_extensions(),
653
+ str(source),
654
+ )
655
+
656
+ def _tokenize(
657
+ self,
658
+ source: str,
659
+ name: t.Optional[str],
660
+ filename: t.Optional[str] = None,
661
+ state: t.Optional[str] = None,
662
+ ) -> TokenStream:
663
+ """Called by the parser to do the preprocessing and filtering
664
+ for all the extensions. Returns a :class:`~jinja2.lexer.TokenStream`.
665
+ """
666
+ source = self.preprocess(source, name, filename)
667
+ stream = self.lexer.tokenize(source, name, filename, state)
668
+
669
+ for ext in self.iter_extensions():
670
+ stream = ext.filter_stream(stream) # type: ignore
671
+
672
+ if not isinstance(stream, TokenStream):
673
+ stream = TokenStream(stream, name, filename) # type: ignore
674
+
675
+ return stream
676
+
677
+ def _generate(
678
+ self,
679
+ source: nodes.Template,
680
+ name: t.Optional[str],
681
+ filename: t.Optional[str],
682
+ defer_init: bool = False,
683
+ ) -> str:
684
+ """Internal hook that can be overridden to hook a different generate
685
+ method in.
686
+
687
+ .. versionadded:: 2.5
688
+ """
689
+ return generate( # type: ignore
690
+ source,
691
+ self,
692
+ name,
693
+ filename,
694
+ defer_init=defer_init,
695
+ optimized=self.optimized,
696
+ )
697
+
698
+ def _compile(self, source: str, filename: str) -> CodeType:
699
+ """Internal hook that can be overridden to hook a different compile
700
+ method in.
701
+
702
+ .. versionadded:: 2.5
703
+ """
704
+ return compile(source, filename, "exec")
705
+
706
+ @typing.overload
707
+ def compile( # type: ignore
708
+ self,
709
+ source: t.Union[str, nodes.Template],
710
+ name: t.Optional[str] = None,
711
+ filename: t.Optional[str] = None,
712
+ raw: "te.Literal[False]" = False,
713
+ defer_init: bool = False,
714
+ ) -> CodeType:
715
+ ...
716
+
717
+ @typing.overload
718
+ def compile(
719
+ self,
720
+ source: t.Union[str, nodes.Template],
721
+ name: t.Optional[str] = None,
722
+ filename: t.Optional[str] = None,
723
+ raw: "te.Literal[True]" = ...,
724
+ defer_init: bool = False,
725
+ ) -> str:
726
+ ...
727
+
728
+ @internalcode
729
+ def compile(
730
+ self,
731
+ source: t.Union[str, nodes.Template],
732
+ name: t.Optional[str] = None,
733
+ filename: t.Optional[str] = None,
734
+ raw: bool = False,
735
+ defer_init: bool = False,
736
+ ) -> t.Union[str, CodeType]:
737
+ """Compile a node or template source code. The `name` parameter is
738
+ the load name of the template after it was joined using
739
+ :meth:`join_path` if necessary, not the filename on the file system.
740
+ the `filename` parameter is the estimated filename of the template on
741
+ the file system. If the template came from a database or memory this
742
+ can be omitted.
743
+
744
+ The return value of this method is a python code object. If the `raw`
745
+ parameter is `True` the return value will be a string with python
746
+ code equivalent to the bytecode returned otherwise. This method is
747
+ mainly used internally.
748
+
749
+ `defer_init` is use internally to aid the module code generator. This
750
+ causes the generated code to be able to import without the global
751
+ environment variable to be set.
752
+
753
+ .. versionadded:: 2.4
754
+ `defer_init` parameter added.
755
+ """
756
+ source_hint = None
757
+ try:
758
+ if isinstance(source, str):
759
+ source_hint = source
760
+ source = self._parse(source, name, filename)
761
+ source = self._generate(source, name, filename, defer_init=defer_init)
762
+ if raw:
763
+ return source
764
+ if filename is None:
765
+ filename = "<template>"
766
+ return self._compile(source, filename)
767
+ except TemplateSyntaxError:
768
+ self.handle_exception(source=source_hint)
769
+
770
+ def compile_expression(
771
+ self, source: str, undefined_to_none: bool = True
772
+ ) -> "TemplateExpression":
773
+ """A handy helper method that returns a callable that accepts keyword
774
+ arguments that appear as variables in the expression. If called it
775
+ returns the result of the expression.
776
+
777
+ This is useful if applications want to use the same rules as Jinja
778
+ in template "configuration files" or similar situations.
779
+
780
+ Example usage:
781
+
782
+ >>> env = Environment()
783
+ >>> expr = env.compile_expression('foo == 42')
784
+ >>> expr(foo=23)
785
+ False
786
+ >>> expr(foo=42)
787
+ True
788
+
789
+ Per default the return value is converted to `None` if the
790
+ expression returns an undefined value. This can be changed
791
+ by setting `undefined_to_none` to `False`.
792
+
793
+ >>> env.compile_expression('var')() is None
794
+ True
795
+ >>> env.compile_expression('var', undefined_to_none=False)()
796
+ Undefined
797
+
798
+ .. versionadded:: 2.1
799
+ """
800
+ parser = Parser(self, source, state="variable")
801
+ try:
802
+ expr = parser.parse_expression()
803
+ if not parser.stream.eos:
804
+ raise TemplateSyntaxError(
805
+ "chunk after expression", parser.stream.current.lineno, None, None
806
+ )
807
+ expr.set_environment(self)
808
+ except TemplateSyntaxError:
809
+ self.handle_exception(source=source)
810
+
811
+ body = [nodes.Assign(nodes.Name("result", "store"), expr, lineno=1)]
812
+ template = self.from_string(nodes.Template(body, lineno=1))
813
+ return TemplateExpression(template, undefined_to_none)
814
+
815
+ def compile_templates(
816
+ self,
817
+ target: t.Union[str, os.PathLike],
818
+ extensions: t.Optional[t.Collection[str]] = None,
819
+ filter_func: t.Optional[t.Callable[[str], bool]] = None,
820
+ zip: t.Optional[str] = "deflated",
821
+ log_function: t.Optional[t.Callable[[str], None]] = None,
822
+ ignore_errors: bool = True,
823
+ ) -> None:
824
+ """Finds all the templates the loader can find, compiles them
825
+ and stores them in `target`. If `zip` is `None`, instead of in a
826
+ zipfile, the templates will be stored in a directory.
827
+ By default a deflate zip algorithm is used. To switch to
828
+ the stored algorithm, `zip` can be set to ``'stored'``.
829
+
830
+ `extensions` and `filter_func` are passed to :meth:`list_templates`.
831
+ Each template returned will be compiled to the target folder or
832
+ zipfile.
833
+
834
+ By default template compilation errors are ignored. In case a
835
+ log function is provided, errors are logged. If you want template
836
+ syntax errors to abort the compilation you can set `ignore_errors`
837
+ to `False` and you will get an exception on syntax errors.
838
+
839
+ .. versionadded:: 2.4
840
+ """
841
+ from .loaders import ModuleLoader
842
+
843
+ if log_function is None:
844
+
845
+ def log_function(x: str) -> None:
846
+ pass
847
+
848
+ assert log_function is not None
849
+ assert self.loader is not None, "No loader configured."
850
+
851
+ def write_file(filename: str, data: str) -> None:
852
+ if zip:
853
+ info = ZipInfo(filename)
854
+ info.external_attr = 0o755 << 16
855
+ zip_file.writestr(info, data)
856
+ else:
857
+ with open(os.path.join(target, filename), "wb") as f:
858
+ f.write(data.encode("utf8"))
859
+
860
+ if zip is not None:
861
+ from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED, ZIP_STORED
862
+
863
+ zip_file = ZipFile(
864
+ target, "w", dict(deflated=ZIP_DEFLATED, stored=ZIP_STORED)[zip]
865
+ )
866
+ log_function(f"Compiling into Zip archive {target!r}")
867
+ else:
868
+ if not os.path.isdir(target):
869
+ os.makedirs(target)
870
+ log_function(f"Compiling into folder {target!r}")
871
+
872
+ try:
873
+ for name in self.list_templates(extensions, filter_func):
874
+ source, filename, _ = self.loader.get_source(self, name)
875
+ try:
876
+ code = self.compile(source, name, filename, True, True)
877
+ except TemplateSyntaxError as e:
878
+ if not ignore_errors:
879
+ raise
880
+ log_function(f'Could not compile "{name}": {e}')
881
+ continue
882
+
883
+ filename = ModuleLoader.get_module_filename(name)
884
+
885
+ write_file(filename, code)
886
+ log_function(f'Compiled "{name}" as {filename}')
887
+ finally:
888
+ if zip:
889
+ zip_file.close()
890
+
891
+ log_function("Finished compiling templates")
892
+
893
+ def list_templates(
894
+ self,
895
+ extensions: t.Optional[t.Collection[str]] = None,
896
+ filter_func: t.Optional[t.Callable[[str], bool]] = None,
897
+ ) -> t.List[str]:
898
+ """Returns a list of templates for this environment. This requires
899
+ that the loader supports the loader's
900
+ :meth:`~BaseLoader.list_templates` method.
901
+
902
+ If there are other files in the template folder besides the
903
+ actual templates, the returned list can be filtered. There are two
904
+ ways: either `extensions` is set to a list of file extensions for
905
+ templates, or a `filter_func` can be provided which is a callable that
906
+ is passed a template name and should return `True` if it should end up
907
+ in the result list.
908
+
909
+ If the loader does not support that, a :exc:`TypeError` is raised.
910
+
911
+ .. versionadded:: 2.4
912
+ """
913
+ assert self.loader is not None, "No loader configured."
914
+ names = self.loader.list_templates()
915
+
916
+ if extensions is not None:
917
+ if filter_func is not None:
918
+ raise TypeError(
919
+ "either extensions or filter_func can be passed, but not both"
920
+ )
921
+
922
+ def filter_func(x: str) -> bool:
923
+ return "." in x and x.rsplit(".", 1)[1] in extensions
924
+
925
+ if filter_func is not None:
926
+ names = [name for name in names if filter_func(name)]
927
+
928
+ return names
929
+
930
+ def handle_exception(self, source: t.Optional[str] = None) -> "te.NoReturn":
931
+ """Exception handling helper. This is used internally to either raise
932
+ rewritten exceptions or return a rendered traceback for the template.
933
+ """
934
+ from .debug import rewrite_traceback_stack
935
+
936
+ raise rewrite_traceback_stack(source=source)
937
+
938
+ def join_path(self, template: str, parent: str) -> str:
939
+ """Join a template with the parent. By default all the lookups are
940
+ relative to the loader root so this method returns the `template`
941
+ parameter unchanged, but if the paths should be relative to the
942
+ parent template, this function can be used to calculate the real
943
+ template name.
944
+
945
+ Subclasses may override this method and implement template path
946
+ joining here.
947
+ """
948
+ return template
949
+
950
+ @internalcode
951
+ def _load_template(
952
+ self, name: str, globals: t.Optional[t.MutableMapping[str, t.Any]]
953
+ ) -> "Template":
954
+ if self.loader is None:
955
+ raise TypeError("no loader for this environment specified")
956
+ cache_key = (weakref.ref(self.loader), name)
957
+ if self.cache is not None:
958
+ template = self.cache.get(cache_key)
959
+ if template is not None and (
960
+ not self.auto_reload or template.is_up_to_date
961
+ ):
962
+ # template.globals is a ChainMap, modifying it will only
963
+ # affect the template, not the environment globals.
964
+ if globals:
965
+ template.globals.update(globals)
966
+
967
+ return template
968
+
969
+ template = self.loader.load(self, name, self.make_globals(globals))
970
+
971
+ if self.cache is not None:
972
+ self.cache[cache_key] = template
973
+ return template
974
+
975
+ @internalcode
976
+ def get_template(
977
+ self,
978
+ name: t.Union[str, "Template"],
979
+ parent: t.Optional[str] = None,
980
+ globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
981
+ ) -> "Template":
982
+ """Load a template by name with :attr:`loader` and return a
983
+ :class:`Template`. If the template does not exist a
984
+ :exc:`TemplateNotFound` exception is raised.
985
+
986
+ :param name: Name of the template to load. When loading
987
+ templates from the filesystem, "/" is used as the path
988
+ separator, even on Windows.
989
+ :param parent: The name of the parent template importing this
990
+ template. :meth:`join_path` can be used to implement name
991
+ transformations with this.
992
+ :param globals: Extend the environment :attr:`globals` with
993
+ these extra variables available for all renders of this
994
+ template. If the template has already been loaded and
995
+ cached, its globals are updated with any new items.
996
+
997
+ .. versionchanged:: 3.0
998
+ If a template is loaded from cache, ``globals`` will update
999
+ the template's globals instead of ignoring the new values.
1000
+
1001
+ .. versionchanged:: 2.4
1002
+ If ``name`` is a :class:`Template` object it is returned
1003
+ unchanged.
1004
+ """
1005
+ if isinstance(name, Template):
1006
+ return name
1007
+ if parent is not None:
1008
+ name = self.join_path(name, parent)
1009
+
1010
+ return self._load_template(name, globals)
1011
+
1012
+ @internalcode
1013
+ def select_template(
1014
+ self,
1015
+ names: t.Iterable[t.Union[str, "Template"]],
1016
+ parent: t.Optional[str] = None,
1017
+ globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
1018
+ ) -> "Template":
1019
+ """Like :meth:`get_template`, but tries loading multiple names.
1020
+ If none of the names can be loaded a :exc:`TemplatesNotFound`
1021
+ exception is raised.
1022
+
1023
+ :param names: List of template names to try loading in order.
1024
+ :param parent: The name of the parent template importing this
1025
+ template. :meth:`join_path` can be used to implement name
1026
+ transformations with this.
1027
+ :param globals: Extend the environment :attr:`globals` with
1028
+ these extra variables available for all renders of this
1029
+ template. If the template has already been loaded and
1030
+ cached, its globals are updated with any new items.
1031
+
1032
+ .. versionchanged:: 3.0
1033
+ If a template is loaded from cache, ``globals`` will update
1034
+ the template's globals instead of ignoring the new values.
1035
+
1036
+ .. versionchanged:: 2.11
1037
+ If ``names`` is :class:`Undefined`, an :exc:`UndefinedError`
1038
+ is raised instead. If no templates were found and ``names``
1039
+ contains :class:`Undefined`, the message is more helpful.
1040
+
1041
+ .. versionchanged:: 2.4
1042
+ If ``names`` contains a :class:`Template` object it is
1043
+ returned unchanged.
1044
+
1045
+ .. versionadded:: 2.3
1046
+ """
1047
+ if isinstance(names, Undefined):
1048
+ names._fail_with_undefined_error()
1049
+
1050
+ if not names:
1051
+ raise TemplatesNotFound(
1052
+ message="Tried to select from an empty list of templates."
1053
+ )
1054
+
1055
+ for name in names:
1056
+ if isinstance(name, Template):
1057
+ return name
1058
+ if parent is not None:
1059
+ name = self.join_path(name, parent)
1060
+ try:
1061
+ return self._load_template(name, globals)
1062
+ except (TemplateNotFound, UndefinedError):
1063
+ pass
1064
+ raise TemplatesNotFound(names) # type: ignore
1065
+
1066
+ @internalcode
1067
+ def get_or_select_template(
1068
+ self,
1069
+ template_name_or_list: t.Union[
1070
+ str, "Template", t.List[t.Union[str, "Template"]]
1071
+ ],
1072
+ parent: t.Optional[str] = None,
1073
+ globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
1074
+ ) -> "Template":
1075
+ """Use :meth:`select_template` if an iterable of template names
1076
+ is given, or :meth:`get_template` if one name is given.
1077
+
1078
+ .. versionadded:: 2.3
1079
+ """
1080
+ if isinstance(template_name_or_list, (str, Undefined)):
1081
+ return self.get_template(template_name_or_list, parent, globals)
1082
+ elif isinstance(template_name_or_list, Template):
1083
+ return template_name_or_list
1084
+ return self.select_template(template_name_or_list, parent, globals)
1085
+
1086
+ def from_string(
1087
+ self,
1088
+ source: t.Union[str, nodes.Template],
1089
+ globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
1090
+ template_class: t.Optional[t.Type["Template"]] = None,
1091
+ ) -> "Template":
1092
+ """Load a template from a source string without using
1093
+ :attr:`loader`.
1094
+
1095
+ :param source: Jinja source to compile into a template.
1096
+ :param globals: Extend the environment :attr:`globals` with
1097
+ these extra variables available for all renders of this
1098
+ template. If the template has already been loaded and
1099
+ cached, its globals are updated with any new items.
1100
+ :param template_class: Return an instance of this
1101
+ :class:`Template` class.
1102
+ """
1103
+ gs = self.make_globals(globals)
1104
+ cls = template_class or self.template_class
1105
+ return cls.from_code(self, self.compile(source), gs, None)
1106
+
1107
+ def make_globals(
1108
+ self, d: t.Optional[t.MutableMapping[str, t.Any]]
1109
+ ) -> t.MutableMapping[str, t.Any]:
1110
+ """Make the globals map for a template. Any given template
1111
+ globals overlay the environment :attr:`globals`.
1112
+
1113
+ Returns a :class:`collections.ChainMap`. This allows any changes
1114
+ to a template's globals to only affect that template, while
1115
+ changes to the environment's globals are still reflected.
1116
+ However, avoid modifying any globals after a template is loaded.
1117
+
1118
+ :param d: Dict of template-specific globals.
1119
+
1120
+ .. versionchanged:: 3.0
1121
+ Use :class:`collections.ChainMap` to always prevent mutating
1122
+ environment globals.
1123
+ """
1124
+ if d is None:
1125
+ d = {}
1126
+
1127
+ return ChainMap(d, self.globals)
1128
+
1129
+
1130
+ class Template:
1131
+ """A compiled template that can be rendered.
1132
+
1133
+ Use the methods on :class:`Environment` to create or load templates.
1134
+ The environment is used to configure how templates are compiled and
1135
+ behave.
1136
+
1137
+ It is also possible to create a template object directly. This is
1138
+ not usually recommended. The constructor takes most of the same
1139
+ arguments as :class:`Environment`. All templates created with the
1140
+ same environment arguments share the same ephemeral ``Environment``
1141
+ instance behind the scenes.
1142
+
1143
+ A template object should be considered immutable. Modifications on
1144
+ the object are not supported.
1145
+ """
1146
+
1147
+ #: Type of environment to create when creating a template directly
1148
+ #: rather than through an existing environment.
1149
+ environment_class: t.Type[Environment] = Environment
1150
+
1151
+ environment: Environment
1152
+ globals: t.MutableMapping[str, t.Any]
1153
+ name: t.Optional[str]
1154
+ filename: t.Optional[str]
1155
+ blocks: t.Dict[str, t.Callable[[Context], t.Iterator[str]]]
1156
+ root_render_func: t.Callable[[Context], t.Iterator[str]]
1157
+ _module: t.Optional["TemplateModule"]
1158
+ _debug_info: str
1159
+ _uptodate: t.Optional[t.Callable[[], bool]]
1160
+
1161
+ def __new__(
1162
+ cls,
1163
+ source: t.Union[str, nodes.Template],
1164
+ block_start_string: str = BLOCK_START_STRING,
1165
+ block_end_string: str = BLOCK_END_STRING,
1166
+ variable_start_string: str = VARIABLE_START_STRING,
1167
+ variable_end_string: str = VARIABLE_END_STRING,
1168
+ comment_start_string: str = COMMENT_START_STRING,
1169
+ comment_end_string: str = COMMENT_END_STRING,
1170
+ line_statement_prefix: t.Optional[str] = LINE_STATEMENT_PREFIX,
1171
+ line_comment_prefix: t.Optional[str] = LINE_COMMENT_PREFIX,
1172
+ trim_blocks: bool = TRIM_BLOCKS,
1173
+ lstrip_blocks: bool = LSTRIP_BLOCKS,
1174
+ newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = NEWLINE_SEQUENCE,
1175
+ keep_trailing_newline: bool = KEEP_TRAILING_NEWLINE,
1176
+ extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = (),
1177
+ optimized: bool = True,
1178
+ undefined: t.Type[Undefined] = Undefined,
1179
+ finalize: t.Optional[t.Callable[..., t.Any]] = None,
1180
+ autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = False,
1181
+ enable_async: bool = False,
1182
+ ) -> t.Any: # it returns a `Template`, but this breaks the sphinx build...
1183
+ env = get_spontaneous_environment(
1184
+ cls.environment_class, # type: ignore
1185
+ block_start_string,
1186
+ block_end_string,
1187
+ variable_start_string,
1188
+ variable_end_string,
1189
+ comment_start_string,
1190
+ comment_end_string,
1191
+ line_statement_prefix,
1192
+ line_comment_prefix,
1193
+ trim_blocks,
1194
+ lstrip_blocks,
1195
+ newline_sequence,
1196
+ keep_trailing_newline,
1197
+ frozenset(extensions),
1198
+ optimized,
1199
+ undefined, # type: ignore
1200
+ finalize,
1201
+ autoescape,
1202
+ None,
1203
+ 0,
1204
+ False,
1205
+ None,
1206
+ enable_async,
1207
+ )
1208
+ return env.from_string(source, template_class=cls)
1209
+
1210
+ @classmethod
1211
+ def from_code(
1212
+ cls,
1213
+ environment: Environment,
1214
+ code: CodeType,
1215
+ globals: t.MutableMapping[str, t.Any],
1216
+ uptodate: t.Optional[t.Callable[[], bool]] = None,
1217
+ ) -> "Template":
1218
+ """Creates a template object from compiled code and the globals. This
1219
+ is used by the loaders and environment to create a template object.
1220
+ """
1221
+ namespace = {"environment": environment, "__file__": code.co_filename}
1222
+ exec(code, namespace)
1223
+ rv = cls._from_namespace(environment, namespace, globals)
1224
+ rv._uptodate = uptodate
1225
+ return rv
1226
+
1227
+ @classmethod
1228
+ def from_module_dict(
1229
+ cls,
1230
+ environment: Environment,
1231
+ module_dict: t.MutableMapping[str, t.Any],
1232
+ globals: t.MutableMapping[str, t.Any],
1233
+ ) -> "Template":
1234
+ """Creates a template object from a module. This is used by the
1235
+ module loader to create a template object.
1236
+
1237
+ .. versionadded:: 2.4
1238
+ """
1239
+ return cls._from_namespace(environment, module_dict, globals)
1240
+
1241
+ @classmethod
1242
+ def _from_namespace(
1243
+ cls,
1244
+ environment: Environment,
1245
+ namespace: t.MutableMapping[str, t.Any],
1246
+ globals: t.MutableMapping[str, t.Any],
1247
+ ) -> "Template":
1248
+ t: "Template" = object.__new__(cls)
1249
+ t.environment = environment
1250
+ t.globals = globals
1251
+ t.name = namespace["name"]
1252
+ t.filename = namespace["__file__"]
1253
+ t.blocks = namespace["blocks"]
1254
+
1255
+ # render function and module
1256
+ t.root_render_func = namespace["root"]
1257
+ t._module = None
1258
+
1259
+ # debug and loader helpers
1260
+ t._debug_info = namespace["debug_info"]
1261
+ t._uptodate = None
1262
+
1263
+ # store the reference
1264
+ namespace["environment"] = environment
1265
+ namespace["__jinja_template__"] = t
1266
+
1267
+ return t
1268
+
1269
+ def render(self, *args: t.Any, **kwargs: t.Any) -> str:
1270
+ """This method accepts the same arguments as the `dict` constructor:
1271
+ A dict, a dict subclass or some keyword arguments. If no arguments
1272
+ are given the context will be empty. These two calls do the same::
1273
+
1274
+ template.render(knights='that say nih')
1275
+ template.render({'knights': 'that say nih'})
1276
+
1277
+ This will return the rendered template as a string.
1278
+ """
1279
+ if self.environment.is_async:
1280
+ import asyncio
1281
+
1282
+ close = False
1283
+
1284
+ try:
1285
+ loop = asyncio.get_running_loop()
1286
+ except RuntimeError:
1287
+ loop = asyncio.new_event_loop()
1288
+ close = True
1289
+
1290
+ try:
1291
+ return loop.run_until_complete(self.render_async(*args, **kwargs))
1292
+ finally:
1293
+ if close:
1294
+ loop.close()
1295
+
1296
+ ctx = self.new_context(dict(*args, **kwargs))
1297
+
1298
+ try:
1299
+ return self.environment.concat(self.root_render_func(ctx)) # type: ignore
1300
+ except Exception:
1301
+ self.environment.handle_exception()
1302
+
1303
+ async def render_async(self, *args: t.Any, **kwargs: t.Any) -> str:
1304
+ """This works similar to :meth:`render` but returns a coroutine
1305
+ that when awaited returns the entire rendered template string. This
1306
+ requires the async feature to be enabled.
1307
+
1308
+ Example usage::
1309
+
1310
+ await template.render_async(knights='that say nih; asynchronously')
1311
+ """
1312
+ if not self.environment.is_async:
1313
+ raise RuntimeError(
1314
+ "The environment was not created with async mode enabled."
1315
+ )
1316
+
1317
+ ctx = self.new_context(dict(*args, **kwargs))
1318
+
1319
+ try:
1320
+ return self.environment.concat( # type: ignore
1321
+ [n async for n in self.root_render_func(ctx)] # type: ignore
1322
+ )
1323
+ except Exception:
1324
+ return self.environment.handle_exception()
1325
+
1326
+ def stream(self, *args: t.Any, **kwargs: t.Any) -> "TemplateStream":
1327
+ """Works exactly like :meth:`generate` but returns a
1328
+ :class:`TemplateStream`.
1329
+ """
1330
+ return TemplateStream(self.generate(*args, **kwargs))
1331
+
1332
+ def generate(self, *args: t.Any, **kwargs: t.Any) -> t.Iterator[str]:
1333
+ """For very large templates it can be useful to not render the whole
1334
+ template at once but evaluate each statement after another and yield
1335
+ piece for piece. This method basically does exactly that and returns
1336
+ a generator that yields one item after another as strings.
1337
+
1338
+ It accepts the same arguments as :meth:`render`.
1339
+ """
1340
+ if self.environment.is_async:
1341
+ import asyncio
1342
+
1343
+ async def to_list() -> t.List[str]:
1344
+ return [x async for x in self.generate_async(*args, **kwargs)]
1345
+
1346
+ yield from asyncio.run(to_list())
1347
+ return
1348
+
1349
+ ctx = self.new_context(dict(*args, **kwargs))
1350
+
1351
+ try:
1352
+ yield from self.root_render_func(ctx)
1353
+ except Exception:
1354
+ yield self.environment.handle_exception()
1355
+
1356
+ async def generate_async(
1357
+ self, *args: t.Any, **kwargs: t.Any
1358
+ ) -> t.AsyncIterator[str]:
1359
+ """An async version of :meth:`generate`. Works very similarly but
1360
+ returns an async iterator instead.
1361
+ """
1362
+ if not self.environment.is_async:
1363
+ raise RuntimeError(
1364
+ "The environment was not created with async mode enabled."
1365
+ )
1366
+
1367
+ ctx = self.new_context(dict(*args, **kwargs))
1368
+
1369
+ try:
1370
+ async for event in self.root_render_func(ctx): # type: ignore
1371
+ yield event
1372
+ except Exception:
1373
+ yield self.environment.handle_exception()
1374
+
1375
+ def new_context(
1376
+ self,
1377
+ vars: t.Optional[t.Dict[str, t.Any]] = None,
1378
+ shared: bool = False,
1379
+ locals: t.Optional[t.Mapping[str, t.Any]] = None,
1380
+ ) -> Context:
1381
+ """Create a new :class:`Context` for this template. The vars
1382
+ provided will be passed to the template. Per default the globals
1383
+ are added to the context. If shared is set to `True` the data
1384
+ is passed as is to the context without adding the globals.
1385
+
1386
+ `locals` can be a dict of local variables for internal usage.
1387
+ """
1388
+ return new_context(
1389
+ self.environment, self.name, self.blocks, vars, shared, self.globals, locals
1390
+ )
1391
+
1392
+ def make_module(
1393
+ self,
1394
+ vars: t.Optional[t.Dict[str, t.Any]] = None,
1395
+ shared: bool = False,
1396
+ locals: t.Optional[t.Mapping[str, t.Any]] = None,
1397
+ ) -> "TemplateModule":
1398
+ """This method works like the :attr:`module` attribute when called
1399
+ without arguments but it will evaluate the template on every call
1400
+ rather than caching it. It's also possible to provide
1401
+ a dict which is then used as context. The arguments are the same
1402
+ as for the :meth:`new_context` method.
1403
+ """
1404
+ ctx = self.new_context(vars, shared, locals)
1405
+ return TemplateModule(self, ctx)
1406
+
1407
+ async def make_module_async(
1408
+ self,
1409
+ vars: t.Optional[t.Dict[str, t.Any]] = None,
1410
+ shared: bool = False,
1411
+ locals: t.Optional[t.Mapping[str, t.Any]] = None,
1412
+ ) -> "TemplateModule":
1413
+ """As template module creation can invoke template code for
1414
+ asynchronous executions this method must be used instead of the
1415
+ normal :meth:`make_module` one. Likewise the module attribute
1416
+ becomes unavailable in async mode.
1417
+ """
1418
+ ctx = self.new_context(vars, shared, locals)
1419
+ return TemplateModule(
1420
+ self, ctx, [x async for x in self.root_render_func(ctx)] # type: ignore
1421
+ )
1422
+
1423
+ @internalcode
1424
+ def _get_default_module(self, ctx: t.Optional[Context] = None) -> "TemplateModule":
1425
+ """If a context is passed in, this means that the template was
1426
+ imported. Imported templates have access to the current
1427
+ template's globals by default, but they can only be accessed via
1428
+ the context during runtime.
1429
+
1430
+ If there are new globals, we need to create a new module because
1431
+ the cached module is already rendered and will not have access
1432
+ to globals from the current context. This new module is not
1433
+ cached because the template can be imported elsewhere, and it
1434
+ should have access to only the current template's globals.
1435
+ """
1436
+ if self.environment.is_async:
1437
+ raise RuntimeError("Module is not available in async mode.")
1438
+
1439
+ if ctx is not None:
1440
+ keys = ctx.globals_keys - self.globals.keys()
1441
+
1442
+ if keys:
1443
+ return self.make_module({k: ctx.parent[k] for k in keys})
1444
+
1445
+ if self._module is None:
1446
+ self._module = self.make_module()
1447
+
1448
+ return self._module
1449
+
1450
+ async def _get_default_module_async(
1451
+ self, ctx: t.Optional[Context] = None
1452
+ ) -> "TemplateModule":
1453
+ if ctx is not None:
1454
+ keys = ctx.globals_keys - self.globals.keys()
1455
+
1456
+ if keys:
1457
+ return await self.make_module_async({k: ctx.parent[k] for k in keys})
1458
+
1459
+ if self._module is None:
1460
+ self._module = await self.make_module_async()
1461
+
1462
+ return self._module
1463
+
1464
+ @property
1465
+ def module(self) -> "TemplateModule":
1466
+ """The template as module. This is used for imports in the
1467
+ template runtime but is also useful if one wants to access
1468
+ exported template variables from the Python layer:
1469
+
1470
+ >>> t = Template('{% macro foo() %}42{% endmacro %}23')
1471
+ >>> str(t.module)
1472
+ '23'
1473
+ >>> t.module.foo() == u'42'
1474
+ True
1475
+
1476
+ This attribute is not available if async mode is enabled.
1477
+ """
1478
+ return self._get_default_module()
1479
+
1480
+ def get_corresponding_lineno(self, lineno: int) -> int:
1481
+ """Return the source line number of a line number in the
1482
+ generated bytecode as they are not in sync.
1483
+ """
1484
+ for template_line, code_line in reversed(self.debug_info):
1485
+ if code_line <= lineno:
1486
+ return template_line
1487
+ return 1
1488
+
1489
+ @property
1490
+ def is_up_to_date(self) -> bool:
1491
+ """If this variable is `False` there is a newer version available."""
1492
+ if self._uptodate is None:
1493
+ return True
1494
+ return self._uptodate()
1495
+
1496
+ @property
1497
+ def debug_info(self) -> t.List[t.Tuple[int, int]]:
1498
+ """The debug info mapping."""
1499
+ if self._debug_info:
1500
+ return [
1501
+ tuple(map(int, x.split("="))) # type: ignore
1502
+ for x in self._debug_info.split("&")
1503
+ ]
1504
+
1505
+ return []
1506
+
1507
+ def __repr__(self) -> str:
1508
+ if self.name is None:
1509
+ name = f"memory:{id(self):x}"
1510
+ else:
1511
+ name = repr(self.name)
1512
+ return f"<{type(self).__name__} {name}>"
1513
+
1514
+
1515
+ class TemplateModule:
1516
+ """Represents an imported template. All the exported names of the
1517
+ template are available as attributes on this object. Additionally
1518
+ converting it into a string renders the contents.
1519
+ """
1520
+
1521
+ def __init__(
1522
+ self,
1523
+ template: Template,
1524
+ context: Context,
1525
+ body_stream: t.Optional[t.Iterable[str]] = None,
1526
+ ) -> None:
1527
+ if body_stream is None:
1528
+ if context.environment.is_async:
1529
+ raise RuntimeError(
1530
+ "Async mode requires a body stream to be passed to"
1531
+ " a template module. Use the async methods of the"
1532
+ " API you are using."
1533
+ )
1534
+
1535
+ body_stream = list(template.root_render_func(context))
1536
+
1537
+ self._body_stream = body_stream
1538
+ self.__dict__.update(context.get_exported())
1539
+ self.__name__ = template.name
1540
+
1541
+ def __html__(self) -> Markup:
1542
+ return Markup(concat(self._body_stream))
1543
+
1544
+ def __str__(self) -> str:
1545
+ return concat(self._body_stream)
1546
+
1547
+ def __repr__(self) -> str:
1548
+ if self.__name__ is None:
1549
+ name = f"memory:{id(self):x}"
1550
+ else:
1551
+ name = repr(self.__name__)
1552
+ return f"<{type(self).__name__} {name}>"
1553
+
1554
+
1555
+ class TemplateExpression:
1556
+ """The :meth:`jinja2.Environment.compile_expression` method returns an
1557
+ instance of this object. It encapsulates the expression-like access
1558
+ to the template with an expression it wraps.
1559
+ """
1560
+
1561
+ def __init__(self, template: Template, undefined_to_none: bool) -> None:
1562
+ self._template = template
1563
+ self._undefined_to_none = undefined_to_none
1564
+
1565
+ def __call__(self, *args: t.Any, **kwargs: t.Any) -> t.Optional[t.Any]:
1566
+ context = self._template.new_context(dict(*args, **kwargs))
1567
+ consume(self._template.root_render_func(context))
1568
+ rv = context.vars["result"]
1569
+ if self._undefined_to_none and isinstance(rv, Undefined):
1570
+ rv = None
1571
+ return rv
1572
+
1573
+
1574
+ class TemplateStream:
1575
+ """A template stream works pretty much like an ordinary python generator
1576
+ but it can buffer multiple items to reduce the number of total iterations.
1577
+ Per default the output is unbuffered which means that for every unbuffered
1578
+ instruction in the template one string is yielded.
1579
+
1580
+ If buffering is enabled with a buffer size of 5, five items are combined
1581
+ into a new string. This is mainly useful if you are streaming
1582
+ big templates to a client via WSGI which flushes after each iteration.
1583
+ """
1584
+
1585
+ def __init__(self, gen: t.Iterator[str]) -> None:
1586
+ self._gen = gen
1587
+ self.disable_buffering()
1588
+
1589
+ def dump(
1590
+ self,
1591
+ fp: t.Union[str, t.IO],
1592
+ encoding: t.Optional[str] = None,
1593
+ errors: t.Optional[str] = "strict",
1594
+ ) -> None:
1595
+ """Dump the complete stream into a file or file-like object.
1596
+ Per default strings are written, if you want to encode
1597
+ before writing specify an `encoding`.
1598
+
1599
+ Example usage::
1600
+
1601
+ Template('Hello {{ name }}!').stream(name='foo').dump('hello.html')
1602
+ """
1603
+ close = False
1604
+
1605
+ if isinstance(fp, str):
1606
+ if encoding is None:
1607
+ encoding = "utf-8"
1608
+
1609
+ fp = open(fp, "wb")
1610
+ close = True
1611
+ try:
1612
+ if encoding is not None:
1613
+ iterable = (x.encode(encoding, errors) for x in self) # type: ignore
1614
+ else:
1615
+ iterable = self # type: ignore
1616
+
1617
+ if hasattr(fp, "writelines"):
1618
+ fp.writelines(iterable)
1619
+ else:
1620
+ for item in iterable:
1621
+ fp.write(item)
1622
+ finally:
1623
+ if close:
1624
+ fp.close()
1625
+
1626
+ def disable_buffering(self) -> None:
1627
+ """Disable the output buffering."""
1628
+ self._next = partial(next, self._gen)
1629
+ self.buffered = False
1630
+
1631
+ def _buffered_generator(self, size: int) -> t.Iterator[str]:
1632
+ buf: t.List[str] = []
1633
+ c_size = 0
1634
+ push = buf.append
1635
+
1636
+ while True:
1637
+ try:
1638
+ while c_size < size:
1639
+ c = next(self._gen)
1640
+ push(c)
1641
+ if c:
1642
+ c_size += 1
1643
+ except StopIteration:
1644
+ if not c_size:
1645
+ return
1646
+ yield concat(buf)
1647
+ del buf[:]
1648
+ c_size = 0
1649
+
1650
+ def enable_buffering(self, size: int = 5) -> None:
1651
+ """Enable buffering. Buffer `size` items before yielding them."""
1652
+ if size <= 1:
1653
+ raise ValueError("buffer size too small")
1654
+
1655
+ self.buffered = True
1656
+ self._next = partial(next, self._buffered_generator(size))
1657
+
1658
+ def __iter__(self) -> "TemplateStream":
1659
+ return self
1660
+
1661
+ def __next__(self) -> str:
1662
+ return self._next() # type: ignore
1663
+
1664
+
1665
+ # hook in default template class. if anyone reads this comment: ignore that
1666
+ # it's possible to use custom templates ;-)
1667
+ Environment.template_class = Template
tuning-competition-baseline/.venv/lib/python3.11/site-packages/jinja2/loaders.py ADDED
@@ -0,0 +1,661 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """API and implementations for loading templates from different data
2
+ sources.
3
+ """
4
+ import importlib.util
5
+ import os
6
+ import posixpath
7
+ import sys
8
+ import typing as t
9
+ import weakref
10
+ import zipimport
11
+ from collections import abc
12
+ from hashlib import sha1
13
+ from importlib import import_module
14
+ from types import ModuleType
15
+
16
+ from .exceptions import TemplateNotFound
17
+ from .utils import internalcode
18
+
19
+ if t.TYPE_CHECKING:
20
+ from .environment import Environment
21
+ from .environment import Template
22
+
23
+
24
+ def split_template_path(template: str) -> t.List[str]:
25
+ """Split a path into segments and perform a sanity check. If it detects
26
+ '..' in the path it will raise a `TemplateNotFound` error.
27
+ """
28
+ pieces = []
29
+ for piece in template.split("/"):
30
+ if (
31
+ os.path.sep in piece
32
+ or (os.path.altsep and os.path.altsep in piece)
33
+ or piece == os.path.pardir
34
+ ):
35
+ raise TemplateNotFound(template)
36
+ elif piece and piece != ".":
37
+ pieces.append(piece)
38
+ return pieces
39
+
40
+
41
+ class BaseLoader:
42
+ """Baseclass for all loaders. Subclass this and override `get_source` to
43
+ implement a custom loading mechanism. The environment provides a
44
+ `get_template` method that calls the loader's `load` method to get the
45
+ :class:`Template` object.
46
+
47
+ A very basic example for a loader that looks up templates on the file
48
+ system could look like this::
49
+
50
+ from jinja2 import BaseLoader, TemplateNotFound
51
+ from os.path import join, exists, getmtime
52
+
53
+ class MyLoader(BaseLoader):
54
+
55
+ def __init__(self, path):
56
+ self.path = path
57
+
58
+ def get_source(self, environment, template):
59
+ path = join(self.path, template)
60
+ if not exists(path):
61
+ raise TemplateNotFound(template)
62
+ mtime = getmtime(path)
63
+ with open(path) as f:
64
+ source = f.read()
65
+ return source, path, lambda: mtime == getmtime(path)
66
+ """
67
+
68
+ #: if set to `False` it indicates that the loader cannot provide access
69
+ #: to the source of templates.
70
+ #:
71
+ #: .. versionadded:: 2.4
72
+ has_source_access = True
73
+
74
+ def get_source(
75
+ self, environment: "Environment", template: str
76
+ ) -> t.Tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]:
77
+ """Get the template source, filename and reload helper for a template.
78
+ It's passed the environment and template name and has to return a
79
+ tuple in the form ``(source, filename, uptodate)`` or raise a
80
+ `TemplateNotFound` error if it can't locate the template.
81
+
82
+ The source part of the returned tuple must be the source of the
83
+ template as a string. The filename should be the name of the
84
+ file on the filesystem if it was loaded from there, otherwise
85
+ ``None``. The filename is used by Python for the tracebacks
86
+ if no loader extension is used.
87
+
88
+ The last item in the tuple is the `uptodate` function. If auto
89
+ reloading is enabled it's always called to check if the template
90
+ changed. No arguments are passed so the function must store the
91
+ old state somewhere (for example in a closure). If it returns `False`
92
+ the template will be reloaded.
93
+ """
94
+ if not self.has_source_access:
95
+ raise RuntimeError(
96
+ f"{type(self).__name__} cannot provide access to the source"
97
+ )
98
+ raise TemplateNotFound(template)
99
+
100
+ def list_templates(self) -> t.List[str]:
101
+ """Iterates over all templates. If the loader does not support that
102
+ it should raise a :exc:`TypeError` which is the default behavior.
103
+ """
104
+ raise TypeError("this loader cannot iterate over all templates")
105
+
106
+ @internalcode
107
+ def load(
108
+ self,
109
+ environment: "Environment",
110
+ name: str,
111
+ globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
112
+ ) -> "Template":
113
+ """Loads a template. This method looks up the template in the cache
114
+ or loads one by calling :meth:`get_source`. Subclasses should not
115
+ override this method as loaders working on collections of other
116
+ loaders (such as :class:`PrefixLoader` or :class:`ChoiceLoader`)
117
+ will not call this method but `get_source` directly.
118
+ """
119
+ code = None
120
+ if globals is None:
121
+ globals = {}
122
+
123
+ # first we try to get the source for this template together
124
+ # with the filename and the uptodate function.
125
+ source, filename, uptodate = self.get_source(environment, name)
126
+
127
+ # try to load the code from the bytecode cache if there is a
128
+ # bytecode cache configured.
129
+ bcc = environment.bytecode_cache
130
+ if bcc is not None:
131
+ bucket = bcc.get_bucket(environment, name, filename, source)
132
+ code = bucket.code
133
+
134
+ # if we don't have code so far (not cached, no longer up to
135
+ # date) etc. we compile the template
136
+ if code is None:
137
+ code = environment.compile(source, name, filename)
138
+
139
+ # if the bytecode cache is available and the bucket doesn't
140
+ # have a code so far, we give the bucket the new code and put
141
+ # it back to the bytecode cache.
142
+ if bcc is not None and bucket.code is None:
143
+ bucket.code = code
144
+ bcc.set_bucket(bucket)
145
+
146
+ return environment.template_class.from_code(
147
+ environment, code, globals, uptodate
148
+ )
149
+
150
+
151
+ class FileSystemLoader(BaseLoader):
152
+ """Load templates from a directory in the file system.
153
+
154
+ The path can be relative or absolute. Relative paths are relative to
155
+ the current working directory.
156
+
157
+ .. code-block:: python
158
+
159
+ loader = FileSystemLoader("templates")
160
+
161
+ A list of paths can be given. The directories will be searched in
162
+ order, stopping at the first matching template.
163
+
164
+ .. code-block:: python
165
+
166
+ loader = FileSystemLoader(["/override/templates", "/default/templates"])
167
+
168
+ :param searchpath: A path, or list of paths, to the directory that
169
+ contains the templates.
170
+ :param encoding: Use this encoding to read the text from template
171
+ files.
172
+ :param followlinks: Follow symbolic links in the path.
173
+
174
+ .. versionchanged:: 2.8
175
+ Added the ``followlinks`` parameter.
176
+ """
177
+
178
+ def __init__(
179
+ self,
180
+ searchpath: t.Union[str, os.PathLike, t.Sequence[t.Union[str, os.PathLike]]],
181
+ encoding: str = "utf-8",
182
+ followlinks: bool = False,
183
+ ) -> None:
184
+ if not isinstance(searchpath, abc.Iterable) or isinstance(searchpath, str):
185
+ searchpath = [searchpath]
186
+
187
+ self.searchpath = [os.fspath(p) for p in searchpath]
188
+ self.encoding = encoding
189
+ self.followlinks = followlinks
190
+
191
+ def get_source(
192
+ self, environment: "Environment", template: str
193
+ ) -> t.Tuple[str, str, t.Callable[[], bool]]:
194
+ pieces = split_template_path(template)
195
+
196
+ for searchpath in self.searchpath:
197
+ # Use posixpath even on Windows to avoid "drive:" or UNC
198
+ # segments breaking out of the search directory.
199
+ filename = posixpath.join(searchpath, *pieces)
200
+
201
+ if os.path.isfile(filename):
202
+ break
203
+ else:
204
+ raise TemplateNotFound(template)
205
+
206
+ with open(filename, encoding=self.encoding) as f:
207
+ contents = f.read()
208
+
209
+ mtime = os.path.getmtime(filename)
210
+
211
+ def uptodate() -> bool:
212
+ try:
213
+ return os.path.getmtime(filename) == mtime
214
+ except OSError:
215
+ return False
216
+
217
+ # Use normpath to convert Windows altsep to sep.
218
+ return contents, os.path.normpath(filename), uptodate
219
+
220
+ def list_templates(self) -> t.List[str]:
221
+ found = set()
222
+ for searchpath in self.searchpath:
223
+ walk_dir = os.walk(searchpath, followlinks=self.followlinks)
224
+ for dirpath, _, filenames in walk_dir:
225
+ for filename in filenames:
226
+ template = (
227
+ os.path.join(dirpath, filename)[len(searchpath) :]
228
+ .strip(os.path.sep)
229
+ .replace(os.path.sep, "/")
230
+ )
231
+ if template[:2] == "./":
232
+ template = template[2:]
233
+ if template not in found:
234
+ found.add(template)
235
+ return sorted(found)
236
+
237
+
238
+ class PackageLoader(BaseLoader):
239
+ """Load templates from a directory in a Python package.
240
+
241
+ :param package_name: Import name of the package that contains the
242
+ template directory.
243
+ :param package_path: Directory within the imported package that
244
+ contains the templates.
245
+ :param encoding: Encoding of template files.
246
+
247
+ The following example looks up templates in the ``pages`` directory
248
+ within the ``project.ui`` package.
249
+
250
+ .. code-block:: python
251
+
252
+ loader = PackageLoader("project.ui", "pages")
253
+
254
+ Only packages installed as directories (standard pip behavior) or
255
+ zip/egg files (less common) are supported. The Python API for
256
+ introspecting data in packages is too limited to support other
257
+ installation methods the way this loader requires.
258
+
259
+ There is limited support for :pep:`420` namespace packages. The
260
+ template directory is assumed to only be in one namespace
261
+ contributor. Zip files contributing to a namespace are not
262
+ supported.
263
+
264
+ .. versionchanged:: 3.0
265
+ No longer uses ``setuptools`` as a dependency.
266
+
267
+ .. versionchanged:: 3.0
268
+ Limited PEP 420 namespace package support.
269
+ """
270
+
271
+ def __init__(
272
+ self,
273
+ package_name: str,
274
+ package_path: "str" = "templates",
275
+ encoding: str = "utf-8",
276
+ ) -> None:
277
+ package_path = os.path.normpath(package_path).rstrip(os.path.sep)
278
+
279
+ # normpath preserves ".", which isn't valid in zip paths.
280
+ if package_path == os.path.curdir:
281
+ package_path = ""
282
+ elif package_path[:2] == os.path.curdir + os.path.sep:
283
+ package_path = package_path[2:]
284
+
285
+ self.package_path = package_path
286
+ self.package_name = package_name
287
+ self.encoding = encoding
288
+
289
+ # Make sure the package exists. This also makes namespace
290
+ # packages work, otherwise get_loader returns None.
291
+ import_module(package_name)
292
+ spec = importlib.util.find_spec(package_name)
293
+ assert spec is not None, "An import spec was not found for the package."
294
+ loader = spec.loader
295
+ assert loader is not None, "A loader was not found for the package."
296
+ self._loader = loader
297
+ self._archive = None
298
+ template_root = None
299
+
300
+ if isinstance(loader, zipimport.zipimporter):
301
+ self._archive = loader.archive
302
+ pkgdir = next(iter(spec.submodule_search_locations)) # type: ignore
303
+ template_root = os.path.join(pkgdir, package_path).rstrip(os.path.sep)
304
+ else:
305
+ roots: t.List[str] = []
306
+
307
+ # One element for regular packages, multiple for namespace
308
+ # packages, or None for single module file.
309
+ if spec.submodule_search_locations:
310
+ roots.extend(spec.submodule_search_locations)
311
+ # A single module file, use the parent directory instead.
312
+ elif spec.origin is not None:
313
+ roots.append(os.path.dirname(spec.origin))
314
+
315
+ for root in roots:
316
+ root = os.path.join(root, package_path)
317
+
318
+ if os.path.isdir(root):
319
+ template_root = root
320
+ break
321
+
322
+ if template_root is None:
323
+ raise ValueError(
324
+ f"The {package_name!r} package was not installed in a"
325
+ " way that PackageLoader understands."
326
+ )
327
+
328
+ self._template_root = template_root
329
+
330
+ def get_source(
331
+ self, environment: "Environment", template: str
332
+ ) -> t.Tuple[str, str, t.Optional[t.Callable[[], bool]]]:
333
+ # Use posixpath even on Windows to avoid "drive:" or UNC
334
+ # segments breaking out of the search directory. Use normpath to
335
+ # convert Windows altsep to sep.
336
+ p = os.path.normpath(
337
+ posixpath.join(self._template_root, *split_template_path(template))
338
+ )
339
+ up_to_date: t.Optional[t.Callable[[], bool]]
340
+
341
+ if self._archive is None:
342
+ # Package is a directory.
343
+ if not os.path.isfile(p):
344
+ raise TemplateNotFound(template)
345
+
346
+ with open(p, "rb") as f:
347
+ source = f.read()
348
+
349
+ mtime = os.path.getmtime(p)
350
+
351
+ def up_to_date() -> bool:
352
+ return os.path.isfile(p) and os.path.getmtime(p) == mtime
353
+
354
+ else:
355
+ # Package is a zip file.
356
+ try:
357
+ source = self._loader.get_data(p) # type: ignore
358
+ except OSError as e:
359
+ raise TemplateNotFound(template) from e
360
+
361
+ # Could use the zip's mtime for all template mtimes, but
362
+ # would need to safely reload the module if it's out of
363
+ # date, so just report it as always current.
364
+ up_to_date = None
365
+
366
+ return source.decode(self.encoding), p, up_to_date
367
+
368
+ def list_templates(self) -> t.List[str]:
369
+ results: t.List[str] = []
370
+
371
+ if self._archive is None:
372
+ # Package is a directory.
373
+ offset = len(self._template_root)
374
+
375
+ for dirpath, _, filenames in os.walk(self._template_root):
376
+ dirpath = dirpath[offset:].lstrip(os.path.sep)
377
+ results.extend(
378
+ os.path.join(dirpath, name).replace(os.path.sep, "/")
379
+ for name in filenames
380
+ )
381
+ else:
382
+ if not hasattr(self._loader, "_files"):
383
+ raise TypeError(
384
+ "This zip import does not have the required"
385
+ " metadata to list templates."
386
+ )
387
+
388
+ # Package is a zip file.
389
+ prefix = (
390
+ self._template_root[len(self._archive) :].lstrip(os.path.sep)
391
+ + os.path.sep
392
+ )
393
+ offset = len(prefix)
394
+
395
+ for name in self._loader._files.keys():
396
+ # Find names under the templates directory that aren't directories.
397
+ if name.startswith(prefix) and name[-1] != os.path.sep:
398
+ results.append(name[offset:].replace(os.path.sep, "/"))
399
+
400
+ results.sort()
401
+ return results
402
+
403
+
404
+ class DictLoader(BaseLoader):
405
+ """Loads a template from a Python dict mapping template names to
406
+ template source. This loader is useful for unittesting:
407
+
408
+ >>> loader = DictLoader({'index.html': 'source here'})
409
+
410
+ Because auto reloading is rarely useful this is disabled per default.
411
+ """
412
+
413
+ def __init__(self, mapping: t.Mapping[str, str]) -> None:
414
+ self.mapping = mapping
415
+
416
+ def get_source(
417
+ self, environment: "Environment", template: str
418
+ ) -> t.Tuple[str, None, t.Callable[[], bool]]:
419
+ if template in self.mapping:
420
+ source = self.mapping[template]
421
+ return source, None, lambda: source == self.mapping.get(template)
422
+ raise TemplateNotFound(template)
423
+
424
+ def list_templates(self) -> t.List[str]:
425
+ return sorted(self.mapping)
426
+
427
+
428
+ class FunctionLoader(BaseLoader):
429
+ """A loader that is passed a function which does the loading. The
430
+ function receives the name of the template and has to return either
431
+ a string with the template source, a tuple in the form ``(source,
432
+ filename, uptodatefunc)`` or `None` if the template does not exist.
433
+
434
+ >>> def load_template(name):
435
+ ... if name == 'index.html':
436
+ ... return '...'
437
+ ...
438
+ >>> loader = FunctionLoader(load_template)
439
+
440
+ The `uptodatefunc` is a function that is called if autoreload is enabled
441
+ and has to return `True` if the template is still up to date. For more
442
+ details have a look at :meth:`BaseLoader.get_source` which has the same
443
+ return value.
444
+ """
445
+
446
+ def __init__(
447
+ self,
448
+ load_func: t.Callable[
449
+ [str],
450
+ t.Optional[
451
+ t.Union[
452
+ str, t.Tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]
453
+ ]
454
+ ],
455
+ ],
456
+ ) -> None:
457
+ self.load_func = load_func
458
+
459
+ def get_source(
460
+ self, environment: "Environment", template: str
461
+ ) -> t.Tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]:
462
+ rv = self.load_func(template)
463
+
464
+ if rv is None:
465
+ raise TemplateNotFound(template)
466
+
467
+ if isinstance(rv, str):
468
+ return rv, None, None
469
+
470
+ return rv
471
+
472
+
473
+ class PrefixLoader(BaseLoader):
474
+ """A loader that is passed a dict of loaders where each loader is bound
475
+ to a prefix. The prefix is delimited from the template by a slash per
476
+ default, which can be changed by setting the `delimiter` argument to
477
+ something else::
478
+
479
+ loader = PrefixLoader({
480
+ 'app1': PackageLoader('mypackage.app1'),
481
+ 'app2': PackageLoader('mypackage.app2')
482
+ })
483
+
484
+ By loading ``'app1/index.html'`` the file from the app1 package is loaded,
485
+ by loading ``'app2/index.html'`` the file from the second.
486
+ """
487
+
488
+ def __init__(
489
+ self, mapping: t.Mapping[str, BaseLoader], delimiter: str = "/"
490
+ ) -> None:
491
+ self.mapping = mapping
492
+ self.delimiter = delimiter
493
+
494
+ def get_loader(self, template: str) -> t.Tuple[BaseLoader, str]:
495
+ try:
496
+ prefix, name = template.split(self.delimiter, 1)
497
+ loader = self.mapping[prefix]
498
+ except (ValueError, KeyError) as e:
499
+ raise TemplateNotFound(template) from e
500
+ return loader, name
501
+
502
+ def get_source(
503
+ self, environment: "Environment", template: str
504
+ ) -> t.Tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]:
505
+ loader, name = self.get_loader(template)
506
+ try:
507
+ return loader.get_source(environment, name)
508
+ except TemplateNotFound as e:
509
+ # re-raise the exception with the correct filename here.
510
+ # (the one that includes the prefix)
511
+ raise TemplateNotFound(template) from e
512
+
513
+ @internalcode
514
+ def load(
515
+ self,
516
+ environment: "Environment",
517
+ name: str,
518
+ globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
519
+ ) -> "Template":
520
+ loader, local_name = self.get_loader(name)
521
+ try:
522
+ return loader.load(environment, local_name, globals)
523
+ except TemplateNotFound as e:
524
+ # re-raise the exception with the correct filename here.
525
+ # (the one that includes the prefix)
526
+ raise TemplateNotFound(name) from e
527
+
528
+ def list_templates(self) -> t.List[str]:
529
+ result = []
530
+ for prefix, loader in self.mapping.items():
531
+ for template in loader.list_templates():
532
+ result.append(prefix + self.delimiter + template)
533
+ return result
534
+
535
+
536
+ class ChoiceLoader(BaseLoader):
537
+ """This loader works like the `PrefixLoader` just that no prefix is
538
+ specified. If a template could not be found by one loader the next one
539
+ is tried.
540
+
541
+ >>> loader = ChoiceLoader([
542
+ ... FileSystemLoader('/path/to/user/templates'),
543
+ ... FileSystemLoader('/path/to/system/templates')
544
+ ... ])
545
+
546
+ This is useful if you want to allow users to override builtin templates
547
+ from a different location.
548
+ """
549
+
550
+ def __init__(self, loaders: t.Sequence[BaseLoader]) -> None:
551
+ self.loaders = loaders
552
+
553
+ def get_source(
554
+ self, environment: "Environment", template: str
555
+ ) -> t.Tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]:
556
+ for loader in self.loaders:
557
+ try:
558
+ return loader.get_source(environment, template)
559
+ except TemplateNotFound:
560
+ pass
561
+ raise TemplateNotFound(template)
562
+
563
+ @internalcode
564
+ def load(
565
+ self,
566
+ environment: "Environment",
567
+ name: str,
568
+ globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
569
+ ) -> "Template":
570
+ for loader in self.loaders:
571
+ try:
572
+ return loader.load(environment, name, globals)
573
+ except TemplateNotFound:
574
+ pass
575
+ raise TemplateNotFound(name)
576
+
577
+ def list_templates(self) -> t.List[str]:
578
+ found = set()
579
+ for loader in self.loaders:
580
+ found.update(loader.list_templates())
581
+ return sorted(found)
582
+
583
+
584
+ class _TemplateModule(ModuleType):
585
+ """Like a normal module but with support for weak references"""
586
+
587
+
588
+ class ModuleLoader(BaseLoader):
589
+ """This loader loads templates from precompiled templates.
590
+
591
+ Example usage:
592
+
593
+ >>> loader = ChoiceLoader([
594
+ ... ModuleLoader('/path/to/compiled/templates'),
595
+ ... FileSystemLoader('/path/to/templates')
596
+ ... ])
597
+
598
+ Templates can be precompiled with :meth:`Environment.compile_templates`.
599
+ """
600
+
601
+ has_source_access = False
602
+
603
+ def __init__(
604
+ self, path: t.Union[str, os.PathLike, t.Sequence[t.Union[str, os.PathLike]]]
605
+ ) -> None:
606
+ package_name = f"_jinja2_module_templates_{id(self):x}"
607
+
608
+ # create a fake module that looks for the templates in the
609
+ # path given.
610
+ mod = _TemplateModule(package_name)
611
+
612
+ if not isinstance(path, abc.Iterable) or isinstance(path, str):
613
+ path = [path]
614
+
615
+ mod.__path__ = [os.fspath(p) for p in path]
616
+
617
+ sys.modules[package_name] = weakref.proxy(
618
+ mod, lambda x: sys.modules.pop(package_name, None)
619
+ )
620
+
621
+ # the only strong reference, the sys.modules entry is weak
622
+ # so that the garbage collector can remove it once the
623
+ # loader that created it goes out of business.
624
+ self.module = mod
625
+ self.package_name = package_name
626
+
627
+ @staticmethod
628
+ def get_template_key(name: str) -> str:
629
+ return "tmpl_" + sha1(name.encode("utf-8")).hexdigest()
630
+
631
+ @staticmethod
632
+ def get_module_filename(name: str) -> str:
633
+ return ModuleLoader.get_template_key(name) + ".py"
634
+
635
+ @internalcode
636
+ def load(
637
+ self,
638
+ environment: "Environment",
639
+ name: str,
640
+ globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
641
+ ) -> "Template":
642
+ key = self.get_template_key(name)
643
+ module = f"{self.package_name}.{key}"
644
+ mod = getattr(self.module, module, None)
645
+
646
+ if mod is None:
647
+ try:
648
+ mod = __import__(module, None, None, ["root"])
649
+ except ImportError as e:
650
+ raise TemplateNotFound(name) from e
651
+
652
+ # remove the entry from sys.modules, we only want the attribute
653
+ # on the module object we have stored on the loader.
654
+ sys.modules.pop(module, None)
655
+
656
+ if globals is None:
657
+ globals = {}
658
+
659
+ return environment.template_class.from_module_dict(
660
+ environment, mod.__dict__, globals
661
+ )
tuning-competition-baseline/.venv/lib/python3.11/site-packages/jinja2/parser.py ADDED
@@ -0,0 +1,1034 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Parse tokens from the lexer into nodes for the compiler."""
2
+ import typing
3
+ import typing as t
4
+
5
+ from . import nodes
6
+ from .exceptions import TemplateAssertionError
7
+ from .exceptions import TemplateSyntaxError
8
+ from .lexer import describe_token
9
+ from .lexer import describe_token_expr
10
+
11
+ if t.TYPE_CHECKING:
12
+ import typing_extensions as te
13
+ from .environment import Environment
14
+
15
+ _ImportInclude = t.TypeVar("_ImportInclude", nodes.Import, nodes.Include)
16
+ _MacroCall = t.TypeVar("_MacroCall", nodes.Macro, nodes.CallBlock)
17
+
18
+ _statement_keywords = frozenset(
19
+ [
20
+ "for",
21
+ "if",
22
+ "block",
23
+ "extends",
24
+ "print",
25
+ "macro",
26
+ "include",
27
+ "from",
28
+ "import",
29
+ "set",
30
+ "with",
31
+ "autoescape",
32
+ ]
33
+ )
34
+ _compare_operators = frozenset(["eq", "ne", "lt", "lteq", "gt", "gteq"])
35
+
36
+ _math_nodes: t.Dict[str, t.Type[nodes.Expr]] = {
37
+ "add": nodes.Add,
38
+ "sub": nodes.Sub,
39
+ "mul": nodes.Mul,
40
+ "div": nodes.Div,
41
+ "floordiv": nodes.FloorDiv,
42
+ "mod": nodes.Mod,
43
+ }
44
+
45
+
46
+ class Parser:
47
+ """This is the central parsing class Jinja uses. It's passed to
48
+ extensions and can be used to parse expressions or statements.
49
+ """
50
+
51
+ def __init__(
52
+ self,
53
+ environment: "Environment",
54
+ source: str,
55
+ name: t.Optional[str] = None,
56
+ filename: t.Optional[str] = None,
57
+ state: t.Optional[str] = None,
58
+ ) -> None:
59
+ self.environment = environment
60
+ self.stream = environment._tokenize(source, name, filename, state)
61
+ self.name = name
62
+ self.filename = filename
63
+ self.closed = False
64
+ self.extensions: t.Dict[
65
+ str, t.Callable[["Parser"], t.Union[nodes.Node, t.List[nodes.Node]]]
66
+ ] = {}
67
+ for extension in environment.iter_extensions():
68
+ for tag in extension.tags:
69
+ self.extensions[tag] = extension.parse
70
+ self._last_identifier = 0
71
+ self._tag_stack: t.List[str] = []
72
+ self._end_token_stack: t.List[t.Tuple[str, ...]] = []
73
+
74
+ def fail(
75
+ self,
76
+ msg: str,
77
+ lineno: t.Optional[int] = None,
78
+ exc: t.Type[TemplateSyntaxError] = TemplateSyntaxError,
79
+ ) -> "te.NoReturn":
80
+ """Convenience method that raises `exc` with the message, passed
81
+ line number or last line number as well as the current name and
82
+ filename.
83
+ """
84
+ if lineno is None:
85
+ lineno = self.stream.current.lineno
86
+ raise exc(msg, lineno, self.name, self.filename)
87
+
88
+ def _fail_ut_eof(
89
+ self,
90
+ name: t.Optional[str],
91
+ end_token_stack: t.List[t.Tuple[str, ...]],
92
+ lineno: t.Optional[int],
93
+ ) -> "te.NoReturn":
94
+ expected: t.Set[str] = set()
95
+ for exprs in end_token_stack:
96
+ expected.update(map(describe_token_expr, exprs))
97
+ if end_token_stack:
98
+ currently_looking: t.Optional[str] = " or ".join(
99
+ map(repr, map(describe_token_expr, end_token_stack[-1]))
100
+ )
101
+ else:
102
+ currently_looking = None
103
+
104
+ if name is None:
105
+ message = ["Unexpected end of template."]
106
+ else:
107
+ message = [f"Encountered unknown tag {name!r}."]
108
+
109
+ if currently_looking:
110
+ if name is not None and name in expected:
111
+ message.append(
112
+ "You probably made a nesting mistake. Jinja is expecting this tag,"
113
+ f" but currently looking for {currently_looking}."
114
+ )
115
+ else:
116
+ message.append(
117
+ f"Jinja was looking for the following tags: {currently_looking}."
118
+ )
119
+
120
+ if self._tag_stack:
121
+ message.append(
122
+ "The innermost block that needs to be closed is"
123
+ f" {self._tag_stack[-1]!r}."
124
+ )
125
+
126
+ self.fail(" ".join(message), lineno)
127
+
128
+ def fail_unknown_tag(
129
+ self, name: str, lineno: t.Optional[int] = None
130
+ ) -> "te.NoReturn":
131
+ """Called if the parser encounters an unknown tag. Tries to fail
132
+ with a human readable error message that could help to identify
133
+ the problem.
134
+ """
135
+ self._fail_ut_eof(name, self._end_token_stack, lineno)
136
+
137
+ def fail_eof(
138
+ self,
139
+ end_tokens: t.Optional[t.Tuple[str, ...]] = None,
140
+ lineno: t.Optional[int] = None,
141
+ ) -> "te.NoReturn":
142
+ """Like fail_unknown_tag but for end of template situations."""
143
+ stack = list(self._end_token_stack)
144
+ if end_tokens is not None:
145
+ stack.append(end_tokens)
146
+ self._fail_ut_eof(None, stack, lineno)
147
+
148
+ def is_tuple_end(
149
+ self, extra_end_rules: t.Optional[t.Tuple[str, ...]] = None
150
+ ) -> bool:
151
+ """Are we at the end of a tuple?"""
152
+ if self.stream.current.type in ("variable_end", "block_end", "rparen"):
153
+ return True
154
+ elif extra_end_rules is not None:
155
+ return self.stream.current.test_any(extra_end_rules) # type: ignore
156
+ return False
157
+
158
+ def free_identifier(self, lineno: t.Optional[int] = None) -> nodes.InternalName:
159
+ """Return a new free identifier as :class:`~jinja2.nodes.InternalName`."""
160
+ self._last_identifier += 1
161
+ rv = object.__new__(nodes.InternalName)
162
+ nodes.Node.__init__(rv, f"fi{self._last_identifier}", lineno=lineno)
163
+ return rv
164
+
165
+ def parse_statement(self) -> t.Union[nodes.Node, t.List[nodes.Node]]:
166
+ """Parse a single statement."""
167
+ token = self.stream.current
168
+ if token.type != "name":
169
+ self.fail("tag name expected", token.lineno)
170
+ self._tag_stack.append(token.value)
171
+ pop_tag = True
172
+ try:
173
+ if token.value in _statement_keywords:
174
+ f = getattr(self, f"parse_{self.stream.current.value}")
175
+ return f() # type: ignore
176
+ if token.value == "call":
177
+ return self.parse_call_block()
178
+ if token.value == "filter":
179
+ return self.parse_filter_block()
180
+ ext = self.extensions.get(token.value)
181
+ if ext is not None:
182
+ return ext(self)
183
+
184
+ # did not work out, remove the token we pushed by accident
185
+ # from the stack so that the unknown tag fail function can
186
+ # produce a proper error message.
187
+ self._tag_stack.pop()
188
+ pop_tag = False
189
+ self.fail_unknown_tag(token.value, token.lineno)
190
+ finally:
191
+ if pop_tag:
192
+ self._tag_stack.pop()
193
+
194
+ def parse_statements(
195
+ self, end_tokens: t.Tuple[str, ...], drop_needle: bool = False
196
+ ) -> t.List[nodes.Node]:
197
+ """Parse multiple statements into a list until one of the end tokens
198
+ is reached. This is used to parse the body of statements as it also
199
+ parses template data if appropriate. The parser checks first if the
200
+ current token is a colon and skips it if there is one. Then it checks
201
+ for the block end and parses until if one of the `end_tokens` is
202
+ reached. Per default the active token in the stream at the end of
203
+ the call is the matched end token. If this is not wanted `drop_needle`
204
+ can be set to `True` and the end token is removed.
205
+ """
206
+ # the first token may be a colon for python compatibility
207
+ self.stream.skip_if("colon")
208
+
209
+ # in the future it would be possible to add whole code sections
210
+ # by adding some sort of end of statement token and parsing those here.
211
+ self.stream.expect("block_end")
212
+ result = self.subparse(end_tokens)
213
+
214
+ # we reached the end of the template too early, the subparser
215
+ # does not check for this, so we do that now
216
+ if self.stream.current.type == "eof":
217
+ self.fail_eof(end_tokens)
218
+
219
+ if drop_needle:
220
+ next(self.stream)
221
+ return result
222
+
223
+ def parse_set(self) -> t.Union[nodes.Assign, nodes.AssignBlock]:
224
+ """Parse an assign statement."""
225
+ lineno = next(self.stream).lineno
226
+ target = self.parse_assign_target(with_namespace=True)
227
+ if self.stream.skip_if("assign"):
228
+ expr = self.parse_tuple()
229
+ return nodes.Assign(target, expr, lineno=lineno)
230
+ filter_node = self.parse_filter(None)
231
+ body = self.parse_statements(("name:endset",), drop_needle=True)
232
+ return nodes.AssignBlock(target, filter_node, body, lineno=lineno)
233
+
234
+ def parse_for(self) -> nodes.For:
235
+ """Parse a for loop."""
236
+ lineno = self.stream.expect("name:for").lineno
237
+ target = self.parse_assign_target(extra_end_rules=("name:in",))
238
+ self.stream.expect("name:in")
239
+ iter = self.parse_tuple(
240
+ with_condexpr=False, extra_end_rules=("name:recursive",)
241
+ )
242
+ test = None
243
+ if self.stream.skip_if("name:if"):
244
+ test = self.parse_expression()
245
+ recursive = self.stream.skip_if("name:recursive")
246
+ body = self.parse_statements(("name:endfor", "name:else"))
247
+ if next(self.stream).value == "endfor":
248
+ else_ = []
249
+ else:
250
+ else_ = self.parse_statements(("name:endfor",), drop_needle=True)
251
+ return nodes.For(target, iter, body, else_, test, recursive, lineno=lineno)
252
+
253
+ def parse_if(self) -> nodes.If:
254
+ """Parse an if construct."""
255
+ node = result = nodes.If(lineno=self.stream.expect("name:if").lineno)
256
+ while True:
257
+ node.test = self.parse_tuple(with_condexpr=False)
258
+ node.body = self.parse_statements(("name:elif", "name:else", "name:endif"))
259
+ node.elif_ = []
260
+ node.else_ = []
261
+ token = next(self.stream)
262
+ if token.test("name:elif"):
263
+ node = nodes.If(lineno=self.stream.current.lineno)
264
+ result.elif_.append(node)
265
+ continue
266
+ elif token.test("name:else"):
267
+ result.else_ = self.parse_statements(("name:endif",), drop_needle=True)
268
+ break
269
+ return result
270
+
271
+ def parse_with(self) -> nodes.With:
272
+ node = nodes.With(lineno=next(self.stream).lineno)
273
+ targets: t.List[nodes.Expr] = []
274
+ values: t.List[nodes.Expr] = []
275
+ while self.stream.current.type != "block_end":
276
+ if targets:
277
+ self.stream.expect("comma")
278
+ target = self.parse_assign_target()
279
+ target.set_ctx("param")
280
+ targets.append(target)
281
+ self.stream.expect("assign")
282
+ values.append(self.parse_expression())
283
+ node.targets = targets
284
+ node.values = values
285
+ node.body = self.parse_statements(("name:endwith",), drop_needle=True)
286
+ return node
287
+
288
+ def parse_autoescape(self) -> nodes.Scope:
289
+ node = nodes.ScopedEvalContextModifier(lineno=next(self.stream).lineno)
290
+ node.options = [nodes.Keyword("autoescape", self.parse_expression())]
291
+ node.body = self.parse_statements(("name:endautoescape",), drop_needle=True)
292
+ return nodes.Scope([node])
293
+
294
+ def parse_block(self) -> nodes.Block:
295
+ node = nodes.Block(lineno=next(self.stream).lineno)
296
+ node.name = self.stream.expect("name").value
297
+ node.scoped = self.stream.skip_if("name:scoped")
298
+ node.required = self.stream.skip_if("name:required")
299
+
300
+ # common problem people encounter when switching from django
301
+ # to jinja. we do not support hyphens in block names, so let's
302
+ # raise a nicer error message in that case.
303
+ if self.stream.current.type == "sub":
304
+ self.fail(
305
+ "Block names in Jinja have to be valid Python identifiers and may not"
306
+ " contain hyphens, use an underscore instead."
307
+ )
308
+
309
+ node.body = self.parse_statements(("name:endblock",), drop_needle=True)
310
+
311
+ # enforce that required blocks only contain whitespace or comments
312
+ # by asserting that the body, if not empty, is just TemplateData nodes
313
+ # with whitespace data
314
+ if node.required:
315
+ for body_node in node.body:
316
+ if not isinstance(body_node, nodes.Output) or any(
317
+ not isinstance(output_node, nodes.TemplateData)
318
+ or not output_node.data.isspace()
319
+ for output_node in body_node.nodes
320
+ ):
321
+ self.fail("Required blocks can only contain comments or whitespace")
322
+
323
+ self.stream.skip_if("name:" + node.name)
324
+ return node
325
+
326
+ def parse_extends(self) -> nodes.Extends:
327
+ node = nodes.Extends(lineno=next(self.stream).lineno)
328
+ node.template = self.parse_expression()
329
+ return node
330
+
331
+ def parse_import_context(
332
+ self, node: _ImportInclude, default: bool
333
+ ) -> _ImportInclude:
334
+ if self.stream.current.test_any(
335
+ "name:with", "name:without"
336
+ ) and self.stream.look().test("name:context"):
337
+ node.with_context = next(self.stream).value == "with"
338
+ self.stream.skip()
339
+ else:
340
+ node.with_context = default
341
+ return node
342
+
343
+ def parse_include(self) -> nodes.Include:
344
+ node = nodes.Include(lineno=next(self.stream).lineno)
345
+ node.template = self.parse_expression()
346
+ if self.stream.current.test("name:ignore") and self.stream.look().test(
347
+ "name:missing"
348
+ ):
349
+ node.ignore_missing = True
350
+ self.stream.skip(2)
351
+ else:
352
+ node.ignore_missing = False
353
+ return self.parse_import_context(node, True)
354
+
355
+ def parse_import(self) -> nodes.Import:
356
+ node = nodes.Import(lineno=next(self.stream).lineno)
357
+ node.template = self.parse_expression()
358
+ self.stream.expect("name:as")
359
+ node.target = self.parse_assign_target(name_only=True).name
360
+ return self.parse_import_context(node, False)
361
+
362
+ def parse_from(self) -> nodes.FromImport:
363
+ node = nodes.FromImport(lineno=next(self.stream).lineno)
364
+ node.template = self.parse_expression()
365
+ self.stream.expect("name:import")
366
+ node.names = []
367
+
368
+ def parse_context() -> bool:
369
+ if self.stream.current.value in {
370
+ "with",
371
+ "without",
372
+ } and self.stream.look().test("name:context"):
373
+ node.with_context = next(self.stream).value == "with"
374
+ self.stream.skip()
375
+ return True
376
+ return False
377
+
378
+ while True:
379
+ if node.names:
380
+ self.stream.expect("comma")
381
+ if self.stream.current.type == "name":
382
+ if parse_context():
383
+ break
384
+ target = self.parse_assign_target(name_only=True)
385
+ if target.name.startswith("_"):
386
+ self.fail(
387
+ "names starting with an underline can not be imported",
388
+ target.lineno,
389
+ exc=TemplateAssertionError,
390
+ )
391
+ if self.stream.skip_if("name:as"):
392
+ alias = self.parse_assign_target(name_only=True)
393
+ node.names.append((target.name, alias.name))
394
+ else:
395
+ node.names.append(target.name)
396
+ if parse_context() or self.stream.current.type != "comma":
397
+ break
398
+ else:
399
+ self.stream.expect("name")
400
+ if not hasattr(node, "with_context"):
401
+ node.with_context = False
402
+ return node
403
+
404
+ def parse_signature(self, node: _MacroCall) -> None:
405
+ args = node.args = []
406
+ defaults = node.defaults = []
407
+ self.stream.expect("lparen")
408
+ while self.stream.current.type != "rparen":
409
+ if args:
410
+ self.stream.expect("comma")
411
+ arg = self.parse_assign_target(name_only=True)
412
+ arg.set_ctx("param")
413
+ if self.stream.skip_if("assign"):
414
+ defaults.append(self.parse_expression())
415
+ elif defaults:
416
+ self.fail("non-default argument follows default argument")
417
+ args.append(arg)
418
+ self.stream.expect("rparen")
419
+
420
+ def parse_call_block(self) -> nodes.CallBlock:
421
+ node = nodes.CallBlock(lineno=next(self.stream).lineno)
422
+ if self.stream.current.type == "lparen":
423
+ self.parse_signature(node)
424
+ else:
425
+ node.args = []
426
+ node.defaults = []
427
+
428
+ call_node = self.parse_expression()
429
+ if not isinstance(call_node, nodes.Call):
430
+ self.fail("expected call", node.lineno)
431
+ node.call = call_node
432
+ node.body = self.parse_statements(("name:endcall",), drop_needle=True)
433
+ return node
434
+
435
+ def parse_filter_block(self) -> nodes.FilterBlock:
436
+ node = nodes.FilterBlock(lineno=next(self.stream).lineno)
437
+ node.filter = self.parse_filter(None, start_inline=True) # type: ignore
438
+ node.body = self.parse_statements(("name:endfilter",), drop_needle=True)
439
+ return node
440
+
441
+ def parse_macro(self) -> nodes.Macro:
442
+ node = nodes.Macro(lineno=next(self.stream).lineno)
443
+ node.name = self.parse_assign_target(name_only=True).name
444
+ self.parse_signature(node)
445
+ node.body = self.parse_statements(("name:endmacro",), drop_needle=True)
446
+ return node
447
+
448
+ def parse_print(self) -> nodes.Output:
449
+ node = nodes.Output(lineno=next(self.stream).lineno)
450
+ node.nodes = []
451
+ while self.stream.current.type != "block_end":
452
+ if node.nodes:
453
+ self.stream.expect("comma")
454
+ node.nodes.append(self.parse_expression())
455
+ return node
456
+
457
+ @typing.overload
458
+ def parse_assign_target(
459
+ self, with_tuple: bool = ..., name_only: "te.Literal[True]" = ...
460
+ ) -> nodes.Name:
461
+ ...
462
+
463
+ @typing.overload
464
+ def parse_assign_target(
465
+ self,
466
+ with_tuple: bool = True,
467
+ name_only: bool = False,
468
+ extra_end_rules: t.Optional[t.Tuple[str, ...]] = None,
469
+ with_namespace: bool = False,
470
+ ) -> t.Union[nodes.NSRef, nodes.Name, nodes.Tuple]:
471
+ ...
472
+
473
+ def parse_assign_target(
474
+ self,
475
+ with_tuple: bool = True,
476
+ name_only: bool = False,
477
+ extra_end_rules: t.Optional[t.Tuple[str, ...]] = None,
478
+ with_namespace: bool = False,
479
+ ) -> t.Union[nodes.NSRef, nodes.Name, nodes.Tuple]:
480
+ """Parse an assignment target. As Jinja allows assignments to
481
+ tuples, this function can parse all allowed assignment targets. Per
482
+ default assignments to tuples are parsed, that can be disable however
483
+ by setting `with_tuple` to `False`. If only assignments to names are
484
+ wanted `name_only` can be set to `True`. The `extra_end_rules`
485
+ parameter is forwarded to the tuple parsing function. If
486
+ `with_namespace` is enabled, a namespace assignment may be parsed.
487
+ """
488
+ target: nodes.Expr
489
+
490
+ if with_namespace and self.stream.look().type == "dot":
491
+ token = self.stream.expect("name")
492
+ next(self.stream) # dot
493
+ attr = self.stream.expect("name")
494
+ target = nodes.NSRef(token.value, attr.value, lineno=token.lineno)
495
+ elif name_only:
496
+ token = self.stream.expect("name")
497
+ target = nodes.Name(token.value, "store", lineno=token.lineno)
498
+ else:
499
+ if with_tuple:
500
+ target = self.parse_tuple(
501
+ simplified=True, extra_end_rules=extra_end_rules
502
+ )
503
+ else:
504
+ target = self.parse_primary()
505
+
506
+ target.set_ctx("store")
507
+
508
+ if not target.can_assign():
509
+ self.fail(
510
+ f"can't assign to {type(target).__name__.lower()!r}", target.lineno
511
+ )
512
+
513
+ return target # type: ignore
514
+
515
+ def parse_expression(self, with_condexpr: bool = True) -> nodes.Expr:
516
+ """Parse an expression. Per default all expressions are parsed, if
517
+ the optional `with_condexpr` parameter is set to `False` conditional
518
+ expressions are not parsed.
519
+ """
520
+ if with_condexpr:
521
+ return self.parse_condexpr()
522
+ return self.parse_or()
523
+
524
+ def parse_condexpr(self) -> nodes.Expr:
525
+ lineno = self.stream.current.lineno
526
+ expr1 = self.parse_or()
527
+ expr3: t.Optional[nodes.Expr]
528
+
529
+ while self.stream.skip_if("name:if"):
530
+ expr2 = self.parse_or()
531
+ if self.stream.skip_if("name:else"):
532
+ expr3 = self.parse_condexpr()
533
+ else:
534
+ expr3 = None
535
+ expr1 = nodes.CondExpr(expr2, expr1, expr3, lineno=lineno)
536
+ lineno = self.stream.current.lineno
537
+ return expr1
538
+
539
+ def parse_or(self) -> nodes.Expr:
540
+ lineno = self.stream.current.lineno
541
+ left = self.parse_and()
542
+ while self.stream.skip_if("name:or"):
543
+ right = self.parse_and()
544
+ left = nodes.Or(left, right, lineno=lineno)
545
+ lineno = self.stream.current.lineno
546
+ return left
547
+
548
+ def parse_and(self) -> nodes.Expr:
549
+ lineno = self.stream.current.lineno
550
+ left = self.parse_not()
551
+ while self.stream.skip_if("name:and"):
552
+ right = self.parse_not()
553
+ left = nodes.And(left, right, lineno=lineno)
554
+ lineno = self.stream.current.lineno
555
+ return left
556
+
557
+ def parse_not(self) -> nodes.Expr:
558
+ if self.stream.current.test("name:not"):
559
+ lineno = next(self.stream).lineno
560
+ return nodes.Not(self.parse_not(), lineno=lineno)
561
+ return self.parse_compare()
562
+
563
+ def parse_compare(self) -> nodes.Expr:
564
+ lineno = self.stream.current.lineno
565
+ expr = self.parse_math1()
566
+ ops = []
567
+ while True:
568
+ token_type = self.stream.current.type
569
+ if token_type in _compare_operators:
570
+ next(self.stream)
571
+ ops.append(nodes.Operand(token_type, self.parse_math1()))
572
+ elif self.stream.skip_if("name:in"):
573
+ ops.append(nodes.Operand("in", self.parse_math1()))
574
+ elif self.stream.current.test("name:not") and self.stream.look().test(
575
+ "name:in"
576
+ ):
577
+ self.stream.skip(2)
578
+ ops.append(nodes.Operand("notin", self.parse_math1()))
579
+ else:
580
+ break
581
+ lineno = self.stream.current.lineno
582
+ if not ops:
583
+ return expr
584
+ return nodes.Compare(expr, ops, lineno=lineno)
585
+
586
+ def parse_math1(self) -> nodes.Expr:
587
+ lineno = self.stream.current.lineno
588
+ left = self.parse_concat()
589
+ while self.stream.current.type in ("add", "sub"):
590
+ cls = _math_nodes[self.stream.current.type]
591
+ next(self.stream)
592
+ right = self.parse_concat()
593
+ left = cls(left, right, lineno=lineno)
594
+ lineno = self.stream.current.lineno
595
+ return left
596
+
597
+ def parse_concat(self) -> nodes.Expr:
598
+ lineno = self.stream.current.lineno
599
+ args = [self.parse_math2()]
600
+ while self.stream.current.type == "tilde":
601
+ next(self.stream)
602
+ args.append(self.parse_math2())
603
+ if len(args) == 1:
604
+ return args[0]
605
+ return nodes.Concat(args, lineno=lineno)
606
+
607
+ def parse_math2(self) -> nodes.Expr:
608
+ lineno = self.stream.current.lineno
609
+ left = self.parse_pow()
610
+ while self.stream.current.type in ("mul", "div", "floordiv", "mod"):
611
+ cls = _math_nodes[self.stream.current.type]
612
+ next(self.stream)
613
+ right = self.parse_pow()
614
+ left = cls(left, right, lineno=lineno)
615
+ lineno = self.stream.current.lineno
616
+ return left
617
+
618
+ def parse_pow(self) -> nodes.Expr:
619
+ lineno = self.stream.current.lineno
620
+ left = self.parse_unary()
621
+ while self.stream.current.type == "pow":
622
+ next(self.stream)
623
+ right = self.parse_unary()
624
+ left = nodes.Pow(left, right, lineno=lineno)
625
+ lineno = self.stream.current.lineno
626
+ return left
627
+
628
+ def parse_unary(self, with_filter: bool = True) -> nodes.Expr:
629
+ token_type = self.stream.current.type
630
+ lineno = self.stream.current.lineno
631
+ node: nodes.Expr
632
+
633
+ if token_type == "sub":
634
+ next(self.stream)
635
+ node = nodes.Neg(self.parse_unary(False), lineno=lineno)
636
+ elif token_type == "add":
637
+ next(self.stream)
638
+ node = nodes.Pos(self.parse_unary(False), lineno=lineno)
639
+ else:
640
+ node = self.parse_primary()
641
+ node = self.parse_postfix(node)
642
+ if with_filter:
643
+ node = self.parse_filter_expr(node)
644
+ return node
645
+
646
+ def parse_primary(self) -> nodes.Expr:
647
+ token = self.stream.current
648
+ node: nodes.Expr
649
+ if token.type == "name":
650
+ if token.value in ("true", "false", "True", "False"):
651
+ node = nodes.Const(token.value in ("true", "True"), lineno=token.lineno)
652
+ elif token.value in ("none", "None"):
653
+ node = nodes.Const(None, lineno=token.lineno)
654
+ else:
655
+ node = nodes.Name(token.value, "load", lineno=token.lineno)
656
+ next(self.stream)
657
+ elif token.type == "string":
658
+ next(self.stream)
659
+ buf = [token.value]
660
+ lineno = token.lineno
661
+ while self.stream.current.type == "string":
662
+ buf.append(self.stream.current.value)
663
+ next(self.stream)
664
+ node = nodes.Const("".join(buf), lineno=lineno)
665
+ elif token.type in ("integer", "float"):
666
+ next(self.stream)
667
+ node = nodes.Const(token.value, lineno=token.lineno)
668
+ elif token.type == "lparen":
669
+ next(self.stream)
670
+ node = self.parse_tuple(explicit_parentheses=True)
671
+ self.stream.expect("rparen")
672
+ elif token.type == "lbracket":
673
+ node = self.parse_list()
674
+ elif token.type == "lbrace":
675
+ node = self.parse_dict()
676
+ else:
677
+ self.fail(f"unexpected {describe_token(token)!r}", token.lineno)
678
+ return node
679
+
680
+ def parse_tuple(
681
+ self,
682
+ simplified: bool = False,
683
+ with_condexpr: bool = True,
684
+ extra_end_rules: t.Optional[t.Tuple[str, ...]] = None,
685
+ explicit_parentheses: bool = False,
686
+ ) -> t.Union[nodes.Tuple, nodes.Expr]:
687
+ """Works like `parse_expression` but if multiple expressions are
688
+ delimited by a comma a :class:`~jinja2.nodes.Tuple` node is created.
689
+ This method could also return a regular expression instead of a tuple
690
+ if no commas where found.
691
+
692
+ The default parsing mode is a full tuple. If `simplified` is `True`
693
+ only names and literals are parsed. The `no_condexpr` parameter is
694
+ forwarded to :meth:`parse_expression`.
695
+
696
+ Because tuples do not require delimiters and may end in a bogus comma
697
+ an extra hint is needed that marks the end of a tuple. For example
698
+ for loops support tuples between `for` and `in`. In that case the
699
+ `extra_end_rules` is set to ``['name:in']``.
700
+
701
+ `explicit_parentheses` is true if the parsing was triggered by an
702
+ expression in parentheses. This is used to figure out if an empty
703
+ tuple is a valid expression or not.
704
+ """
705
+ lineno = self.stream.current.lineno
706
+ if simplified:
707
+ parse = self.parse_primary
708
+ elif with_condexpr:
709
+ parse = self.parse_expression
710
+ else:
711
+
712
+ def parse() -> nodes.Expr:
713
+ return self.parse_expression(with_condexpr=False)
714
+
715
+ args: t.List[nodes.Expr] = []
716
+ is_tuple = False
717
+
718
+ while True:
719
+ if args:
720
+ self.stream.expect("comma")
721
+ if self.is_tuple_end(extra_end_rules):
722
+ break
723
+ args.append(parse())
724
+ if self.stream.current.type == "comma":
725
+ is_tuple = True
726
+ else:
727
+ break
728
+ lineno = self.stream.current.lineno
729
+
730
+ if not is_tuple:
731
+ if args:
732
+ return args[0]
733
+
734
+ # if we don't have explicit parentheses, an empty tuple is
735
+ # not a valid expression. This would mean nothing (literally
736
+ # nothing) in the spot of an expression would be an empty
737
+ # tuple.
738
+ if not explicit_parentheses:
739
+ self.fail(
740
+ "Expected an expression,"
741
+ f" got {describe_token(self.stream.current)!r}"
742
+ )
743
+
744
+ return nodes.Tuple(args, "load", lineno=lineno)
745
+
746
+ def parse_list(self) -> nodes.List:
747
+ token = self.stream.expect("lbracket")
748
+ items: t.List[nodes.Expr] = []
749
+ while self.stream.current.type != "rbracket":
750
+ if items:
751
+ self.stream.expect("comma")
752
+ if self.stream.current.type == "rbracket":
753
+ break
754
+ items.append(self.parse_expression())
755
+ self.stream.expect("rbracket")
756
+ return nodes.List(items, lineno=token.lineno)
757
+
758
+ def parse_dict(self) -> nodes.Dict:
759
+ token = self.stream.expect("lbrace")
760
+ items: t.List[nodes.Pair] = []
761
+ while self.stream.current.type != "rbrace":
762
+ if items:
763
+ self.stream.expect("comma")
764
+ if self.stream.current.type == "rbrace":
765
+ break
766
+ key = self.parse_expression()
767
+ self.stream.expect("colon")
768
+ value = self.parse_expression()
769
+ items.append(nodes.Pair(key, value, lineno=key.lineno))
770
+ self.stream.expect("rbrace")
771
+ return nodes.Dict(items, lineno=token.lineno)
772
+
773
+ def parse_postfix(self, node: nodes.Expr) -> nodes.Expr:
774
+ while True:
775
+ token_type = self.stream.current.type
776
+ if token_type == "dot" or token_type == "lbracket":
777
+ node = self.parse_subscript(node)
778
+ # calls are valid both after postfix expressions (getattr
779
+ # and getitem) as well as filters and tests
780
+ elif token_type == "lparen":
781
+ node = self.parse_call(node)
782
+ else:
783
+ break
784
+ return node
785
+
786
+ def parse_filter_expr(self, node: nodes.Expr) -> nodes.Expr:
787
+ while True:
788
+ token_type = self.stream.current.type
789
+ if token_type == "pipe":
790
+ node = self.parse_filter(node) # type: ignore
791
+ elif token_type == "name" and self.stream.current.value == "is":
792
+ node = self.parse_test(node)
793
+ # calls are valid both after postfix expressions (getattr
794
+ # and getitem) as well as filters and tests
795
+ elif token_type == "lparen":
796
+ node = self.parse_call(node)
797
+ else:
798
+ break
799
+ return node
800
+
801
+ def parse_subscript(
802
+ self, node: nodes.Expr
803
+ ) -> t.Union[nodes.Getattr, nodes.Getitem]:
804
+ token = next(self.stream)
805
+ arg: nodes.Expr
806
+
807
+ if token.type == "dot":
808
+ attr_token = self.stream.current
809
+ next(self.stream)
810
+ if attr_token.type == "name":
811
+ return nodes.Getattr(
812
+ node, attr_token.value, "load", lineno=token.lineno
813
+ )
814
+ elif attr_token.type != "integer":
815
+ self.fail("expected name or number", attr_token.lineno)
816
+ arg = nodes.Const(attr_token.value, lineno=attr_token.lineno)
817
+ return nodes.Getitem(node, arg, "load", lineno=token.lineno)
818
+ if token.type == "lbracket":
819
+ args: t.List[nodes.Expr] = []
820
+ while self.stream.current.type != "rbracket":
821
+ if args:
822
+ self.stream.expect("comma")
823
+ args.append(self.parse_subscribed())
824
+ self.stream.expect("rbracket")
825
+ if len(args) == 1:
826
+ arg = args[0]
827
+ else:
828
+ arg = nodes.Tuple(args, "load", lineno=token.lineno)
829
+ return nodes.Getitem(node, arg, "load", lineno=token.lineno)
830
+ self.fail("expected subscript expression", token.lineno)
831
+
832
+ def parse_subscribed(self) -> nodes.Expr:
833
+ lineno = self.stream.current.lineno
834
+ args: t.List[t.Optional[nodes.Expr]]
835
+
836
+ if self.stream.current.type == "colon":
837
+ next(self.stream)
838
+ args = [None]
839
+ else:
840
+ node = self.parse_expression()
841
+ if self.stream.current.type != "colon":
842
+ return node
843
+ next(self.stream)
844
+ args = [node]
845
+
846
+ if self.stream.current.type == "colon":
847
+ args.append(None)
848
+ elif self.stream.current.type not in ("rbracket", "comma"):
849
+ args.append(self.parse_expression())
850
+ else:
851
+ args.append(None)
852
+
853
+ if self.stream.current.type == "colon":
854
+ next(self.stream)
855
+ if self.stream.current.type not in ("rbracket", "comma"):
856
+ args.append(self.parse_expression())
857
+ else:
858
+ args.append(None)
859
+ else:
860
+ args.append(None)
861
+
862
+ return nodes.Slice(lineno=lineno, *args) # noqa: B026
863
+
864
+ def parse_call_args(self) -> t.Tuple:
865
+ token = self.stream.expect("lparen")
866
+ args = []
867
+ kwargs = []
868
+ dyn_args = None
869
+ dyn_kwargs = None
870
+ require_comma = False
871
+
872
+ def ensure(expr: bool) -> None:
873
+ if not expr:
874
+ self.fail("invalid syntax for function call expression", token.lineno)
875
+
876
+ while self.stream.current.type != "rparen":
877
+ if require_comma:
878
+ self.stream.expect("comma")
879
+
880
+ # support for trailing comma
881
+ if self.stream.current.type == "rparen":
882
+ break
883
+
884
+ if self.stream.current.type == "mul":
885
+ ensure(dyn_args is None and dyn_kwargs is None)
886
+ next(self.stream)
887
+ dyn_args = self.parse_expression()
888
+ elif self.stream.current.type == "pow":
889
+ ensure(dyn_kwargs is None)
890
+ next(self.stream)
891
+ dyn_kwargs = self.parse_expression()
892
+ else:
893
+ if (
894
+ self.stream.current.type == "name"
895
+ and self.stream.look().type == "assign"
896
+ ):
897
+ # Parsing a kwarg
898
+ ensure(dyn_kwargs is None)
899
+ key = self.stream.current.value
900
+ self.stream.skip(2)
901
+ value = self.parse_expression()
902
+ kwargs.append(nodes.Keyword(key, value, lineno=value.lineno))
903
+ else:
904
+ # Parsing an arg
905
+ ensure(dyn_args is None and dyn_kwargs is None and not kwargs)
906
+ args.append(self.parse_expression())
907
+
908
+ require_comma = True
909
+
910
+ self.stream.expect("rparen")
911
+ return args, kwargs, dyn_args, dyn_kwargs
912
+
913
+ def parse_call(self, node: nodes.Expr) -> nodes.Call:
914
+ # The lparen will be expected in parse_call_args, but the lineno
915
+ # needs to be recorded before the stream is advanced.
916
+ token = self.stream.current
917
+ args, kwargs, dyn_args, dyn_kwargs = self.parse_call_args()
918
+ return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno)
919
+
920
+ def parse_filter(
921
+ self, node: t.Optional[nodes.Expr], start_inline: bool = False
922
+ ) -> t.Optional[nodes.Expr]:
923
+ while self.stream.current.type == "pipe" or start_inline:
924
+ if not start_inline:
925
+ next(self.stream)
926
+ token = self.stream.expect("name")
927
+ name = token.value
928
+ while self.stream.current.type == "dot":
929
+ next(self.stream)
930
+ name += "." + self.stream.expect("name").value
931
+ if self.stream.current.type == "lparen":
932
+ args, kwargs, dyn_args, dyn_kwargs = self.parse_call_args()
933
+ else:
934
+ args = []
935
+ kwargs = []
936
+ dyn_args = dyn_kwargs = None
937
+ node = nodes.Filter(
938
+ node, name, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno
939
+ )
940
+ start_inline = False
941
+ return node
942
+
943
+ def parse_test(self, node: nodes.Expr) -> nodes.Expr:
944
+ token = next(self.stream)
945
+ if self.stream.current.test("name:not"):
946
+ next(self.stream)
947
+ negated = True
948
+ else:
949
+ negated = False
950
+ name = self.stream.expect("name").value
951
+ while self.stream.current.type == "dot":
952
+ next(self.stream)
953
+ name += "." + self.stream.expect("name").value
954
+ dyn_args = dyn_kwargs = None
955
+ kwargs = []
956
+ if self.stream.current.type == "lparen":
957
+ args, kwargs, dyn_args, dyn_kwargs = self.parse_call_args()
958
+ elif self.stream.current.type in {
959
+ "name",
960
+ "string",
961
+ "integer",
962
+ "float",
963
+ "lparen",
964
+ "lbracket",
965
+ "lbrace",
966
+ } and not self.stream.current.test_any("name:else", "name:or", "name:and"):
967
+ if self.stream.current.test("name:is"):
968
+ self.fail("You cannot chain multiple tests with is")
969
+ arg_node = self.parse_primary()
970
+ arg_node = self.parse_postfix(arg_node)
971
+ args = [arg_node]
972
+ else:
973
+ args = []
974
+ node = nodes.Test(
975
+ node, name, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno
976
+ )
977
+ if negated:
978
+ node = nodes.Not(node, lineno=token.lineno)
979
+ return node
980
+
981
+ def subparse(
982
+ self, end_tokens: t.Optional[t.Tuple[str, ...]] = None
983
+ ) -> t.List[nodes.Node]:
984
+ body: t.List[nodes.Node] = []
985
+ data_buffer: t.List[nodes.Node] = []
986
+ add_data = data_buffer.append
987
+
988
+ if end_tokens is not None:
989
+ self._end_token_stack.append(end_tokens)
990
+
991
+ def flush_data() -> None:
992
+ if data_buffer:
993
+ lineno = data_buffer[0].lineno
994
+ body.append(nodes.Output(data_buffer[:], lineno=lineno))
995
+ del data_buffer[:]
996
+
997
+ try:
998
+ while self.stream:
999
+ token = self.stream.current
1000
+ if token.type == "data":
1001
+ if token.value:
1002
+ add_data(nodes.TemplateData(token.value, lineno=token.lineno))
1003
+ next(self.stream)
1004
+ elif token.type == "variable_begin":
1005
+ next(self.stream)
1006
+ add_data(self.parse_tuple(with_condexpr=True))
1007
+ self.stream.expect("variable_end")
1008
+ elif token.type == "block_begin":
1009
+ flush_data()
1010
+ next(self.stream)
1011
+ if end_tokens is not None and self.stream.current.test_any(
1012
+ *end_tokens
1013
+ ):
1014
+ return body
1015
+ rv = self.parse_statement()
1016
+ if isinstance(rv, list):
1017
+ body.extend(rv)
1018
+ else:
1019
+ body.append(rv)
1020
+ self.stream.expect("block_end")
1021
+ else:
1022
+ raise AssertionError("internal parsing error")
1023
+
1024
+ flush_data()
1025
+ finally:
1026
+ if end_tokens is not None:
1027
+ self._end_token_stack.pop()
1028
+ return body
1029
+
1030
+ def parse(self) -> nodes.Template:
1031
+ """Parse the whole template into a `Template` node."""
1032
+ result = nodes.Template(self.subparse(), lineno=1)
1033
+ result.set_environment(self.environment)
1034
+ return result
tuning-competition-baseline/.venv/lib/python3.11/site-packages/jinja2/utils.py ADDED
@@ -0,0 +1,755 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import enum
2
+ import json
3
+ import os
4
+ import re
5
+ import typing as t
6
+ from collections import abc
7
+ from collections import deque
8
+ from random import choice
9
+ from random import randrange
10
+ from threading import Lock
11
+ from types import CodeType
12
+ from urllib.parse import quote_from_bytes
13
+
14
+ import markupsafe
15
+
16
+ if t.TYPE_CHECKING:
17
+ import typing_extensions as te
18
+
19
+ F = t.TypeVar("F", bound=t.Callable[..., t.Any])
20
+
21
+ # special singleton representing missing values for the runtime
22
+ missing: t.Any = type("MissingType", (), {"__repr__": lambda x: "missing"})()
23
+
24
+ internal_code: t.MutableSet[CodeType] = set()
25
+
26
+ concat = "".join
27
+
28
+
29
+ def pass_context(f: F) -> F:
30
+ """Pass the :class:`~jinja2.runtime.Context` as the first argument
31
+ to the decorated function when called while rendering a template.
32
+
33
+ Can be used on functions, filters, and tests.
34
+
35
+ If only ``Context.eval_context`` is needed, use
36
+ :func:`pass_eval_context`. If only ``Context.environment`` is
37
+ needed, use :func:`pass_environment`.
38
+
39
+ .. versionadded:: 3.0.0
40
+ Replaces ``contextfunction`` and ``contextfilter``.
41
+ """
42
+ f.jinja_pass_arg = _PassArg.context # type: ignore
43
+ return f
44
+
45
+
46
+ def pass_eval_context(f: F) -> F:
47
+ """Pass the :class:`~jinja2.nodes.EvalContext` as the first argument
48
+ to the decorated function when called while rendering a template.
49
+ See :ref:`eval-context`.
50
+
51
+ Can be used on functions, filters, and tests.
52
+
53
+ If only ``EvalContext.environment`` is needed, use
54
+ :func:`pass_environment`.
55
+
56
+ .. versionadded:: 3.0.0
57
+ Replaces ``evalcontextfunction`` and ``evalcontextfilter``.
58
+ """
59
+ f.jinja_pass_arg = _PassArg.eval_context # type: ignore
60
+ return f
61
+
62
+
63
+ def pass_environment(f: F) -> F:
64
+ """Pass the :class:`~jinja2.Environment` as the first argument to
65
+ the decorated function when called while rendering a template.
66
+
67
+ Can be used on functions, filters, and tests.
68
+
69
+ .. versionadded:: 3.0.0
70
+ Replaces ``environmentfunction`` and ``environmentfilter``.
71
+ """
72
+ f.jinja_pass_arg = _PassArg.environment # type: ignore
73
+ return f
74
+
75
+
76
+ class _PassArg(enum.Enum):
77
+ context = enum.auto()
78
+ eval_context = enum.auto()
79
+ environment = enum.auto()
80
+
81
+ @classmethod
82
+ def from_obj(cls, obj: F) -> t.Optional["_PassArg"]:
83
+ if hasattr(obj, "jinja_pass_arg"):
84
+ return obj.jinja_pass_arg # type: ignore
85
+
86
+ return None
87
+
88
+
89
+ def internalcode(f: F) -> F:
90
+ """Marks the function as internally used"""
91
+ internal_code.add(f.__code__)
92
+ return f
93
+
94
+
95
+ def is_undefined(obj: t.Any) -> bool:
96
+ """Check if the object passed is undefined. This does nothing more than
97
+ performing an instance check against :class:`Undefined` but looks nicer.
98
+ This can be used for custom filters or tests that want to react to
99
+ undefined variables. For example a custom default filter can look like
100
+ this::
101
+
102
+ def default(var, default=''):
103
+ if is_undefined(var):
104
+ return default
105
+ return var
106
+ """
107
+ from .runtime import Undefined
108
+
109
+ return isinstance(obj, Undefined)
110
+
111
+
112
+ def consume(iterable: t.Iterable[t.Any]) -> None:
113
+ """Consumes an iterable without doing anything with it."""
114
+ for _ in iterable:
115
+ pass
116
+
117
+
118
+ def clear_caches() -> None:
119
+ """Jinja keeps internal caches for environments and lexers. These are
120
+ used so that Jinja doesn't have to recreate environments and lexers all
121
+ the time. Normally you don't have to care about that but if you are
122
+ measuring memory consumption you may want to clean the caches.
123
+ """
124
+ from .environment import get_spontaneous_environment
125
+ from .lexer import _lexer_cache
126
+
127
+ get_spontaneous_environment.cache_clear()
128
+ _lexer_cache.clear()
129
+
130
+
131
+ def import_string(import_name: str, silent: bool = False) -> t.Any:
132
+ """Imports an object based on a string. This is useful if you want to
133
+ use import paths as endpoints or something similar. An import path can
134
+ be specified either in dotted notation (``xml.sax.saxutils.escape``)
135
+ or with a colon as object delimiter (``xml.sax.saxutils:escape``).
136
+
137
+ If the `silent` is True the return value will be `None` if the import
138
+ fails.
139
+
140
+ :return: imported object
141
+ """
142
+ try:
143
+ if ":" in import_name:
144
+ module, obj = import_name.split(":", 1)
145
+ elif "." in import_name:
146
+ module, _, obj = import_name.rpartition(".")
147
+ else:
148
+ return __import__(import_name)
149
+ return getattr(__import__(module, None, None, [obj]), obj)
150
+ except (ImportError, AttributeError):
151
+ if not silent:
152
+ raise
153
+
154
+
155
+ def open_if_exists(filename: str, mode: str = "rb") -> t.Optional[t.IO]:
156
+ """Returns a file descriptor for the filename if that file exists,
157
+ otherwise ``None``.
158
+ """
159
+ if not os.path.isfile(filename):
160
+ return None
161
+
162
+ return open(filename, mode)
163
+
164
+
165
+ def object_type_repr(obj: t.Any) -> str:
166
+ """Returns the name of the object's type. For some recognized
167
+ singletons the name of the object is returned instead. (For
168
+ example for `None` and `Ellipsis`).
169
+ """
170
+ if obj is None:
171
+ return "None"
172
+ elif obj is Ellipsis:
173
+ return "Ellipsis"
174
+
175
+ cls = type(obj)
176
+
177
+ if cls.__module__ == "builtins":
178
+ return f"{cls.__name__} object"
179
+
180
+ return f"{cls.__module__}.{cls.__name__} object"
181
+
182
+
183
+ def pformat(obj: t.Any) -> str:
184
+ """Format an object using :func:`pprint.pformat`."""
185
+ from pprint import pformat
186
+
187
+ return pformat(obj)
188
+
189
+
190
+ _http_re = re.compile(
191
+ r"""
192
+ ^
193
+ (
194
+ (https?://|www\.) # scheme or www
195
+ (([\w%-]+\.)+)? # subdomain
196
+ (
197
+ [a-z]{2,63} # basic tld
198
+ |
199
+ xn--[\w%]{2,59} # idna tld
200
+ )
201
+ |
202
+ ([\w%-]{2,63}\.)+ # basic domain
203
+ (com|net|int|edu|gov|org|info|mil) # basic tld
204
+ |
205
+ (https?://) # scheme
206
+ (
207
+ (([\d]{1,3})(\.[\d]{1,3}){3}) # IPv4
208
+ |
209
+ (\[([\da-f]{0,4}:){2}([\da-f]{0,4}:?){1,6}]) # IPv6
210
+ )
211
+ )
212
+ (?::[\d]{1,5})? # port
213
+ (?:[/?#]\S*)? # path, query, and fragment
214
+ $
215
+ """,
216
+ re.IGNORECASE | re.VERBOSE,
217
+ )
218
+ _email_re = re.compile(r"^\S+@\w[\w.-]*\.\w+$")
219
+
220
+
221
+ def urlize(
222
+ text: str,
223
+ trim_url_limit: t.Optional[int] = None,
224
+ rel: t.Optional[str] = None,
225
+ target: t.Optional[str] = None,
226
+ extra_schemes: t.Optional[t.Iterable[str]] = None,
227
+ ) -> str:
228
+ """Convert URLs in text into clickable links.
229
+
230
+ This may not recognize links in some situations. Usually, a more
231
+ comprehensive formatter, such as a Markdown library, is a better
232
+ choice.
233
+
234
+ Works on ``http://``, ``https://``, ``www.``, ``mailto:``, and email
235
+ addresses. Links with trailing punctuation (periods, commas, closing
236
+ parentheses) and leading punctuation (opening parentheses) are
237
+ recognized excluding the punctuation. Email addresses that include
238
+ header fields are not recognized (for example,
239
+ ``mailto:address@example.com?cc=copy@example.com``).
240
+
241
+ :param text: Original text containing URLs to link.
242
+ :param trim_url_limit: Shorten displayed URL values to this length.
243
+ :param target: Add the ``target`` attribute to links.
244
+ :param rel: Add the ``rel`` attribute to links.
245
+ :param extra_schemes: Recognize URLs that start with these schemes
246
+ in addition to the default behavior.
247
+
248
+ .. versionchanged:: 3.0
249
+ The ``extra_schemes`` parameter was added.
250
+
251
+ .. versionchanged:: 3.0
252
+ Generate ``https://`` links for URLs without a scheme.
253
+
254
+ .. versionchanged:: 3.0
255
+ The parsing rules were updated. Recognize email addresses with
256
+ or without the ``mailto:`` scheme. Validate IP addresses. Ignore
257
+ parentheses and brackets in more cases.
258
+ """
259
+ if trim_url_limit is not None:
260
+
261
+ def trim_url(x: str) -> str:
262
+ if len(x) > trim_url_limit:
263
+ return f"{x[:trim_url_limit]}..."
264
+
265
+ return x
266
+
267
+ else:
268
+
269
+ def trim_url(x: str) -> str:
270
+ return x
271
+
272
+ words = re.split(r"(\s+)", str(markupsafe.escape(text)))
273
+ rel_attr = f' rel="{markupsafe.escape(rel)}"' if rel else ""
274
+ target_attr = f' target="{markupsafe.escape(target)}"' if target else ""
275
+
276
+ for i, word in enumerate(words):
277
+ head, middle, tail = "", word, ""
278
+ match = re.match(r"^([(<]|&lt;)+", middle)
279
+
280
+ if match:
281
+ head = match.group()
282
+ middle = middle[match.end() :]
283
+
284
+ # Unlike lead, which is anchored to the start of the string,
285
+ # need to check that the string ends with any of the characters
286
+ # before trying to match all of them, to avoid backtracking.
287
+ if middle.endswith((")", ">", ".", ",", "\n", "&gt;")):
288
+ match = re.search(r"([)>.,\n]|&gt;)+$", middle)
289
+
290
+ if match:
291
+ tail = match.group()
292
+ middle = middle[: match.start()]
293
+
294
+ # Prefer balancing parentheses in URLs instead of ignoring a
295
+ # trailing character.
296
+ for start_char, end_char in ("(", ")"), ("<", ">"), ("&lt;", "&gt;"):
297
+ start_count = middle.count(start_char)
298
+
299
+ if start_count <= middle.count(end_char):
300
+ # Balanced, or lighter on the left
301
+ continue
302
+
303
+ # Move as many as possible from the tail to balance
304
+ for _ in range(min(start_count, tail.count(end_char))):
305
+ end_index = tail.index(end_char) + len(end_char)
306
+ # Move anything in the tail before the end char too
307
+ middle += tail[:end_index]
308
+ tail = tail[end_index:]
309
+
310
+ if _http_re.match(middle):
311
+ if middle.startswith("https://") or middle.startswith("http://"):
312
+ middle = (
313
+ f'<a href="{middle}"{rel_attr}{target_attr}>{trim_url(middle)}</a>'
314
+ )
315
+ else:
316
+ middle = (
317
+ f'<a href="https://{middle}"{rel_attr}{target_attr}>'
318
+ f"{trim_url(middle)}</a>"
319
+ )
320
+
321
+ elif middle.startswith("mailto:") and _email_re.match(middle[7:]):
322
+ middle = f'<a href="{middle}">{middle[7:]}</a>'
323
+
324
+ elif (
325
+ "@" in middle
326
+ and not middle.startswith("www.")
327
+ and ":" not in middle
328
+ and _email_re.match(middle)
329
+ ):
330
+ middle = f'<a href="mailto:{middle}">{middle}</a>'
331
+
332
+ elif extra_schemes is not None:
333
+ for scheme in extra_schemes:
334
+ if middle != scheme and middle.startswith(scheme):
335
+ middle = f'<a href="{middle}"{rel_attr}{target_attr}>{middle}</a>'
336
+
337
+ words[i] = f"{head}{middle}{tail}"
338
+
339
+ return "".join(words)
340
+
341
+
342
+ def generate_lorem_ipsum(
343
+ n: int = 5, html: bool = True, min: int = 20, max: int = 100
344
+ ) -> str:
345
+ """Generate some lorem ipsum for the template."""
346
+ from .constants import LOREM_IPSUM_WORDS
347
+
348
+ words = LOREM_IPSUM_WORDS.split()
349
+ result = []
350
+
351
+ for _ in range(n):
352
+ next_capitalized = True
353
+ last_comma = last_fullstop = 0
354
+ word = None
355
+ last = None
356
+ p = []
357
+
358
+ # each paragraph contains out of 20 to 100 words.
359
+ for idx, _ in enumerate(range(randrange(min, max))):
360
+ while True:
361
+ word = choice(words)
362
+ if word != last:
363
+ last = word
364
+ break
365
+ if next_capitalized:
366
+ word = word.capitalize()
367
+ next_capitalized = False
368
+ # add commas
369
+ if idx - randrange(3, 8) > last_comma:
370
+ last_comma = idx
371
+ last_fullstop += 2
372
+ word += ","
373
+ # add end of sentences
374
+ if idx - randrange(10, 20) > last_fullstop:
375
+ last_comma = last_fullstop = idx
376
+ word += "."
377
+ next_capitalized = True
378
+ p.append(word)
379
+
380
+ # ensure that the paragraph ends with a dot.
381
+ p_str = " ".join(p)
382
+
383
+ if p_str.endswith(","):
384
+ p_str = p_str[:-1] + "."
385
+ elif not p_str.endswith("."):
386
+ p_str += "."
387
+
388
+ result.append(p_str)
389
+
390
+ if not html:
391
+ return "\n\n".join(result)
392
+ return markupsafe.Markup(
393
+ "\n".join(f"<p>{markupsafe.escape(x)}</p>" for x in result)
394
+ )
395
+
396
+
397
+ def url_quote(obj: t.Any, charset: str = "utf-8", for_qs: bool = False) -> str:
398
+ """Quote a string for use in a URL using the given charset.
399
+
400
+ :param obj: String or bytes to quote. Other types are converted to
401
+ string then encoded to bytes using the given charset.
402
+ :param charset: Encode text to bytes using this charset.
403
+ :param for_qs: Quote "/" and use "+" for spaces.
404
+ """
405
+ if not isinstance(obj, bytes):
406
+ if not isinstance(obj, str):
407
+ obj = str(obj)
408
+
409
+ obj = obj.encode(charset)
410
+
411
+ safe = b"" if for_qs else b"/"
412
+ rv = quote_from_bytes(obj, safe)
413
+
414
+ if for_qs:
415
+ rv = rv.replace("%20", "+")
416
+
417
+ return rv
418
+
419
+
420
+ @abc.MutableMapping.register
421
+ class LRUCache:
422
+ """A simple LRU Cache implementation."""
423
+
424
+ # this is fast for small capacities (something below 1000) but doesn't
425
+ # scale. But as long as it's only used as storage for templates this
426
+ # won't do any harm.
427
+
428
+ def __init__(self, capacity: int) -> None:
429
+ self.capacity = capacity
430
+ self._mapping: t.Dict[t.Any, t.Any] = {}
431
+ self._queue: "te.Deque[t.Any]" = deque()
432
+ self._postinit()
433
+
434
+ def _postinit(self) -> None:
435
+ # alias all queue methods for faster lookup
436
+ self._popleft = self._queue.popleft
437
+ self._pop = self._queue.pop
438
+ self._remove = self._queue.remove
439
+ self._wlock = Lock()
440
+ self._append = self._queue.append
441
+
442
+ def __getstate__(self) -> t.Mapping[str, t.Any]:
443
+ return {
444
+ "capacity": self.capacity,
445
+ "_mapping": self._mapping,
446
+ "_queue": self._queue,
447
+ }
448
+
449
+ def __setstate__(self, d: t.Mapping[str, t.Any]) -> None:
450
+ self.__dict__.update(d)
451
+ self._postinit()
452
+
453
+ def __getnewargs__(self) -> t.Tuple:
454
+ return (self.capacity,)
455
+
456
+ def copy(self) -> "LRUCache":
457
+ """Return a shallow copy of the instance."""
458
+ rv = self.__class__(self.capacity)
459
+ rv._mapping.update(self._mapping)
460
+ rv._queue.extend(self._queue)
461
+ return rv
462
+
463
+ def get(self, key: t.Any, default: t.Any = None) -> t.Any:
464
+ """Return an item from the cache dict or `default`"""
465
+ try:
466
+ return self[key]
467
+ except KeyError:
468
+ return default
469
+
470
+ def setdefault(self, key: t.Any, default: t.Any = None) -> t.Any:
471
+ """Set `default` if the key is not in the cache otherwise
472
+ leave unchanged. Return the value of this key.
473
+ """
474
+ try:
475
+ return self[key]
476
+ except KeyError:
477
+ self[key] = default
478
+ return default
479
+
480
+ def clear(self) -> None:
481
+ """Clear the cache."""
482
+ with self._wlock:
483
+ self._mapping.clear()
484
+ self._queue.clear()
485
+
486
+ def __contains__(self, key: t.Any) -> bool:
487
+ """Check if a key exists in this cache."""
488
+ return key in self._mapping
489
+
490
+ def __len__(self) -> int:
491
+ """Return the current size of the cache."""
492
+ return len(self._mapping)
493
+
494
+ def __repr__(self) -> str:
495
+ return f"<{type(self).__name__} {self._mapping!r}>"
496
+
497
+ def __getitem__(self, key: t.Any) -> t.Any:
498
+ """Get an item from the cache. Moves the item up so that it has the
499
+ highest priority then.
500
+
501
+ Raise a `KeyError` if it does not exist.
502
+ """
503
+ with self._wlock:
504
+ rv = self._mapping[key]
505
+
506
+ if self._queue[-1] != key:
507
+ try:
508
+ self._remove(key)
509
+ except ValueError:
510
+ # if something removed the key from the container
511
+ # when we read, ignore the ValueError that we would
512
+ # get otherwise.
513
+ pass
514
+
515
+ self._append(key)
516
+
517
+ return rv
518
+
519
+ def __setitem__(self, key: t.Any, value: t.Any) -> None:
520
+ """Sets the value for an item. Moves the item up so that it
521
+ has the highest priority then.
522
+ """
523
+ with self._wlock:
524
+ if key in self._mapping:
525
+ self._remove(key)
526
+ elif len(self._mapping) == self.capacity:
527
+ del self._mapping[self._popleft()]
528
+
529
+ self._append(key)
530
+ self._mapping[key] = value
531
+
532
+ def __delitem__(self, key: t.Any) -> None:
533
+ """Remove an item from the cache dict.
534
+ Raise a `KeyError` if it does not exist.
535
+ """
536
+ with self._wlock:
537
+ del self._mapping[key]
538
+
539
+ try:
540
+ self._remove(key)
541
+ except ValueError:
542
+ pass
543
+
544
+ def items(self) -> t.Iterable[t.Tuple[t.Any, t.Any]]:
545
+ """Return a list of items."""
546
+ result = [(key, self._mapping[key]) for key in list(self._queue)]
547
+ result.reverse()
548
+ return result
549
+
550
+ def values(self) -> t.Iterable[t.Any]:
551
+ """Return a list of all values."""
552
+ return [x[1] for x in self.items()]
553
+
554
+ def keys(self) -> t.Iterable[t.Any]:
555
+ """Return a list of all keys ordered by most recent usage."""
556
+ return list(self)
557
+
558
+ def __iter__(self) -> t.Iterator[t.Any]:
559
+ return reversed(tuple(self._queue))
560
+
561
+ def __reversed__(self) -> t.Iterator[t.Any]:
562
+ """Iterate over the keys in the cache dict, oldest items
563
+ coming first.
564
+ """
565
+ return iter(tuple(self._queue))
566
+
567
+ __copy__ = copy
568
+
569
+
570
+ def select_autoescape(
571
+ enabled_extensions: t.Collection[str] = ("html", "htm", "xml"),
572
+ disabled_extensions: t.Collection[str] = (),
573
+ default_for_string: bool = True,
574
+ default: bool = False,
575
+ ) -> t.Callable[[t.Optional[str]], bool]:
576
+ """Intelligently sets the initial value of autoescaping based on the
577
+ filename of the template. This is the recommended way to configure
578
+ autoescaping if you do not want to write a custom function yourself.
579
+
580
+ If you want to enable it for all templates created from strings or
581
+ for all templates with `.html` and `.xml` extensions::
582
+
583
+ from jinja2 import Environment, select_autoescape
584
+ env = Environment(autoescape=select_autoescape(
585
+ enabled_extensions=('html', 'xml'),
586
+ default_for_string=True,
587
+ ))
588
+
589
+ Example configuration to turn it on at all times except if the template
590
+ ends with `.txt`::
591
+
592
+ from jinja2 import Environment, select_autoescape
593
+ env = Environment(autoescape=select_autoescape(
594
+ disabled_extensions=('txt',),
595
+ default_for_string=True,
596
+ default=True,
597
+ ))
598
+
599
+ The `enabled_extensions` is an iterable of all the extensions that
600
+ autoescaping should be enabled for. Likewise `disabled_extensions` is
601
+ a list of all templates it should be disabled for. If a template is
602
+ loaded from a string then the default from `default_for_string` is used.
603
+ If nothing matches then the initial value of autoescaping is set to the
604
+ value of `default`.
605
+
606
+ For security reasons this function operates case insensitive.
607
+
608
+ .. versionadded:: 2.9
609
+ """
610
+ enabled_patterns = tuple(f".{x.lstrip('.').lower()}" for x in enabled_extensions)
611
+ disabled_patterns = tuple(f".{x.lstrip('.').lower()}" for x in disabled_extensions)
612
+
613
+ def autoescape(template_name: t.Optional[str]) -> bool:
614
+ if template_name is None:
615
+ return default_for_string
616
+ template_name = template_name.lower()
617
+ if template_name.endswith(enabled_patterns):
618
+ return True
619
+ if template_name.endswith(disabled_patterns):
620
+ return False
621
+ return default
622
+
623
+ return autoescape
624
+
625
+
626
+ def htmlsafe_json_dumps(
627
+ obj: t.Any, dumps: t.Optional[t.Callable[..., str]] = None, **kwargs: t.Any
628
+ ) -> markupsafe.Markup:
629
+ """Serialize an object to a string of JSON with :func:`json.dumps`,
630
+ then replace HTML-unsafe characters with Unicode escapes and mark
631
+ the result safe with :class:`~markupsafe.Markup`.
632
+
633
+ This is available in templates as the ``|tojson`` filter.
634
+
635
+ The following characters are escaped: ``<``, ``>``, ``&``, ``'``.
636
+
637
+ The returned string is safe to render in HTML documents and
638
+ ``<script>`` tags. The exception is in HTML attributes that are
639
+ double quoted; either use single quotes or the ``|forceescape``
640
+ filter.
641
+
642
+ :param obj: The object to serialize to JSON.
643
+ :param dumps: The ``dumps`` function to use. Defaults to
644
+ ``env.policies["json.dumps_function"]``, which defaults to
645
+ :func:`json.dumps`.
646
+ :param kwargs: Extra arguments to pass to ``dumps``. Merged onto
647
+ ``env.policies["json.dumps_kwargs"]``.
648
+
649
+ .. versionchanged:: 3.0
650
+ The ``dumper`` parameter is renamed to ``dumps``.
651
+
652
+ .. versionadded:: 2.9
653
+ """
654
+ if dumps is None:
655
+ dumps = json.dumps
656
+
657
+ return markupsafe.Markup(
658
+ dumps(obj, **kwargs)
659
+ .replace("<", "\\u003c")
660
+ .replace(">", "\\u003e")
661
+ .replace("&", "\\u0026")
662
+ .replace("'", "\\u0027")
663
+ )
664
+
665
+
666
+ class Cycler:
667
+ """Cycle through values by yield them one at a time, then restarting
668
+ once the end is reached. Available as ``cycler`` in templates.
669
+
670
+ Similar to ``loop.cycle``, but can be used outside loops or across
671
+ multiple loops. For example, render a list of folders and files in a
672
+ list, alternating giving them "odd" and "even" classes.
673
+
674
+ .. code-block:: html+jinja
675
+
676
+ {% set row_class = cycler("odd", "even") %}
677
+ <ul class="browser">
678
+ {% for folder in folders %}
679
+ <li class="folder {{ row_class.next() }}">{{ folder }}
680
+ {% endfor %}
681
+ {% for file in files %}
682
+ <li class="file {{ row_class.next() }}">{{ file }}
683
+ {% endfor %}
684
+ </ul>
685
+
686
+ :param items: Each positional argument will be yielded in the order
687
+ given for each cycle.
688
+
689
+ .. versionadded:: 2.1
690
+ """
691
+
692
+ def __init__(self, *items: t.Any) -> None:
693
+ if not items:
694
+ raise RuntimeError("at least one item has to be provided")
695
+ self.items = items
696
+ self.pos = 0
697
+
698
+ def reset(self) -> None:
699
+ """Resets the current item to the first item."""
700
+ self.pos = 0
701
+
702
+ @property
703
+ def current(self) -> t.Any:
704
+ """Return the current item. Equivalent to the item that will be
705
+ returned next time :meth:`next` is called.
706
+ """
707
+ return self.items[self.pos]
708
+
709
+ def next(self) -> t.Any:
710
+ """Return the current item, then advance :attr:`current` to the
711
+ next item.
712
+ """
713
+ rv = self.current
714
+ self.pos = (self.pos + 1) % len(self.items)
715
+ return rv
716
+
717
+ __next__ = next
718
+
719
+
720
+ class Joiner:
721
+ """A joining helper for templates."""
722
+
723
+ def __init__(self, sep: str = ", ") -> None:
724
+ self.sep = sep
725
+ self.used = False
726
+
727
+ def __call__(self) -> str:
728
+ if not self.used:
729
+ self.used = True
730
+ return ""
731
+ return self.sep
732
+
733
+
734
+ class Namespace:
735
+ """A namespace object that can hold arbitrary attributes. It may be
736
+ initialized from a dictionary or with keyword arguments."""
737
+
738
+ def __init__(*args: t.Any, **kwargs: t.Any) -> None: # noqa: B902
739
+ self, args = args[0], args[1:]
740
+ self.__attrs = dict(*args, **kwargs)
741
+
742
+ def __getattribute__(self, name: str) -> t.Any:
743
+ # __class__ is needed for the awaitable check in async mode
744
+ if name in {"_Namespace__attrs", "__class__"}:
745
+ return object.__getattribute__(self, name)
746
+ try:
747
+ return self.__attrs[name]
748
+ except KeyError:
749
+ raise AttributeError(name) from None
750
+
751
+ def __setitem__(self, name: str, value: t.Any) -> None:
752
+ self.__attrs[name] = value
753
+
754
+ def __repr__(self) -> str:
755
+ return f"<Namespace {self.__attrs!r}>"
tuning-competition-baseline/.venv/lib/python3.11/site-packages/nvidia_cufft_cu11-10.9.0.58.dist-info/License.txt ADDED
@@ -0,0 +1,1568 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ End User License Agreement
2
+ --------------------------
3
+
4
+
5
+ Preface
6
+ -------
7
+
8
+ The Software License Agreement in Chapter 1 and the Supplement
9
+ in Chapter 2 contain license terms and conditions that govern
10
+ the use of NVIDIA software. By accepting this agreement, you
11
+ agree to comply with all the terms and conditions applicable
12
+ to the product(s) included herein.
13
+
14
+
15
+ NVIDIA Driver
16
+
17
+
18
+ Description
19
+
20
+ This package contains the operating system driver and
21
+ fundamental system software components for NVIDIA GPUs.
22
+
23
+
24
+ NVIDIA CUDA Toolkit
25
+
26
+
27
+ Description
28
+
29
+ The NVIDIA CUDA Toolkit provides command-line and graphical
30
+ tools for building, debugging and optimizing the performance
31
+ of applications accelerated by NVIDIA GPUs, runtime and math
32
+ libraries, and documentation including programming guides,
33
+ user manuals, and API references.
34
+
35
+
36
+ Default Install Location of CUDA Toolkit
37
+
38
+ Windows platform:
39
+
40
+ %ProgramFiles%\NVIDIA GPU Computing Toolkit\CUDA\v#.#
41
+
42
+ Linux platform:
43
+
44
+ /usr/local/cuda-#.#
45
+
46
+ Mac platform:
47
+
48
+ /Developer/NVIDIA/CUDA-#.#
49
+
50
+
51
+ NVIDIA CUDA Samples
52
+
53
+
54
+ Description
55
+
56
+ This package includes over 100+ CUDA examples that demonstrate
57
+ various CUDA programming principles, and efficient CUDA
58
+ implementation of algorithms in specific application domains.
59
+
60
+
61
+ Default Install Location of CUDA Samples
62
+
63
+ Windows platform:
64
+
65
+ %ProgramData%\NVIDIA Corporation\CUDA Samples\v#.#
66
+
67
+ Linux platform:
68
+
69
+ /usr/local/cuda-#.#/samples
70
+
71
+ and
72
+
73
+ $HOME/NVIDIA_CUDA-#.#_Samples
74
+
75
+ Mac platform:
76
+
77
+ /Developer/NVIDIA/CUDA-#.#/samples
78
+
79
+
80
+ NVIDIA Nsight Visual Studio Edition (Windows only)
81
+
82
+
83
+ Description
84
+
85
+ NVIDIA Nsight Development Platform, Visual Studio Edition is a
86
+ development environment integrated into Microsoft Visual
87
+ Studio that provides tools for debugging, profiling, analyzing
88
+ and optimizing your GPU computing and graphics applications.
89
+
90
+
91
+ Default Install Location of Nsight Visual Studio Edition
92
+
93
+ Windows platform:
94
+
95
+ %ProgramFiles(x86)%\NVIDIA Corporation\Nsight Visual Studio Edition #.#
96
+
97
+
98
+ 1. License Agreement for NVIDIA Software Development Kits
99
+ ---------------------------------------------------------
100
+
101
+
102
+ Release Date: July 26, 2018
103
+ ---------------------------
104
+
105
+
106
+ Important NoticeRead before downloading, installing,
107
+ copying or using the licensed software:
108
+ -------------------------------------------------------
109
+
110
+ This license agreement, including exhibits attached
111
+ ("Agreement”) is a legal agreement between you and NVIDIA
112
+ Corporation ("NVIDIA") and governs your use of a NVIDIA
113
+ software development kit (“SDK”).
114
+
115
+ Each SDK has its own set of software and materials, but here
116
+ is a description of the types of items that may be included in
117
+ a SDK: source code, header files, APIs, data sets and assets
118
+ (examples include images, textures, models, scenes, videos,
119
+ native API input/output files), binary software, sample code,
120
+ libraries, utility programs, programming code and
121
+ documentation.
122
+
123
+ This Agreement can be accepted only by an adult of legal age
124
+ of majority in the country in which the SDK is used.
125
+
126
+ If you are entering into this Agreement on behalf of a company
127
+ or other legal entity, you represent that you have the legal
128
+ authority to bind the entity to this Agreement, in which case
129
+ “you” will mean the entity you represent.
130
+
131
+ If you don’t have the required age or authority to accept
132
+ this Agreement, or if you don’t accept all the terms and
133
+ conditions of this Agreement, do not download, install or use
134
+ the SDK.
135
+
136
+ You agree to use the SDK only for purposes that are permitted
137
+ by (a) this Agreement, and (b) any applicable law, regulation
138
+ or generally accepted practices or guidelines in the relevant
139
+ jurisdictions.
140
+
141
+
142
+ 1.1. License
143
+
144
+
145
+ 1.1.1. License Grant
146
+
147
+ Subject to the terms of this Agreement, NVIDIA hereby grants
148
+ you a non-exclusive, non-transferable license, without the
149
+ right to sublicense (except as expressly provided in this
150
+ Agreement) to:
151
+
152
+ 1. Install and use the SDK,
153
+
154
+ 2. Modify and create derivative works of sample source code
155
+ delivered in the SDK, and
156
+
157
+ 3. Distribute those portions of the SDK that are identified
158
+ in this Agreement as distributable, as incorporated in
159
+ object code format into a software application that meets
160
+ the distribution requirements indicated in this Agreement.
161
+
162
+
163
+ 1.1.2. Distribution Requirements
164
+
165
+ These are the distribution requirements for you to exercise
166
+ the distribution grant:
167
+
168
+ 1. Your application must have material additional
169
+ functionality, beyond the included portions of the SDK.
170
+
171
+ 2. The distributable portions of the SDK shall only be
172
+ accessed by your application.
173
+
174
+ 3. The following notice shall be included in modifications
175
+ and derivative works of sample source code distributed:
176
+ “This software contains source code provided by NVIDIA
177
+ Corporation.”
178
+
179
+ 4. Unless a developer tool is identified in this Agreement
180
+ as distributable, it is delivered for your internal use
181
+ only.
182
+
183
+ 5. The terms under which you distribute your application
184
+ must be consistent with the terms of this Agreement,
185
+ including (without limitation) terms relating to the
186
+ license grant and license restrictions and protection of
187
+ NVIDIA’s intellectual property rights. Additionally, you
188
+ agree that you will protect the privacy, security and
189
+ legal rights of your application users.
190
+
191
+ 6. You agree to notify NVIDIA in writing of any known or
192
+ suspected distribution or use of the SDK not in compliance
193
+ with the requirements of this Agreement, and to enforce
194
+ the terms of your agreements with respect to distributed
195
+ SDK.
196
+
197
+
198
+ 1.1.3. Authorized Users
199
+
200
+ You may allow employees and contractors of your entity or of
201
+ your subsidiary(ies) to access and use the SDK from your
202
+ secure network to perform work on your behalf.
203
+
204
+ If you are an academic institution you may allow users
205
+ enrolled or employed by the academic institution to access and
206
+ use the SDK from your secure network.
207
+
208
+ You are responsible for the compliance with the terms of this
209
+ Agreement by your authorized users. If you become aware that
210
+ your authorized users didn’t follow the terms of this
211
+ Agreement, you agree to take reasonable steps to resolve the
212
+ non-compliance and prevent new occurrences.
213
+
214
+
215
+ 1.1.4. Pre-Release SDK
216
+
217
+ The SDK versions identified as alpha, beta, preview or
218
+ otherwise as pre-release, may not be fully functional, may
219
+ contain errors or design flaws, and may have reduced or
220
+ different security, privacy, accessibility, availability, and
221
+ reliability standards relative to commercial versions of
222
+ NVIDIA software and materials. Use of a pre-release SDK may
223
+ result in unexpected results, loss of data, project delays or
224
+ other unpredictable damage or loss.
225
+
226
+ You may use a pre-release SDK at your own risk, understanding
227
+ that pre-release SDKs are not intended for use in production
228
+ or business-critical systems.
229
+
230
+ NVIDIA may choose not to make available a commercial version
231
+ of any pre-release SDK. NVIDIA may also choose to abandon
232
+ development and terminate the availability of a pre-release
233
+ SDK at any time without liability.
234
+
235
+
236
+ 1.1.5. Updates
237
+
238
+ NVIDIA may, at its option, make available patches, workarounds
239
+ or other updates to this SDK. Unless the updates are provided
240
+ with their separate governing terms, they are deemed part of
241
+ the SDK licensed to you as provided in this Agreement. You
242
+ agree that the form and content of the SDK that NVIDIA
243
+ provides may change without prior notice to you. While NVIDIA
244
+ generally maintains compatibility between versions, NVIDIA may
245
+ in some cases make changes that introduce incompatibilities in
246
+ future versions of the SDK.
247
+
248
+
249
+ 1.1.6. Third Party Licenses
250
+
251
+ The SDK may come bundled with, or otherwise include or be
252
+ distributed with, third party software licensed by a NVIDIA
253
+ supplier and/or open source software provided under an open
254
+ source license. Use of third party software is subject to the
255
+ third-party license terms, or in the absence of third party
256
+ terms, the terms of this Agreement. Copyright to third party
257
+ software is held by the copyright holders indicated in the
258
+ third-party software or license.
259
+
260
+
261
+ 1.1.7. Reservation of Rights
262
+
263
+ NVIDIA reserves all rights, title, and interest in and to the
264
+ SDK, not expressly granted to you under this Agreement.
265
+
266
+
267
+ 1.2. Limitations
268
+
269
+ The following license limitations apply to your use of the
270
+ SDK:
271
+
272
+ 1. You may not reverse engineer, decompile or disassemble,
273
+ or remove copyright or other proprietary notices from any
274
+ portion of the SDK or copies of the SDK.
275
+
276
+ 2. Except as expressly provided in this Agreement, you may
277
+ not copy, sell, rent, sublicense, transfer, distribute,
278
+ modify, or create derivative works of any portion of the
279
+ SDK. For clarity, you may not distribute or sublicense the
280
+ SDK as a stand-alone product.
281
+
282
+ 3. Unless you have an agreement with NVIDIA for this
283
+ purpose, you may not indicate that an application created
284
+ with the SDK is sponsored or endorsed by NVIDIA.
285
+
286
+ 4. You may not bypass, disable, or circumvent any
287
+ encryption, security, digital rights management or
288
+ authentication mechanism in the SDK.
289
+
290
+ 5. You may not use the SDK in any manner that would cause it
291
+ to become subject to an open source software license. As
292
+ examples, licenses that require as a condition of use,
293
+ modification, and/or distribution that the SDK be:
294
+
295
+ a. Disclosed or distributed in source code form;
296
+
297
+ b. Licensed for the purpose of making derivative works;
298
+ or
299
+
300
+ c. Redistributable at no charge.
301
+
302
+ 6. Unless you have an agreement with NVIDIA for this
303
+ purpose, you may not use the SDK with any system or
304
+ application where the use or failure of the system or
305
+ application can reasonably be expected to threaten or
306
+ result in personal injury, death, or catastrophic loss.
307
+ Examples include use in avionics, navigation, military,
308
+ medical, life support or other life critical applications.
309
+ NVIDIA does not design, test or manufacture the SDK for
310
+ these critical uses and NVIDIA shall not be liable to you
311
+ or any third party, in whole or in part, for any claims or
312
+ damages arising from such uses.
313
+
314
+ 7. You agree to defend, indemnify and hold harmless NVIDIA
315
+ and its affiliates, and their respective employees,
316
+ contractors, agents, officers and directors, from and
317
+ against any and all claims, damages, obligations, losses,
318
+ liabilities, costs or debt, fines, restitutions and
319
+ expenses (including but not limited to attorney’s fees
320
+ and costs incident to establishing the right of
321
+ indemnification) arising out of or related to your use of
322
+ the SDK outside of the scope of this Agreement, or not in
323
+ compliance with its terms.
324
+
325
+
326
+ 1.3. Ownership
327
+
328
+ 1. NVIDIA or its licensors hold all rights, title and
329
+ interest in and to the SDK and its modifications and
330
+ derivative works, including their respective intellectual
331
+ property rights, subject to your rights described in this
332
+ section. This SDK may include software and materials from
333
+ NVIDIA’s licensors, and these licensors are intended
334
+ third party beneficiaries that may enforce this Agreement
335
+ with respect to their intellectual property rights.
336
+
337
+ 2. You hold all rights, title and interest in and to your
338
+ applications and your derivative works of the sample
339
+ source code delivered in the SDK, including their
340
+ respective intellectual property rights, subject to
341
+ NVIDIA’s rights described in this section.
342
+
343
+ 3. You may, but don’t have to, provide to NVIDIA
344
+ suggestions, feature requests or other feedback regarding
345
+ the SDK, including possible enhancements or modifications
346
+ to the SDK. For any feedback that you voluntarily provide,
347
+ you hereby grant NVIDIA and its affiliates a perpetual,
348
+ non-exclusive, worldwide, irrevocable license to use,
349
+ reproduce, modify, license, sublicense (through multiple
350
+ tiers of sublicensees), and distribute (through multiple
351
+ tiers of distributors) it without the payment of any
352
+ royalties or fees to you. NVIDIA will use feedback at its
353
+ choice. NVIDIA is constantly looking for ways to improve
354
+ its products, so you may send feedback to NVIDIA through
355
+ the developer portal at https://developer.nvidia.com.
356
+
357
+
358
+ 1.4. No Warranties
359
+
360
+ THE SDK IS PROVIDED BY NVIDIA “AS IS” AND “WITH ALL
361
+ FAULTS.” TO THE MAXIMUM EXTENT PERMITTED BY LAW, NVIDIA AND
362
+ ITS AFFILIATES EXPRESSLY DISCLAIM ALL WARRANTIES OF ANY KIND
363
+ OR NATURE, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING,
364
+ BUT NOT LIMITED TO, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
365
+ FOR A PARTICULAR PURPOSE, TITLE, NON-INFRINGEMENT, OR THE
366
+ ABSENCE OF ANY DEFECTS THEREIN, WHETHER LATENT OR PATENT. NO
367
+ WARRANTY IS MADE ON THE BASIS OF TRADE USAGE, COURSE OF
368
+ DEALING OR COURSE OF TRADE.
369
+
370
+
371
+ 1.5. Limitation of Liability
372
+
373
+ TO THE MAXIMUM EXTENT PERMITTED BY LAW, NVIDIA AND ITS
374
+ AFFILIATES SHALL NOT BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
375
+ PUNITIVE OR CONSEQUENTIAL DAMAGES, OR ANY LOST PROFITS, LOSS
376
+ OF USE, LOSS OF DATA OR LOSS OF GOODWILL, OR THE COSTS OF
377
+ PROCURING SUBSTITUTE PRODUCTS, ARISING OUT OF OR IN CONNECTION
378
+ WITH THIS AGREEMENT OR THE USE OR PERFORMANCE OF THE SDK,
379
+ WHETHER SUCH LIABILITY ARISES FROM ANY CLAIM BASED UPON BREACH
380
+ OF CONTRACT, BREACH OF WARRANTY, TORT (INCLUDING NEGLIGENCE),
381
+ PRODUCT LIABILITY OR ANY OTHER CAUSE OF ACTION OR THEORY OF
382
+ LIABILITY. IN NO EVENT WILL NVIDIA’S AND ITS AFFILIATES
383
+ TOTAL CUMULATIVE LIABILITY UNDER OR ARISING OUT OF THIS
384
+ AGREEMENT EXCEED US$10.00. THE NATURE OF THE LIABILITY OR THE
385
+ NUMBER OF CLAIMS OR SUITS SHALL NOT ENLARGE OR EXTEND THIS
386
+ LIMIT.
387
+
388
+ These exclusions and limitations of liability shall apply
389
+ regardless if NVIDIA or its affiliates have been advised of
390
+ the possibility of such damages, and regardless of whether a
391
+ remedy fails its essential purpose. These exclusions and
392
+ limitations of liability form an essential basis of the
393
+ bargain between the parties, and, absent any of these
394
+ exclusions or limitations of liability, the provisions of this
395
+ Agreement, including, without limitation, the economic terms,
396
+ would be substantially different.
397
+
398
+
399
+ 1.6. Termination
400
+
401
+ 1. This Agreement will continue to apply until terminated by
402
+ either you or NVIDIA as described below.
403
+
404
+ 2. If you want to terminate this Agreement, you may do so by
405
+ stopping to use the SDK.
406
+
407
+ 3. NVIDIA may, at any time, terminate this Agreement if:
408
+
409
+ a. (i) you fail to comply with any term of this
410
+ Agreement and the non-compliance is not fixed within
411
+ thirty (30) days following notice from NVIDIA (or
412
+ immediately if you violate NVIDIA’s intellectual
413
+ property rights);
414
+
415
+ b. (ii) you commence or participate in any legal
416
+ proceeding against NVIDIA with respect to the SDK; or
417
+
418
+ c. (iii) NVIDIA decides to no longer provide the SDK in
419
+ a country or, in NVIDIA’s sole discretion, the
420
+ continued use of it is no longer commercially viable.
421
+
422
+ 4. Upon any termination of this Agreement, you agree to
423
+ promptly discontinue use of the SDK and destroy all copies
424
+ in your possession or control. Your prior distributions in
425
+ accordance with this Agreement are not affected by the
426
+ termination of this Agreement. Upon written request, you
427
+ will certify in writing that you have complied with your
428
+ commitments under this section. Upon any termination of
429
+ this Agreement all provisions survive except for the
430
+ license grant provisions.
431
+
432
+
433
+ 1.7. General
434
+
435
+ If you wish to assign this Agreement or your rights and
436
+ obligations, including by merger, consolidation, dissolution
437
+ or operation of law, contact NVIDIA to ask for permission. Any
438
+ attempted assignment not approved by NVIDIA in writing shall
439
+ be void and of no effect. NVIDIA may assign, delegate or
440
+ transfer this Agreement and its rights and obligations, and if
441
+ to a non-affiliate you will be notified.
442
+
443
+ You agree to cooperate with NVIDIA and provide reasonably
444
+ requested information to verify your compliance with this
445
+ Agreement.
446
+
447
+ This Agreement will be governed in all respects by the laws of
448
+ the United States and of the State of Delaware as those laws
449
+ are applied to contracts entered into and performed entirely
450
+ within Delaware by Delaware residents, without regard to the
451
+ conflicts of laws principles. The United Nations Convention on
452
+ Contracts for the International Sale of Goods is specifically
453
+ disclaimed. You agree to all terms of this Agreement in the
454
+ English language.
455
+
456
+ The state or federal courts residing in Santa Clara County,
457
+ California shall have exclusive jurisdiction over any dispute
458
+ or claim arising out of this Agreement. Notwithstanding this,
459
+ you agree that NVIDIA shall still be allowed to apply for
460
+ injunctive remedies or an equivalent type of urgent legal
461
+ relief in any jurisdiction.
462
+
463
+ If any court of competent jurisdiction determines that any
464
+ provision of this Agreement is illegal, invalid or
465
+ unenforceable, such provision will be construed as limited to
466
+ the extent necessary to be consistent with and fully
467
+ enforceable under the law and the remaining provisions will
468
+ remain in full force and effect. Unless otherwise specified,
469
+ remedies are cumulative.
470
+
471
+ Each party acknowledges and agrees that the other is an
472
+ independent contractor in the performance of this Agreement.
473
+
474
+ The SDK has been developed entirely at private expense and is
475
+ “commercial items” consisting of “commercial computer
476
+ software” and “commercial computer software
477
+ documentation” provided with RESTRICTED RIGHTS. Use,
478
+ duplication or disclosure by the U.S. Government or a U.S.
479
+ Government subcontractor is subject to the restrictions in
480
+ this Agreement pursuant to DFARS 227.7202-3(a) or as set forth
481
+ in subparagraphs (c)(1) and (2) of the Commercial Computer
482
+ Software - Restricted Rights clause at FAR 52.227-19, as
483
+ applicable. Contractor/manufacturer is NVIDIA, 2788 San Tomas
484
+ Expressway, Santa Clara, CA 95051.
485
+
486
+ The SDK is subject to United States export laws and
487
+ regulations. You agree that you will not ship, transfer or
488
+ export the SDK into any country, or use the SDK in any manner,
489
+ prohibited by the United States Bureau of Industry and
490
+ Security or economic sanctions regulations administered by the
491
+ U.S. Department of Treasury’s Office of Foreign Assets
492
+ Control (OFAC), or any applicable export laws, restrictions or
493
+ regulations. These laws include restrictions on destinations,
494
+ end users and end use. By accepting this Agreement, you
495
+ confirm that you are not a resident or citizen of any country
496
+ currently embargoed by the U.S. and that you are not otherwise
497
+ prohibited from receiving the SDK.
498
+
499
+ Any notice delivered by NVIDIA to you under this Agreement
500
+ will be delivered via mail, email or fax. You agree that any
501
+ notices that NVIDIA sends you electronically will satisfy any
502
+ legal communication requirements. Please direct your legal
503
+ notices or other correspondence to NVIDIA Corporation, 2788
504
+ San Tomas Expressway, Santa Clara, California 95051, United
505
+ States of America, Attention: Legal Department.
506
+
507
+ This Agreement and any exhibits incorporated into this
508
+ Agreement constitute the entire agreement of the parties with
509
+ respect to the subject matter of this Agreement and supersede
510
+ all prior negotiations or documentation exchanged between the
511
+ parties relating to this SDK license. Any additional and/or
512
+ conflicting terms on documents issued by you are null, void,
513
+ and invalid. Any amendment or waiver under this Agreement
514
+ shall be in writing and signed by representatives of both
515
+ parties.
516
+
517
+
518
+ 2. CUDA Toolkit Supplement to Software License Agreement for
519
+ NVIDIA Software Development Kits
520
+ ------------------------------------------------------------
521
+
522
+
523
+ Release date: August 16, 2018
524
+ -----------------------------
525
+
526
+ The terms in this supplement govern your use of the NVIDIA
527
+ CUDA Toolkit SDK under the terms of your license agreement
528
+ (“Agreement”) as modified by this supplement. Capitalized
529
+ terms used but not defined below have the meaning assigned to
530
+ them in the Agreement.
531
+
532
+ This supplement is an exhibit to the Agreement and is
533
+ incorporated as an integral part of the Agreement. In the
534
+ event of conflict between the terms in this supplement and the
535
+ terms in the Agreement, the terms in this supplement govern.
536
+
537
+
538
+ 2.1. License Scope
539
+
540
+ The SDK is licensed for you to develop applications only for
541
+ use in systems with NVIDIA GPUs.
542
+
543
+
544
+ 2.2. Distribution
545
+
546
+ The portions of the SDK that are distributable under the
547
+ Agreement are listed in Attachment A.
548
+
549
+
550
+ 2.3. Operating Systems
551
+
552
+ Those portions of the SDK designed exclusively for use on the
553
+ Linux or FreeBSD operating systems, or other operating systems
554
+ derived from the source code to these operating systems, may
555
+ be copied and redistributed for use in accordance with this
556
+ Agreement, provided that the object code files are not
557
+ modified in any way (except for unzipping of compressed
558
+ files).
559
+
560
+
561
+ 2.4. Audio and Video Encoders and Decoders
562
+
563
+ You acknowledge and agree that it is your sole responsibility
564
+ to obtain any additional third-party licenses required to
565
+ make, have made, use, have used, sell, import, and offer for
566
+ sale your products or services that include or incorporate any
567
+ third-party software and content relating to audio and/or
568
+ video encoders and decoders from, including but not limited
569
+ to, Microsoft, Thomson, Fraunhofer IIS, Sisvel S.p.A.,
570
+ MPEG-LA, and Coding Technologies. NVIDIA does not grant to you
571
+ under this Agreement any necessary patent or other rights with
572
+ respect to any audio and/or video encoders and decoders.
573
+
574
+
575
+ 2.5. Licensing
576
+
577
+ If the distribution terms in this Agreement are not suitable
578
+ for your organization, or for any questions regarding this
579
+ Agreement, please contact NVIDIA at
580
+ nvidia-compute-license-questions@nvidia.com.
581
+
582
+
583
+ 2.6. Attachment A
584
+
585
+ The following portions of the SDK are distributable under the
586
+ Agreement:
587
+
588
+ Component
589
+
590
+ CUDA Runtime
591
+
592
+ Windows
593
+
594
+ cudart.dll, cudart_static.lib, cudadevrt.lib
595
+
596
+ Mac OSX
597
+
598
+ libcudart.dylib, libcudart_static.a, libcudadevrt.a
599
+
600
+ Linux
601
+
602
+ libcudart.so, libcudart_static.a, libcudadevrt.a
603
+
604
+ Android
605
+
606
+ libcudart.so, libcudart_static.a, libcudadevrt.a
607
+
608
+ Component
609
+
610
+ CUDA FFT Library
611
+
612
+ Windows
613
+
614
+ cufft.dll, cufftw.dll, cufft.lib, cufftw.lib
615
+
616
+ Mac OSX
617
+
618
+ libcufft.dylib, libcufft_static.a, libcufftw.dylib,
619
+ libcufftw_static.a
620
+
621
+ Linux
622
+
623
+ libcufft.so, libcufft_static.a, libcufftw.so,
624
+ libcufftw_static.a
625
+
626
+ Android
627
+
628
+ libcufft.so, libcufft_static.a, libcufftw.so,
629
+ libcufftw_static.a
630
+
631
+ Component
632
+
633
+ CUDA BLAS Library
634
+
635
+ Windows
636
+
637
+ cublas.dll, cublasLt.dll
638
+
639
+ Mac OSX
640
+
641
+ libcublas.dylib, libcublasLt.dylib, libcublas_static.a,
642
+ libcublasLt_static.a
643
+
644
+ Linux
645
+
646
+ libcublas.so, libcublasLt.so, libcublas_static.a,
647
+ libcublasLt_static.a
648
+
649
+ Android
650
+
651
+ libcublas.so, libcublasLt.so, libcublas_static.a,
652
+ libcublasLt_static.a
653
+
654
+ Component
655
+
656
+ NVIDIA "Drop-in" BLAS Library
657
+
658
+ Windows
659
+
660
+ nvblas.dll
661
+
662
+ Mac OSX
663
+
664
+ libnvblas.dylib
665
+
666
+ Linux
667
+
668
+ libnvblas.so
669
+
670
+ Component
671
+
672
+ CUDA Sparse Matrix Library
673
+
674
+ Windows
675
+
676
+ cusparse.dll, cusparse.lib
677
+
678
+ Mac OSX
679
+
680
+ libcusparse.dylib, libcusparse_static.a
681
+
682
+ Linux
683
+
684
+ libcusparse.so, libcusparse_static.a
685
+
686
+ Android
687
+
688
+ libcusparse.so, libcusparse_static.a
689
+
690
+ Component
691
+
692
+ CUDA Linear Solver Library
693
+
694
+ Windows
695
+
696
+ cusolver.dll, cusolver.lib
697
+
698
+ Mac OSX
699
+
700
+ libcusolver.dylib, libcusolver_static.a
701
+
702
+ Linux
703
+
704
+ libcusolver.so, libcusolver_static.a
705
+
706
+ Android
707
+
708
+ libcusolver.so, libcusolver_static.a
709
+
710
+ Component
711
+
712
+ CUDA Random Number Generation Library
713
+
714
+ Windows
715
+
716
+ curand.dll, curand.lib
717
+
718
+ Mac OSX
719
+
720
+ libcurand.dylib, libcurand_static.a
721
+
722
+ Linux
723
+
724
+ libcurand.so, libcurand_static.a
725
+
726
+ Android
727
+
728
+ libcurand.so, libcurand_static.a
729
+
730
+ Component
731
+
732
+ CUDA Accelerated Graph Library
733
+
734
+ Component
735
+
736
+ NVIDIA Performance Primitives Library
737
+
738
+ Windows
739
+
740
+ nppc.dll, nppc.lib, nppial.dll, nppial.lib, nppicc.dll,
741
+ nppicc.lib, nppicom.dll, nppicom.lib, nppidei.dll,
742
+ nppidei.lib, nppif.dll, nppif.lib, nppig.dll, nppig.lib,
743
+ nppim.dll, nppim.lib, nppist.dll, nppist.lib, nppisu.dll,
744
+ nppisu.lib, nppitc.dll, nppitc.lib, npps.dll, npps.lib
745
+
746
+ Mac OSX
747
+
748
+ libnppc.dylib, libnppc_static.a, libnppial.dylib,
749
+ libnppial_static.a, libnppicc.dylib, libnppicc_static.a,
750
+ libnppicom.dylib, libnppicom_static.a, libnppidei.dylib,
751
+ libnppidei_static.a, libnppif.dylib, libnppif_static.a,
752
+ libnppig.dylib, libnppig_static.a, libnppim.dylib,
753
+ libnppisu_static.a, libnppitc.dylib, libnppitc_static.a,
754
+ libnpps.dylib, libnpps_static.a
755
+
756
+ Linux
757
+
758
+ libnppc.so, libnppc_static.a, libnppial.so,
759
+ libnppial_static.a, libnppicc.so, libnppicc_static.a,
760
+ libnppicom.so, libnppicom_static.a, libnppidei.so,
761
+ libnppidei_static.a, libnppif.so, libnppif_static.a
762
+ libnppig.so, libnppig_static.a, libnppim.so,
763
+ libnppim_static.a, libnppist.so, libnppist_static.a,
764
+ libnppisu.so, libnppisu_static.a, libnppitc.so
765
+ libnppitc_static.a, libnpps.so, libnpps_static.a
766
+
767
+ Android
768
+
769
+ libnppc.so, libnppc_static.a, libnppial.so,
770
+ libnppial_static.a, libnppicc.so, libnppicc_static.a,
771
+ libnppicom.so, libnppicom_static.a, libnppidei.so,
772
+ libnppidei_static.a, libnppif.so, libnppif_static.a
773
+ libnppig.so, libnppig_static.a, libnppim.so,
774
+ libnppim_static.a, libnppist.so, libnppist_static.a,
775
+ libnppisu.so, libnppisu_static.a, libnppitc.so
776
+ libnppitc_static.a, libnpps.so, libnpps_static.a
777
+
778
+ Component
779
+
780
+ NVIDIA JPEG Library
781
+
782
+ Linux
783
+
784
+ libnvjpeg.so, libnvjpeg_static.a
785
+
786
+ Component
787
+
788
+ Internal common library required for statically linking to
789
+ cuBLAS, cuSPARSE, cuFFT, cuRAND, nvJPEG and NPP
790
+
791
+ Mac OSX
792
+
793
+ libculibos.a
794
+
795
+ Linux
796
+
797
+ libculibos.a
798
+
799
+ Component
800
+
801
+ NVIDIA Runtime Compilation Library and Header
802
+
803
+ All
804
+
805
+ nvrtc.h
806
+
807
+ Windows
808
+
809
+ nvrtc.dll, nvrtc-builtins.dll
810
+
811
+ Mac OSX
812
+
813
+ libnvrtc.dylib, libnvrtc-builtins.dylib
814
+
815
+ Linux
816
+
817
+ libnvrtc.so, libnvrtc-builtins.so
818
+
819
+ Component
820
+
821
+ NVIDIA Optimizing Compiler Library
822
+
823
+ Windows
824
+
825
+ nvvm.dll
826
+
827
+ Mac OSX
828
+
829
+ libnvvm.dylib
830
+
831
+ Linux
832
+
833
+ libnvvm.so
834
+
835
+ Component
836
+
837
+ NVIDIA Common Device Math Functions Library
838
+
839
+ Windows
840
+
841
+ libdevice.10.bc
842
+
843
+ Mac OSX
844
+
845
+ libdevice.10.bc
846
+
847
+ Linux
848
+
849
+ libdevice.10.bc
850
+
851
+ Component
852
+
853
+ CUDA Occupancy Calculation Header Library
854
+
855
+ All
856
+
857
+ cuda_occupancy.h
858
+
859
+ Component
860
+
861
+ CUDA Half Precision Headers
862
+
863
+ All
864
+
865
+ cuda_fp16.h, cuda_fp16.hpp
866
+
867
+ Component
868
+
869
+ CUDA Profiling Tools Interface (CUPTI) Library
870
+
871
+ Windows
872
+
873
+ cupti.dll
874
+
875
+ Mac OSX
876
+
877
+ libcupti.dylib
878
+
879
+ Linux
880
+
881
+ libcupti.so
882
+
883
+ Component
884
+
885
+ NVIDIA Tools Extension Library
886
+
887
+ Windows
888
+
889
+ nvToolsExt.dll, nvToolsExt.lib
890
+
891
+ Mac OSX
892
+
893
+ libnvToolsExt.dylib
894
+
895
+ Linux
896
+
897
+ libnvToolsExt.so
898
+
899
+ Component
900
+
901
+ NVIDIA CUDA Driver Libraries
902
+
903
+ Linux
904
+
905
+ libcuda.so, libnvidia-fatbinaryloader.so,
906
+ libnvidia-ptxjitcompiler.so
907
+
908
+ The NVIDIA CUDA Driver Libraries are only distributable in
909
+ applications that meet this criteria:
910
+
911
+ 1. The application was developed starting from a NVIDIA CUDA
912
+ container obtained from Docker Hub or the NVIDIA GPU
913
+ Cloud, and
914
+
915
+ 2. The resulting application is packaged as a Docker
916
+ container and distributed to users on Docker Hub or the
917
+ NVIDIA GPU Cloud only.
918
+
919
+
920
+ 2.7. Attachment B
921
+
922
+
923
+ Additional Licensing Obligations
924
+
925
+ The following third party components included in the SOFTWARE
926
+ are licensed to Licensee pursuant to the following terms and
927
+ conditions:
928
+
929
+ 1. Licensee's use of the GDB third party component is
930
+ subject to the terms and conditions of GNU GPL v3:
931
+
932
+ This product includes copyrighted third-party software licensed
933
+ under the terms of the GNU General Public License v3 ("GPL v3").
934
+ All third-party software packages are copyright by their respective
935
+ authors. GPL v3 terms and conditions are hereby incorporated into
936
+ the Agreement by this reference: http://www.gnu.org/licenses/gpl.txt
937
+
938
+ Consistent with these licensing requirements, the software
939
+ listed below is provided under the terms of the specified
940
+ open source software licenses. To obtain source code for
941
+ software provided under licenses that require
942
+ redistribution of source code, including the GNU General
943
+ Public License (GPL) and GNU Lesser General Public License
944
+ (LGPL), contact oss-requests@nvidia.com. This offer is
945
+ valid for a period of three (3) years from the date of the
946
+ distribution of this product by NVIDIA CORPORATION.
947
+
948
+ Component License
949
+ CUDA-GDB GPL v3
950
+
951
+ 2. Licensee represents and warrants that any and all third
952
+ party licensing and/or royalty payment obligations in
953
+ connection with Licensee's use of the H.264 video codecs
954
+ are solely the responsibility of Licensee.
955
+
956
+ 3. Licensee's use of the Thrust library is subject to the
957
+ terms and conditions of the Apache License Version 2.0.
958
+ All third-party software packages are copyright by their
959
+ respective authors. Apache License Version 2.0 terms and
960
+ conditions are hereby incorporated into the Agreement by
961
+ this reference.
962
+ http://www.apache.org/licenses/LICENSE-2.0.html
963
+
964
+ In addition, Licensee acknowledges the following notice:
965
+ Thrust includes source code from the Boost Iterator,
966
+ Tuple, System, and Random Number libraries.
967
+
968
+ Boost Software License - Version 1.0 - August 17th, 2003
969
+ . . . .
970
+
971
+ Permission is hereby granted, free of charge, to any person or
972
+ organization obtaining a copy of the software and accompanying
973
+ documentation covered by this license (the "Software") to use,
974
+ reproduce, display, distribute, execute, and transmit the Software,
975
+ and to prepare derivative works of the Software, and to permit
976
+ third-parties to whom the Software is furnished to do so, all
977
+ subject to the following:
978
+
979
+ The copyright notices in the Software and this entire statement,
980
+ including the above license grant, this restriction and the following
981
+ disclaimer, must be included in all copies of the Software, in whole
982
+ or in part, and all derivative works of the Software, unless such
983
+ copies or derivative works are solely in the form of machine-executable
984
+ object code generated by a source language processor.
985
+
986
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
987
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
988
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
989
+ NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
990
+ ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR
991
+ OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING
992
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
993
+ OTHER DEALINGS IN THE SOFTWARE.
994
+
995
+ 4. Licensee's use of the LLVM third party component is
996
+ subject to the following terms and conditions:
997
+
998
+ ======================================================
999
+ LLVM Release License
1000
+ ======================================================
1001
+ University of Illinois/NCSA
1002
+ Open Source License
1003
+
1004
+ Copyright (c) 2003-2010 University of Illinois at Urbana-Champaign.
1005
+ All rights reserved.
1006
+
1007
+ Developed by:
1008
+
1009
+ LLVM Team
1010
+
1011
+ University of Illinois at Urbana-Champaign
1012
+
1013
+ http://llvm.org
1014
+
1015
+ Permission is hereby granted, free of charge, to any person obtaining a copy
1016
+ of this software and associated documentation files (the "Software"), to
1017
+ deal with the Software without restriction, including without limitation the
1018
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
1019
+ sell copies of the Software, and to permit persons to whom the Software is
1020
+ furnished to do so, subject to the following conditions:
1021
+
1022
+ * Redistributions of source code must retain the above copyright notice,
1023
+ this list of conditions and the following disclaimers.
1024
+
1025
+ * Redistributions in binary form must reproduce the above copyright
1026
+ notice, this list of conditions and the following disclaimers in the
1027
+ documentation and/or other materials provided with the distribution.
1028
+
1029
+ * Neither the names of the LLVM Team, University of Illinois at Urbana-
1030
+ Champaign, nor the names of its contributors may be used to endorse or
1031
+ promote products derived from this Software without specific prior
1032
+ written permission.
1033
+
1034
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1035
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1036
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
1037
+ THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
1038
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
1039
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
1040
+ DEALINGS WITH THE SOFTWARE.
1041
+
1042
+ 5. Licensee's use (e.g. nvprof) of the PCRE third party
1043
+ component is subject to the following terms and
1044
+ conditions:
1045
+
1046
+ ------------
1047
+ PCRE LICENCE
1048
+ ------------
1049
+ PCRE is a library of functions to support regular expressions whose syntax
1050
+ and semantics are as close as possible to those of the Perl 5 language.
1051
+ Release 8 of PCRE is distributed under the terms of the "BSD" licence, as
1052
+ specified below. The documentation for PCRE, supplied in the "doc"
1053
+ directory, is distributed under the same terms as the software itself. The
1054
+ basic library functions are written in C and are freestanding. Also
1055
+ included in the distribution is a set of C++ wrapper functions, and a just-
1056
+ in-time compiler that can be used to optimize pattern matching. These are
1057
+ both optional features that can be omitted when the library is built.
1058
+
1059
+ THE BASIC LIBRARY FUNCTIONS
1060
+ ---------------------------
1061
+ Written by: Philip Hazel
1062
+ Email local part: ph10
1063
+ Email domain: cam.ac.uk
1064
+ University of Cambridge Computing Service,
1065
+ Cambridge, England.
1066
+ Copyright (c) 1997-2012 University of Cambridge
1067
+ All rights reserved.
1068
+
1069
+ PCRE JUST-IN-TIME COMPILATION SUPPORT
1070
+ -------------------------------------
1071
+ Written by: Zoltan Herczeg
1072
+ Email local part: hzmester
1073
+ Emain domain: freemail.hu
1074
+ Copyright(c) 2010-2012 Zoltan Herczeg
1075
+ All rights reserved.
1076
+
1077
+ STACK-LESS JUST-IN-TIME COMPILER
1078
+ --------------------------------
1079
+ Written by: Zoltan Herczeg
1080
+ Email local part: hzmester
1081
+ Emain domain: freemail.hu
1082
+ Copyright(c) 2009-2012 Zoltan Herczeg
1083
+ All rights reserved.
1084
+
1085
+ THE C++ WRAPPER FUNCTIONS
1086
+ -------------------------
1087
+ Contributed by: Google Inc.
1088
+ Copyright (c) 2007-2012, Google Inc.
1089
+ All rights reserved.
1090
+
1091
+ THE "BSD" LICENCE
1092
+ -----------------
1093
+ Redistribution and use in source and binary forms, with or without
1094
+ modification, are permitted provided that the following conditions are met:
1095
+
1096
+ * Redistributions of source code must retain the above copyright notice,
1097
+ this list of conditions and the following disclaimer.
1098
+
1099
+ * Redistributions in binary form must reproduce the above copyright
1100
+ notice, this list of conditions and the following disclaimer in the
1101
+ documentation and/or other materials provided with the distribution.
1102
+
1103
+ * Neither the name of the University of Cambridge nor the name of Google
1104
+ Inc. nor the names of their contributors may be used to endorse or
1105
+ promote products derived from this software without specific prior
1106
+ written permission.
1107
+
1108
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
1109
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1110
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1111
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
1112
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
1113
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
1114
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
1115
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
1116
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
1117
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
1118
+ POSSIBILITY OF SUCH DAMAGE.
1119
+
1120
+ 6. Some of the cuBLAS library routines were written by or
1121
+ derived from code written by Vasily Volkov and are subject
1122
+ to the Modified Berkeley Software Distribution License as
1123
+ follows:
1124
+
1125
+ Copyright (c) 2007-2009, Regents of the University of California
1126
+
1127
+ All rights reserved.
1128
+
1129
+ Redistribution and use in source and binary forms, with or without
1130
+ modification, are permitted provided that the following conditions are
1131
+ met:
1132
+ * Redistributions of source code must retain the above copyright
1133
+ notice, this list of conditions and the following disclaimer.
1134
+ * Redistributions in binary form must reproduce the above
1135
+ copyright notice, this list of conditions and the following
1136
+ disclaimer in the documentation and/or other materials provided
1137
+ with the distribution.
1138
+ * Neither the name of the University of California, Berkeley nor
1139
+ the names of its contributors may be used to endorse or promote
1140
+ products derived from this software without specific prior
1141
+ written permission.
1142
+
1143
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
1144
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1145
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1146
+ DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
1147
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
1148
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
1149
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1150
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
1151
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
1152
+ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
1153
+ POSSIBILITY OF SUCH DAMAGE.
1154
+
1155
+ 7. Some of the cuBLAS library routines were written by or
1156
+ derived from code written by Davide Barbieri and are
1157
+ subject to the Modified Berkeley Software Distribution
1158
+ License as follows:
1159
+
1160
+ Copyright (c) 2008-2009 Davide Barbieri @ University of Rome Tor Vergata.
1161
+
1162
+ All rights reserved.
1163
+
1164
+ Redistribution and use in source and binary forms, with or without
1165
+ modification, are permitted provided that the following conditions are
1166
+ met:
1167
+ * Redistributions of source code must retain the above copyright
1168
+ notice, this list of conditions and the following disclaimer.
1169
+ * Redistributions in binary form must reproduce the above
1170
+ copyright notice, this list of conditions and the following
1171
+ disclaimer in the documentation and/or other materials provided
1172
+ with the distribution.
1173
+ * The name of the author may not be used to endorse or promote
1174
+ products derived from this software without specific prior
1175
+ written permission.
1176
+
1177
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
1178
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1179
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1180
+ DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
1181
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
1182
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
1183
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1184
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
1185
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
1186
+ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
1187
+ POSSIBILITY OF SUCH DAMAGE.
1188
+
1189
+ 8. Some of the cuBLAS library routines were derived from
1190
+ code developed by the University of Tennessee and are
1191
+ subject to the Modified Berkeley Software Distribution
1192
+ License as follows:
1193
+
1194
+ Copyright (c) 2010 The University of Tennessee.
1195
+
1196
+ All rights reserved.
1197
+
1198
+ Redistribution and use in source and binary forms, with or without
1199
+ modification, are permitted provided that the following conditions are
1200
+ met:
1201
+ * Redistributions of source code must retain the above copyright
1202
+ notice, this list of conditions and the following disclaimer.
1203
+ * Redistributions in binary form must reproduce the above
1204
+ copyright notice, this list of conditions and the following
1205
+ disclaimer listed in this license in the documentation and/or
1206
+ other materials provided with the distribution.
1207
+ * Neither the name of the copyright holders nor the names of its
1208
+ contributors may be used to endorse or promote products derived
1209
+ from this software without specific prior written permission.
1210
+
1211
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1212
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1213
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1214
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1215
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1216
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
1217
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1218
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1219
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1220
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1221
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1222
+
1223
+ 9. Some of the cuBLAS library routines were written by or
1224
+ derived from code written by Jonathan Hogg and are subject
1225
+ to the Modified Berkeley Software Distribution License as
1226
+ follows:
1227
+
1228
+ Copyright (c) 2012, The Science and Technology Facilities Council (STFC).
1229
+
1230
+ All rights reserved.
1231
+
1232
+ Redistribution and use in source and binary forms, with or without
1233
+ modification, are permitted provided that the following conditions are
1234
+ met:
1235
+ * Redistributions of source code must retain the above copyright
1236
+ notice, this list of conditions and the following disclaimer.
1237
+ * Redistributions in binary form must reproduce the above
1238
+ copyright notice, this list of conditions and the following
1239
+ disclaimer in the documentation and/or other materials provided
1240
+ with the distribution.
1241
+ * Neither the name of the STFC nor the names of its contributors
1242
+ may be used to endorse or promote products derived from this
1243
+ software without specific prior written permission.
1244
+
1245
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1246
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1247
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1248
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE STFC BE
1249
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
1250
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
1251
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
1252
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
1253
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
1254
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
1255
+ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1256
+
1257
+ 10. Some of the cuBLAS library routines were written by or
1258
+ derived from code written by Ahmad M. Abdelfattah, David
1259
+ Keyes, and Hatem Ltaief, and are subject to the Apache
1260
+ License, Version 2.0, as follows:
1261
+
1262
+ -- (C) Copyright 2013 King Abdullah University of Science and Technology
1263
+ Authors:
1264
+ Ahmad Abdelfattah (ahmad.ahmad@kaust.edu.sa)
1265
+ David Keyes (david.keyes@kaust.edu.sa)
1266
+ Hatem Ltaief (hatem.ltaief@kaust.edu.sa)
1267
+
1268
+ Redistribution and use in source and binary forms, with or without
1269
+ modification, are permitted provided that the following conditions
1270
+ are met:
1271
+
1272
+ * Redistributions of source code must retain the above copyright
1273
+ notice, this list of conditions and the following disclaimer.
1274
+ * Redistributions in binary form must reproduce the above copyright
1275
+ notice, this list of conditions and the following disclaimer in the
1276
+ documentation and/or other materials provided with the distribution.
1277
+ * Neither the name of the King Abdullah University of Science and
1278
+ Technology nor the names of its contributors may be used to endorse
1279
+ or promote products derived from this software without specific prior
1280
+ written permission.
1281
+
1282
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1283
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1284
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1285
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1286
+ HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1287
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
1288
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1289
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1290
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1291
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1292
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
1293
+
1294
+ 11. Some of the cuSPARSE library routines were written by or
1295
+ derived from code written by Li-Wen Chang and are subject
1296
+ to the NCSA Open Source License as follows:
1297
+
1298
+ Copyright (c) 2012, University of Illinois.
1299
+
1300
+ All rights reserved.
1301
+
1302
+ Developed by: IMPACT Group, University of Illinois, http://impact.crhc.illinois.edu
1303
+
1304
+ Permission is hereby granted, free of charge, to any person obtaining
1305
+ a copy of this software and associated documentation files (the
1306
+ "Software"), to deal with the Software without restriction, including
1307
+ without limitation the rights to use, copy, modify, merge, publish,
1308
+ distribute, sublicense, and/or sell copies of the Software, and to
1309
+ permit persons to whom the Software is furnished to do so, subject to
1310
+ the following conditions:
1311
+ * Redistributions of source code must retain the above copyright
1312
+ notice, this list of conditions and the following disclaimer.
1313
+ * Redistributions in binary form must reproduce the above
1314
+ copyright notice, this list of conditions and the following
1315
+ disclaimers in the documentation and/or other materials provided
1316
+ with the distribution.
1317
+ * Neither the names of IMPACT Group, University of Illinois, nor
1318
+ the names of its contributors may be used to endorse or promote
1319
+ products derived from this Software without specific prior
1320
+ written permission.
1321
+
1322
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
1323
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
1324
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
1325
+ NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT
1326
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
1327
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
1328
+ IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
1329
+ SOFTWARE.
1330
+
1331
+ 12. Some of the cuRAND library routines were written by or
1332
+ derived from code written by Mutsuo Saito and Makoto
1333
+ Matsumoto and are subject to the following license:
1334
+
1335
+ Copyright (c) 2009, 2010 Mutsuo Saito, Makoto Matsumoto and Hiroshima
1336
+ University. All rights reserved.
1337
+
1338
+ Copyright (c) 2011 Mutsuo Saito, Makoto Matsumoto, Hiroshima
1339
+ University and University of Tokyo. All rights reserved.
1340
+
1341
+ Redistribution and use in source and binary forms, with or without
1342
+ modification, are permitted provided that the following conditions are
1343
+ met:
1344
+ * Redistributions of source code must retain the above copyright
1345
+ notice, this list of conditions and the following disclaimer.
1346
+ * Redistributions in binary form must reproduce the above
1347
+ copyright notice, this list of conditions and the following
1348
+ disclaimer in the documentation and/or other materials provided
1349
+ with the distribution.
1350
+ * Neither the name of the Hiroshima University nor the names of
1351
+ its contributors may be used to endorse or promote products
1352
+ derived from this software without specific prior written
1353
+ permission.
1354
+
1355
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1356
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1357
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1358
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1359
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1360
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
1361
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1362
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1363
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1364
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1365
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1366
+
1367
+ 13. Some of the cuRAND library routines were derived from
1368
+ code developed by D. E. Shaw Research and are subject to
1369
+ the following license:
1370
+
1371
+ Copyright 2010-2011, D. E. Shaw Research.
1372
+
1373
+ All rights reserved.
1374
+
1375
+ Redistribution and use in source and binary forms, with or without
1376
+ modification, are permitted provided that the following conditions are
1377
+ met:
1378
+ * Redistributions of source code must retain the above copyright
1379
+ notice, this list of conditions, and the following disclaimer.
1380
+ * Redistributions in binary form must reproduce the above
1381
+ copyright notice, this list of conditions, and the following
1382
+ disclaimer in the documentation and/or other materials provided
1383
+ with the distribution.
1384
+ * Neither the name of D. E. Shaw Research nor the names of its
1385
+ contributors may be used to endorse or promote products derived
1386
+ from this software without specific prior written permission.
1387
+
1388
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1389
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1390
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1391
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1392
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1393
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
1394
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1395
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1396
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1397
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1398
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1399
+
1400
+ 14. Some of the Math library routines were written by or
1401
+ derived from code developed by Norbert Juffa and are
1402
+ subject to the following license:
1403
+
1404
+ Copyright (c) 2015-2017, Norbert Juffa
1405
+ All rights reserved.
1406
+
1407
+ Redistribution and use in source and binary forms, with or without
1408
+ modification, are permitted provided that the following conditions
1409
+ are met:
1410
+
1411
+ 1. Redistributions of source code must retain the above copyright
1412
+ notice, this list of conditions and the following disclaimer.
1413
+
1414
+ 2. Redistributions in binary form must reproduce the above copyright
1415
+ notice, this list of conditions and the following disclaimer in the
1416
+ documentation and/or other materials provided with the distribution.
1417
+
1418
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1419
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1420
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1421
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1422
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1423
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
1424
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1425
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1426
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1427
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1428
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1429
+
1430
+ 15. Licensee's use of the lz4 third party component is
1431
+ subject to the following terms and conditions:
1432
+
1433
+ Copyright (C) 2011-2013, Yann Collet.
1434
+ BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
1435
+
1436
+ Redistribution and use in source and binary forms, with or without
1437
+ modification, are permitted provided that the following conditions are
1438
+ met:
1439
+
1440
+ * Redistributions of source code must retain the above copyright
1441
+ notice, this list of conditions and the following disclaimer.
1442
+ * Redistributions in binary form must reproduce the above
1443
+ copyright notice, this list of conditions and the following disclaimer
1444
+ in the documentation and/or other materials provided with the
1445
+ distribution.
1446
+
1447
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1448
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1449
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1450
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1451
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1452
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
1453
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1454
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1455
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1456
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1457
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1458
+
1459
+ 16. The NPP library uses code from the Boost Math Toolkit,
1460
+ and is subject to the following license:
1461
+
1462
+ Boost Software License - Version 1.0 - August 17th, 2003
1463
+ . . . .
1464
+
1465
+ Permission is hereby granted, free of charge, to any person or
1466
+ organization obtaining a copy of the software and accompanying
1467
+ documentation covered by this license (the "Software") to use,
1468
+ reproduce, display, distribute, execute, and transmit the Software,
1469
+ and to prepare derivative works of the Software, and to permit
1470
+ third-parties to whom the Software is furnished to do so, all
1471
+ subject to the following:
1472
+
1473
+ The copyright notices in the Software and this entire statement,
1474
+ including the above license grant, this restriction and the following
1475
+ disclaimer, must be included in all copies of the Software, in whole
1476
+ or in part, and all derivative works of the Software, unless such
1477
+ copies or derivative works are solely in the form of machine-executable
1478
+ object code generated by a source language processor.
1479
+
1480
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
1481
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
1482
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
1483
+ NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
1484
+ ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR
1485
+ OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING
1486
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
1487
+ OTHER DEALINGS IN THE SOFTWARE.
1488
+
1489
+ 17. Portions of the Nsight Eclipse Edition is subject to the
1490
+ following license:
1491
+
1492
+ The Eclipse Foundation makes available all content in this plug-in
1493
+ ("Content"). Unless otherwise indicated below, the Content is provided
1494
+ to you under the terms and conditions of the Eclipse Public License
1495
+ Version 1.0 ("EPL"). A copy of the EPL is available at http://
1496
+ www.eclipse.org/legal/epl-v10.html. For purposes of the EPL, "Program"
1497
+ will mean the Content.
1498
+
1499
+ If you did not receive this Content directly from the Eclipse
1500
+ Foundation, the Content is being redistributed by another party
1501
+ ("Redistributor") and different terms and conditions may apply to your
1502
+ use of any object code in the Content. Check the Redistributor's
1503
+ license that was provided with the Content. If no such license exists,
1504
+ contact the Redistributor. Unless otherwise indicated below, the terms
1505
+ and conditions of the EPL still apply to any source code in the
1506
+ Content and such source code may be obtained at http://www.eclipse.org.
1507
+
1508
+ 18. Some of the cuBLAS library routines uses code from
1509
+ OpenAI, which is subject to the following license:
1510
+
1511
+ License URL
1512
+ https://github.com/openai/openai-gemm/blob/master/LICENSE
1513
+
1514
+ License Text
1515
+ The MIT License
1516
+
1517
+ Copyright (c) 2016 OpenAI (http://openai.com), 2016 Google Inc.
1518
+
1519
+ Permission is hereby granted, free of charge, to any person obtaining a copy
1520
+ of this software and associated documentation files (the "Software"), to deal
1521
+ in the Software without restriction, including without limitation the rights
1522
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1523
+ copies of the Software, and to permit persons to whom the Software is
1524
+ furnished to do so, subject to the following conditions:
1525
+
1526
+ The above copyright notice and this permission notice shall be included in
1527
+ all copies or substantial portions of the Software.
1528
+
1529
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1530
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1531
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1532
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1533
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1534
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
1535
+ THE SOFTWARE.
1536
+
1537
+ 19. Licensee's use of the Visual Studio Setup Configuration
1538
+ Samples is subject to the following license:
1539
+
1540
+ The MIT License (MIT)
1541
+ Copyright (C) Microsoft Corporation. All rights reserved.
1542
+
1543
+ Permission is hereby granted, free of charge, to any person
1544
+ obtaining a copy of this software and associated documentation
1545
+ files (the "Software"), to deal in the Software without restriction,
1546
+ including without limitation the rights to use, copy, modify, merge,
1547
+ publish, distribute, sublicense, and/or sell copies of the Software,
1548
+ and to permit persons to whom the Software is furnished to do so,
1549
+ subject to the following conditions:
1550
+
1551
+ The above copyright notice and this permission notice shall be included
1552
+ in all copies or substantial portions of the Software.
1553
+
1554
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
1555
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1556
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1557
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1558
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1559
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1560
+
1561
+ 20. Licensee's use of linmath.h header for CPU functions for
1562
+ GL vector/matrix operations from lunarG is subject to the
1563
+ Apache License Version 2.0.
1564
+
1565
+ 21. The DX12-CUDA sample uses the d3dx12.h header, which is
1566
+ subject to the MIT license .
1567
+
1568
+ -----------------
tuning-competition-baseline/.venv/lib/python3.11/site-packages/nvidia_cufft_cu11-10.9.0.58.dist-info/METADATA ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Metadata-Version: 2.1
2
+ Name: nvidia-cufft-cu11
3
+ Version: 10.9.0.58
4
+ Summary: CUFFT native runtime libraries
5
+ Home-page: https://developer.nvidia.com/cuda-zone
6
+ Author: Nvidia CUDA Installer Team
7
+ Author-email: cuda_installer@nvidia.com
8
+ License: NVIDIA Proprietary Software
9
+ Keywords: cuda,nvidia,runtime,machine learning,deep learning
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Education
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: License :: Other/Proprietary License
15
+ Classifier: Natural Language :: English
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.5
18
+ Classifier: Programming Language :: Python :: 3.6
19
+ Classifier: Programming Language :: Python :: 3.7
20
+ Classifier: Programming Language :: Python :: 3.8
21
+ Classifier: Programming Language :: Python :: 3.9
22
+ Classifier: Programming Language :: Python :: 3.10
23
+ Classifier: Programming Language :: Python :: 3.11
24
+ Classifier: Programming Language :: Python :: 3 :: Only
25
+ Classifier: Topic :: Scientific/Engineering
26
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
27
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
28
+ Classifier: Topic :: Software Development
29
+ Classifier: Topic :: Software Development :: Libraries
30
+ Classifier: Operating System :: Microsoft :: Windows
31
+ Classifier: Operating System :: POSIX :: Linux
32
+ Requires-Python: >=3
33
+ License-File: License.txt
34
+
35
+ CUFFT native runtime libraries
tuning-competition-baseline/.venv/lib/python3.11/site-packages/nvidia_cufft_cu11-10.9.0.58.dist-info/WHEEL ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ Wheel-Version: 1.0
2
+ Generator: bdist_wheel (0.37.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-manylinux1_x86_64
5
+
tuning-competition-baseline/.venv/lib/python3.11/site-packages/nvidia_cufft_cu11-10.9.0.58.dist-info/top_level.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ nvidia
tuning-competition-baseline/.venv/lib/python3.11/site-packages/nvidia_cusolver_cu11-11.4.1.48.dist-info/METADATA ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Metadata-Version: 2.1
2
+ Name: nvidia-cusolver-cu11
3
+ Version: 11.4.1.48
4
+ Summary: CUDA solver native runtime libraries
5
+ Home-page: https://developer.nvidia.com/cuda-zone
6
+ Author: Nvidia CUDA Installer Team
7
+ Author-email: cuda_installer@nvidia.com
8
+ License: NVIDIA Proprietary Software
9
+ Keywords: cuda,nvidia,runtime,machine learning,deep learning
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Education
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: License :: Other/Proprietary License
15
+ Classifier: Natural Language :: English
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.5
18
+ Classifier: Programming Language :: Python :: 3.6
19
+ Classifier: Programming Language :: Python :: 3.7
20
+ Classifier: Programming Language :: Python :: 3.8
21
+ Classifier: Programming Language :: Python :: 3.9
22
+ Classifier: Programming Language :: Python :: 3.10
23
+ Classifier: Programming Language :: Python :: 3.11
24
+ Classifier: Programming Language :: Python :: 3 :: Only
25
+ Classifier: Topic :: Scientific/Engineering
26
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
27
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
28
+ Classifier: Topic :: Software Development
29
+ Classifier: Topic :: Software Development :: Libraries
30
+ Classifier: Operating System :: Microsoft :: Windows
31
+ Classifier: Operating System :: POSIX :: Linux
32
+ Requires-Python: >=3
33
+ License-File: License.txt
34
+ Requires-Dist: nvidia-cublas-cu11
35
+
36
+ CUDA solver native runtime libraries
tuning-competition-baseline/.venv/lib/python3.11/site-packages/nvidia_cusolver_cu11-11.4.1.48.dist-info/top_level.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ nvidia
tuning-competition-baseline/.venv/lib/python3.11/site-packages/pip/_internal/commands/__init__.py ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Package containing all pip commands
3
+ """
4
+
5
+ import importlib
6
+ from collections import namedtuple
7
+ from typing import Any, Dict, Optional
8
+
9
+ from pip._internal.cli.base_command import Command
10
+
11
+ CommandInfo = namedtuple("CommandInfo", "module_path, class_name, summary")
12
+
13
+ # This dictionary does a bunch of heavy lifting for help output:
14
+ # - Enables avoiding additional (costly) imports for presenting `--help`.
15
+ # - The ordering matters for help display.
16
+ #
17
+ # Even though the module path starts with the same "pip._internal.commands"
18
+ # prefix, the full path makes testing easier (specifically when modifying
19
+ # `commands_dict` in test setup / teardown).
20
+ commands_dict: Dict[str, CommandInfo] = {
21
+ "install": CommandInfo(
22
+ "pip._internal.commands.install",
23
+ "InstallCommand",
24
+ "Install packages.",
25
+ ),
26
+ "download": CommandInfo(
27
+ "pip._internal.commands.download",
28
+ "DownloadCommand",
29
+ "Download packages.",
30
+ ),
31
+ "uninstall": CommandInfo(
32
+ "pip._internal.commands.uninstall",
33
+ "UninstallCommand",
34
+ "Uninstall packages.",
35
+ ),
36
+ "freeze": CommandInfo(
37
+ "pip._internal.commands.freeze",
38
+ "FreezeCommand",
39
+ "Output installed packages in requirements format.",
40
+ ),
41
+ "inspect": CommandInfo(
42
+ "pip._internal.commands.inspect",
43
+ "InspectCommand",
44
+ "Inspect the python environment.",
45
+ ),
46
+ "list": CommandInfo(
47
+ "pip._internal.commands.list",
48
+ "ListCommand",
49
+ "List installed packages.",
50
+ ),
51
+ "show": CommandInfo(
52
+ "pip._internal.commands.show",
53
+ "ShowCommand",
54
+ "Show information about installed packages.",
55
+ ),
56
+ "check": CommandInfo(
57
+ "pip._internal.commands.check",
58
+ "CheckCommand",
59
+ "Verify installed packages have compatible dependencies.",
60
+ ),
61
+ "config": CommandInfo(
62
+ "pip._internal.commands.configuration",
63
+ "ConfigurationCommand",
64
+ "Manage local and global configuration.",
65
+ ),
66
+ "search": CommandInfo(
67
+ "pip._internal.commands.search",
68
+ "SearchCommand",
69
+ "Search PyPI for packages.",
70
+ ),
71
+ "cache": CommandInfo(
72
+ "pip._internal.commands.cache",
73
+ "CacheCommand",
74
+ "Inspect and manage pip's wheel cache.",
75
+ ),
76
+ "index": CommandInfo(
77
+ "pip._internal.commands.index",
78
+ "IndexCommand",
79
+ "Inspect information available from package indexes.",
80
+ ),
81
+ "wheel": CommandInfo(
82
+ "pip._internal.commands.wheel",
83
+ "WheelCommand",
84
+ "Build wheels from your requirements.",
85
+ ),
86
+ "hash": CommandInfo(
87
+ "pip._internal.commands.hash",
88
+ "HashCommand",
89
+ "Compute hashes of package archives.",
90
+ ),
91
+ "completion": CommandInfo(
92
+ "pip._internal.commands.completion",
93
+ "CompletionCommand",
94
+ "A helper command used for command completion.",
95
+ ),
96
+ "debug": CommandInfo(
97
+ "pip._internal.commands.debug",
98
+ "DebugCommand",
99
+ "Show information useful for debugging.",
100
+ ),
101
+ "help": CommandInfo(
102
+ "pip._internal.commands.help",
103
+ "HelpCommand",
104
+ "Show help for commands.",
105
+ ),
106
+ }
107
+
108
+
109
+ def create_command(name: str, **kwargs: Any) -> Command:
110
+ """
111
+ Create an instance of the Command class with the given name.
112
+ """
113
+ module_path, class_name, summary = commands_dict[name]
114
+ module = importlib.import_module(module_path)
115
+ command_class = getattr(module, class_name)
116
+ command = command_class(name=name, summary=summary, **kwargs)
117
+
118
+ return command
119
+
120
+
121
+ def get_similar_commands(name: str) -> Optional[str]:
122
+ """Command name auto-correct."""
123
+ from difflib import get_close_matches
124
+
125
+ name = name.lower()
126
+
127
+ close_commands = get_close_matches(name, commands_dict.keys())
128
+
129
+ if close_commands:
130
+ return close_commands[0]
131
+ else:
132
+ return None
tuning-competition-baseline/.venv/lib/python3.11/site-packages/pip/_internal/commands/__pycache__/configuration.cpython-311.pyc ADDED
Binary file (14.9 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/pip/_internal/commands/__pycache__/hash.cpython-311.pyc ADDED
Binary file (3.38 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/pip/_internal/commands/__pycache__/help.cpython-311.pyc ADDED
Binary file (1.99 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/pip/_internal/commands/__pycache__/inspect.cpython-311.pyc ADDED
Binary file (4.47 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/pip/_internal/commands/__pycache__/install.cpython-311.pyc ADDED
Binary file (31.4 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/pip/_internal/commands/__pycache__/uninstall.cpython-311.pyc ADDED
Binary file (5.18 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/pip/_internal/commands/check.py ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ from optparse import Values
3
+ from typing import List
4
+
5
+ from pip._internal.cli.base_command import Command
6
+ from pip._internal.cli.status_codes import ERROR, SUCCESS
7
+ from pip._internal.metadata import get_default_environment
8
+ from pip._internal.operations.check import (
9
+ check_package_set,
10
+ check_unsupported,
11
+ create_package_set_from_installed,
12
+ )
13
+ from pip._internal.utils.compatibility_tags import get_supported
14
+ from pip._internal.utils.misc import write_output
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class CheckCommand(Command):
20
+ """Verify installed packages have compatible dependencies."""
21
+
22
+ ignore_require_venv = True
23
+ usage = """
24
+ %prog [options]"""
25
+
26
+ def run(self, options: Values, args: List[str]) -> int:
27
+ package_set, parsing_probs = create_package_set_from_installed()
28
+ missing, conflicting = check_package_set(package_set)
29
+ unsupported = list(
30
+ check_unsupported(
31
+ get_default_environment().iter_installed_distributions(),
32
+ get_supported(),
33
+ )
34
+ )
35
+
36
+ for project_name in missing:
37
+ version = package_set[project_name].version
38
+ for dependency in missing[project_name]:
39
+ write_output(
40
+ "%s %s requires %s, which is not installed.",
41
+ project_name,
42
+ version,
43
+ dependency[0],
44
+ )
45
+
46
+ for project_name in conflicting:
47
+ version = package_set[project_name].version
48
+ for dep_name, dep_version, req in conflicting[project_name]:
49
+ write_output(
50
+ "%s %s has requirement %s, but you have %s %s.",
51
+ project_name,
52
+ version,
53
+ req,
54
+ dep_name,
55
+ dep_version,
56
+ )
57
+ for package in unsupported:
58
+ write_output(
59
+ "%s %s is not supported on this platform",
60
+ package.raw_name,
61
+ package.version,
62
+ )
63
+ if missing or conflicting or parsing_probs or unsupported:
64
+ return ERROR
65
+ else:
66
+ write_output("No broken requirements found.")
67
+ return SUCCESS
tuning-competition-baseline/.venv/lib/python3.11/site-packages/pip/_internal/commands/completion.py ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ import textwrap
3
+ from optparse import Values
4
+ from typing import List
5
+
6
+ from pip._internal.cli.base_command import Command
7
+ from pip._internal.cli.status_codes import SUCCESS
8
+ from pip._internal.utils.misc import get_prog
9
+
10
+ BASE_COMPLETION = """
11
+ # pip {shell} completion start{script}# pip {shell} completion end
12
+ """
13
+
14
+ COMPLETION_SCRIPTS = {
15
+ "bash": """
16
+ _pip_completion()
17
+ {{
18
+ COMPREPLY=( $( COMP_WORDS="${{COMP_WORDS[*]}}" \\
19
+ COMP_CWORD=$COMP_CWORD \\
20
+ PIP_AUTO_COMPLETE=1 $1 2>/dev/null ) )
21
+ }}
22
+ complete -o default -F _pip_completion {prog}
23
+ """,
24
+ "zsh": """
25
+ #compdef -P pip[0-9.]#
26
+ __pip() {{
27
+ compadd $( COMP_WORDS="$words[*]" \\
28
+ COMP_CWORD=$((CURRENT-1)) \\
29
+ PIP_AUTO_COMPLETE=1 $words[1] 2>/dev/null )
30
+ }}
31
+ if [[ $zsh_eval_context[-1] == loadautofunc ]]; then
32
+ # autoload from fpath, call function directly
33
+ __pip "$@"
34
+ else
35
+ # eval/source/. command, register function for later
36
+ compdef __pip -P 'pip[0-9.]#'
37
+ fi
38
+ """,
39
+ "fish": """
40
+ function __fish_complete_pip
41
+ set -lx COMP_WORDS (commandline -o) ""
42
+ set -lx COMP_CWORD ( \\
43
+ math (contains -i -- (commandline -t) $COMP_WORDS)-1 \\
44
+ )
45
+ set -lx PIP_AUTO_COMPLETE 1
46
+ string split \\ -- (eval $COMP_WORDS[1])
47
+ end
48
+ complete -fa "(__fish_complete_pip)" -c {prog}
49
+ """,
50
+ "powershell": """
51
+ if ((Test-Path Function:\\TabExpansion) -and -not `
52
+ (Test-Path Function:\\_pip_completeBackup)) {{
53
+ Rename-Item Function:\\TabExpansion _pip_completeBackup
54
+ }}
55
+ function TabExpansion($line, $lastWord) {{
56
+ $lastBlock = [regex]::Split($line, '[|;]')[-1].TrimStart()
57
+ if ($lastBlock.StartsWith("{prog} ")) {{
58
+ $Env:COMP_WORDS=$lastBlock
59
+ $Env:COMP_CWORD=$lastBlock.Split().Length - 1
60
+ $Env:PIP_AUTO_COMPLETE=1
61
+ (& {prog}).Split()
62
+ Remove-Item Env:COMP_WORDS
63
+ Remove-Item Env:COMP_CWORD
64
+ Remove-Item Env:PIP_AUTO_COMPLETE
65
+ }}
66
+ elseif (Test-Path Function:\\_pip_completeBackup) {{
67
+ # Fall back on existing tab expansion
68
+ _pip_completeBackup $line $lastWord
69
+ }}
70
+ }}
71
+ """,
72
+ }
73
+
74
+
75
+ class CompletionCommand(Command):
76
+ """A helper command to be used for command completion."""
77
+
78
+ ignore_require_venv = True
79
+
80
+ def add_options(self) -> None:
81
+ self.cmd_opts.add_option(
82
+ "--bash",
83
+ "-b",
84
+ action="store_const",
85
+ const="bash",
86
+ dest="shell",
87
+ help="Emit completion code for bash",
88
+ )
89
+ self.cmd_opts.add_option(
90
+ "--zsh",
91
+ "-z",
92
+ action="store_const",
93
+ const="zsh",
94
+ dest="shell",
95
+ help="Emit completion code for zsh",
96
+ )
97
+ self.cmd_opts.add_option(
98
+ "--fish",
99
+ "-f",
100
+ action="store_const",
101
+ const="fish",
102
+ dest="shell",
103
+ help="Emit completion code for fish",
104
+ )
105
+ self.cmd_opts.add_option(
106
+ "--powershell",
107
+ "-p",
108
+ action="store_const",
109
+ const="powershell",
110
+ dest="shell",
111
+ help="Emit completion code for powershell",
112
+ )
113
+
114
+ self.parser.insert_option_group(0, self.cmd_opts)
115
+
116
+ def run(self, options: Values, args: List[str]) -> int:
117
+ """Prints the completion code of the given shell"""
118
+ shells = COMPLETION_SCRIPTS.keys()
119
+ shell_options = ["--" + shell for shell in sorted(shells)]
120
+ if options.shell in shells:
121
+ script = textwrap.dedent(
122
+ COMPLETION_SCRIPTS.get(options.shell, "").format(prog=get_prog())
123
+ )
124
+ print(BASE_COMPLETION.format(script=script, shell=options.shell))
125
+ return SUCCESS
126
+ else:
127
+ sys.stderr.write(
128
+ "ERROR: You must pass {}\n".format(" or ".join(shell_options))
129
+ )
130
+ return SUCCESS
tuning-competition-baseline/.venv/lib/python3.11/site-packages/pip/_internal/commands/inspect.py ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ from optparse import Values
3
+ from typing import Any, Dict, List
4
+
5
+ from pip._vendor.packaging.markers import default_environment
6
+ from pip._vendor.rich import print_json
7
+
8
+ from pip import __version__
9
+ from pip._internal.cli import cmdoptions
10
+ from pip._internal.cli.base_command import Command
11
+ from pip._internal.cli.status_codes import SUCCESS
12
+ from pip._internal.metadata import BaseDistribution, get_environment
13
+ from pip._internal.utils.compat import stdlib_pkgs
14
+ from pip._internal.utils.urls import path_to_url
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class InspectCommand(Command):
20
+ """
21
+ Inspect the content of a Python environment and produce a report in JSON format.
22
+ """
23
+
24
+ ignore_require_venv = True
25
+ usage = """
26
+ %prog [options]"""
27
+
28
+ def add_options(self) -> None:
29
+ self.cmd_opts.add_option(
30
+ "--local",
31
+ action="store_true",
32
+ default=False,
33
+ help=(
34
+ "If in a virtualenv that has global access, do not list "
35
+ "globally-installed packages."
36
+ ),
37
+ )
38
+ self.cmd_opts.add_option(
39
+ "--user",
40
+ dest="user",
41
+ action="store_true",
42
+ default=False,
43
+ help="Only output packages installed in user-site.",
44
+ )
45
+ self.cmd_opts.add_option(cmdoptions.list_path())
46
+ self.parser.insert_option_group(0, self.cmd_opts)
47
+
48
+ def run(self, options: Values, args: List[str]) -> int:
49
+ cmdoptions.check_list_path_option(options)
50
+ dists = get_environment(options.path).iter_installed_distributions(
51
+ local_only=options.local,
52
+ user_only=options.user,
53
+ skip=set(stdlib_pkgs),
54
+ )
55
+ output = {
56
+ "version": "1",
57
+ "pip_version": __version__,
58
+ "installed": [self._dist_to_dict(dist) for dist in dists],
59
+ "environment": default_environment(),
60
+ # TODO tags? scheme?
61
+ }
62
+ print_json(data=output)
63
+ return SUCCESS
64
+
65
+ def _dist_to_dict(self, dist: BaseDistribution) -> Dict[str, Any]:
66
+ res: Dict[str, Any] = {
67
+ "metadata": dist.metadata_dict,
68
+ "metadata_location": dist.info_location,
69
+ }
70
+ # direct_url. Note that we don't have download_info (as in the installation
71
+ # report) since it is not recorded in installed metadata.
72
+ direct_url = dist.direct_url
73
+ if direct_url is not None:
74
+ res["direct_url"] = direct_url.to_dict()
75
+ else:
76
+ # Emulate direct_url for legacy editable installs.
77
+ editable_project_location = dist.editable_project_location
78
+ if editable_project_location is not None:
79
+ res["direct_url"] = {
80
+ "url": path_to_url(editable_project_location),
81
+ "dir_info": {
82
+ "editable": True,
83
+ },
84
+ }
85
+ # installer
86
+ installer = dist.installer
87
+ if dist.installer:
88
+ res["installer"] = installer
89
+ # requested
90
+ if dist.installed_with_dist_info:
91
+ res["requested"] = dist.requested
92
+ return res