litdata 0.2.60__tar.gz → 0.2.64__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. {litdata-0.2.60/src/litdata.egg-info → litdata-0.2.64}/PKG-INFO +3 -3
  2. {litdata-0.2.60 → litdata-0.2.64}/setup.py +5 -2
  3. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/__about__.py +1 -1
  4. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/cli/commands.py +1 -1
  5. litdata-0.2.64/src/litdata/cli/handler/__init__.py +21 -0
  6. litdata-0.2.64/src/litdata/cli/handler/cache.py +32 -0
  7. litdata-0.2.64/src/litdata/cli/handler/optimize.py +19 -0
  8. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/constants.py +1 -0
  9. litdata-0.2.64/src/litdata/imports.py +363 -0
  10. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/processing/data_processor.py +1 -1
  11. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/processing/utilities.py +8 -4
  12. litdata-0.2.64/src/litdata/requirements.py +192 -0
  13. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/streaming/client.py +90 -49
  14. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/streaming/config.py +5 -0
  15. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/streaming/dataloader.py +45 -0
  16. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/streaming/downloader.py +16 -9
  17. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/streaming/fs_provider.py +9 -7
  18. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/streaming/item_loader.py +24 -8
  19. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/streaming/reader.py +95 -45
  20. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/streaming/resolver.py +13 -1
  21. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/streaming/serializers.py +32 -0
  22. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/streaming/writer.py +5 -1
  23. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/utilities/parquet.py +11 -3
  24. {litdata-0.2.60 → litdata-0.2.64/src/litdata.egg-info}/PKG-INFO +3 -3
  25. {litdata-0.2.60 → litdata-0.2.64}/src/litdata.egg-info/SOURCES.txt +4 -0
  26. {litdata-0.2.60 → litdata-0.2.64}/src/litdata.egg-info/requires.txt +2 -2
  27. litdata-0.2.60/src/litdata/imports.py +0 -121
  28. {litdata-0.2.60 → litdata-0.2.64}/CONTRIBUTING.md +0 -0
  29. {litdata-0.2.60 → litdata-0.2.64}/LICENSE +0 -0
  30. {litdata-0.2.60 → litdata-0.2.64}/MANIFEST.in +0 -0
  31. {litdata-0.2.60 → litdata-0.2.64}/README.md +0 -0
  32. {litdata-0.2.60 → litdata-0.2.64}/requirements.txt +0 -0
  33. {litdata-0.2.60 → litdata-0.2.64}/setup.cfg +0 -0
  34. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/__init__.py +0 -0
  35. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/__main__.py +0 -0
  36. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/cli/__init__.py +0 -0
  37. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/cli/parser.py +0 -0
  38. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/debugger.py +0 -0
  39. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/helpers.py +0 -0
  40. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/processing/__init__.py +0 -0
  41. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/processing/functions.py +0 -0
  42. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/processing/readers.py +0 -0
  43. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/raw/__init__.py +0 -0
  44. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/raw/dataset.py +0 -0
  45. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/raw/indexer.py +0 -0
  46. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/raw/types.py +0 -0
  47. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/streaming/__init__.py +0 -0
  48. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/streaming/cache.py +0 -0
  49. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/streaming/combined.py +0 -0
  50. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/streaming/compression.py +0 -0
  51. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/streaming/dataset.py +0 -0
  52. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/streaming/parallel.py +0 -0
  53. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/streaming/sampler.py +0 -0
  54. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/streaming/shuffle.py +0 -0
  55. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/utilities/__init__.py +0 -0
  56. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/utilities/_pytree.py +0 -0
  57. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/utilities/base.py +0 -0
  58. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/utilities/breakpoint.py +0 -0
  59. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/utilities/broadcast.py +0 -0
  60. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/utilities/dataset_utilities.py +0 -0
  61. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/utilities/encryption.py +0 -0
  62. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/utilities/env.py +0 -0
  63. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/utilities/format.py +0 -0
  64. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/utilities/hf_dataset.py +0 -0
  65. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/utilities/packing.py +0 -0
  66. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/utilities/shuffle.py +0 -0
  67. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/utilities/subsample.py +0 -0
  68. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/utilities/torch_utils.py +0 -0
  69. {litdata-0.2.60 → litdata-0.2.64}/src/litdata/utilities/train_test_split.py +0 -0
  70. {litdata-0.2.60 → litdata-0.2.64}/src/litdata.egg-info/dependency_links.txt +0 -0
  71. {litdata-0.2.60 → litdata-0.2.64}/src/litdata.egg-info/entry_points.txt +0 -0
  72. {litdata-0.2.60 → litdata-0.2.64}/src/litdata.egg-info/not-zip-safe +0 -0
  73. {litdata-0.2.60 → litdata-0.2.64}/src/litdata.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: litdata
3
- Version: 0.2.60
3
+ Version: 0.2.64
4
4
  Summary: The Deep Learning framework to train, deploy, and ship AI products Lightning fast.
5
5
  Home-page: https://github.com/Lightning-AI/litdata
6
6
  Download-URL: https://github.com/Lightning-AI/litdata
@@ -39,10 +39,10 @@ Requires-Dist: obstore
39
39
  Provides-Extra: extras
40
40
  Requires-Dist: fsspec; extra == "extras"
41
41
  Requires-Dist: google-cloud-storage; extra == "extras"
42
- Requires-Dist: lightning-sdk==2025.12.17; extra == "extras"
42
+ Requires-Dist: lightning-sdk==2026.2.6; extra == "extras"
43
43
  Requires-Dist: pillow; extra == "extras"
44
44
  Requires-Dist: polars; extra == "extras"
45
- Requires-Dist: pyarrow; extra == "extras"
45
+ Requires-Dist: pyarrow<25.0.0; extra == "extras"
46
46
  Requires-Dist: tqdm; extra == "extras"
47
47
  Requires-Dist: viztracer; extra == "extras"
48
48
  Dynamic: author
@@ -4,7 +4,6 @@ import os
4
4
  from importlib.util import module_from_spec, spec_from_file_location
5
5
  from pathlib import Path
6
6
 
7
- from pkg_resources import parse_requirements
8
7
  from setuptools import find_packages, setup
9
8
 
10
9
  _PATH_ROOT = os.path.dirname(__file__)
@@ -19,8 +18,12 @@ def _load_py_module(fname, pkg="litdata"):
19
18
  return py
20
19
 
21
20
 
21
+ about = _load_py_module("__about__.py")
22
+ requirements_module = _load_py_module("requirements.py")
23
+
24
+
22
25
  def _load_requirements(path_dir: str = _PATH_ROOT, file_name: str = "requirements.txt") -> list:
23
- reqs = parse_requirements(open(os.path.join(path_dir, file_name)).readlines())
26
+ reqs = requirements_module._parse_requirements(open(os.path.join(path_dir, file_name)).readlines())
24
27
  return list(map(str, reqs))
25
28
 
26
29
 
@@ -14,7 +14,7 @@
14
14
 
15
15
  import time
16
16
 
17
- __version__ = "0.2.60"
17
+ __version__ = "0.2.64"
18
18
  __author__ = "Lightning AI et al."
19
19
  __author_email__ = "pytorch@lightning.ai"
20
20
  __license__ = "Apache-2.0"
@@ -13,9 +13,9 @@
13
13
 
14
14
  from argparse import _SubParsersAction
15
15
 
16
+ from litdata.cli import LitFormatter
16
17
  from litdata.cli.handler.cache import clear_cache, show_cache_path
17
18
  from litdata.cli.handler.optimize import optimize_dataset
18
- from litdata.cli.parser import LitFormatter
19
19
 
20
20
 
21
21
  def register_cache_subcommand(subparser: _SubParsersAction) -> None:
@@ -0,0 +1,21 @@
1
+ # Copyright The Lightning AI team.
2
+ # Licensed under the Apache License, Version 2.0 (the "License");
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an "AS IS" BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+
14
+ from litdata.cli.handler.cache import clear_cache, show_cache_path
15
+ from litdata.cli.handler.optimize import optimize_dataset
16
+
17
+ __all__ = [
18
+ "clear_cache",
19
+ "show_cache_path",
20
+ "optimize_dataset",
21
+ ]
@@ -0,0 +1,32 @@
1
+ # Copyright The Lightning AI team.
2
+ # Licensed under the Apache License, Version 2.0 (the "License");
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an "AS IS" BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+
14
+ import shutil
15
+ from argparse import Namespace
16
+
17
+ from litdata.utilities.dataset_utilities import get_default_cache_dir
18
+
19
+
20
+ def clear_cache(args: Namespace) -> None:
21
+ """Clear default cache used for StreamingDataset and other utilities."""
22
+ streaming_default_cache_dir = get_default_cache_dir()
23
+
24
+ shutil.rmtree(streaming_default_cache_dir, ignore_errors=True)
25
+
26
+ print(f"Cache directory '{streaming_default_cache_dir}' cleared.")
27
+
28
+
29
+ def show_cache_path(args: Namespace) -> None:
30
+ """Show the path to the cache directory."""
31
+ streaming_default_cache_dir = get_default_cache_dir()
32
+ print(f"Default cache directory: {streaming_default_cache_dir}")
@@ -0,0 +1,19 @@
1
+ # Copyright The Lightning AI team.
2
+ # Licensed under the Apache License, Version 2.0 (the "License");
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an "AS IS" BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+
14
+ from argparse import Namespace
15
+
16
+
17
+ def optimize_dataset(args: Namespace) -> None:
18
+ """Handle the optimize command."""
19
+ print(f"Optimizing dataset at {args.dataset}...")
@@ -49,6 +49,7 @@ _PYARROW_AVAILABLE = RequirementCache("pyarrow")
49
49
  _POLARS_AVAILABLE = RequirementCache("polars>1.0.0")
50
50
  _PIL_AVAILABLE = RequirementCache("PIL")
51
51
  _TORCH_VISION_AVAILABLE = RequirementCache("torchvision")
52
+ _TORCH_VISION_LESS_THAN_0_26 = RequirementCache("torchvision<0.26.0")
52
53
  _AV_AVAILABLE = RequirementCache("av")
53
54
  _OBSTORE_AVAILABLE = RequirementCache("obstore")
54
55
 
@@ -0,0 +1,363 @@
1
+ # Copyright The Lightning AI team.
2
+ # Licensed under the Apache License, Version 2.0 (the "License");
3
+ # http://www.apache.org/licenses/LICENSE-2.0
4
+
5
+ import functools
6
+ import importlib
7
+ import os
8
+ import warnings
9
+ from collections.abc import Callable
10
+ from functools import lru_cache
11
+ from importlib.metadata import PackageNotFoundError, distribution
12
+ from importlib.metadata import version as _version
13
+ from importlib.util import find_spec
14
+ from types import ModuleType
15
+ from typing import Any, TypeVar
16
+
17
+ from packaging.requirements import Requirement
18
+ from packaging.version import InvalidVersion, Version
19
+ from typing_extensions import ParamSpec
20
+
21
+ T = TypeVar("T")
22
+ P = ParamSpec("P")
23
+
24
+ try:
25
+ from importlib import metadata
26
+ except ImportError:
27
+ # Python < 3.8
28
+ import importlib_metadata as metadata # type: ignore
29
+
30
+
31
+ @lru_cache
32
+ def package_available(package_name: str) -> bool:
33
+ """Check if a package is available in your environment.
34
+
35
+ >>> package_available('os')
36
+ True
37
+ >>> package_available('bla')
38
+ False
39
+
40
+ """
41
+ try:
42
+ return find_spec(package_name) is not None
43
+ except ModuleNotFoundError:
44
+ return False
45
+
46
+
47
+ @lru_cache
48
+ def module_available(module_path: str) -> bool:
49
+ """Check if a module path is available in your environment.
50
+
51
+ >>> module_available('os')
52
+ True
53
+ >>> module_available('os.bla')
54
+ False
55
+ >>> module_available('bla.bla')
56
+ False
57
+
58
+ """
59
+ module_names = module_path.split(".")
60
+ if not package_available(module_names[0]):
61
+ return False
62
+ try:
63
+ importlib.import_module(module_path)
64
+ except ImportError:
65
+ return False
66
+ return True
67
+
68
+
69
+ def compare_version(package: str, op: Callable, version: str, use_base_version: bool = False) -> bool:
70
+ """Compare package version with some requirements.
71
+
72
+ >>> compare_version("torch", operator.ge, "0.1")
73
+ True
74
+ >>> compare_version("does_not_exist", operator.ge, "0.0")
75
+ False
76
+
77
+ """
78
+ try:
79
+ pkg = importlib.import_module(package)
80
+ except (ImportError, RuntimeError):
81
+ return False
82
+ try:
83
+ # Use importlib.metadata to infer version
84
+ pkg_version = Version(pkg.__version__) if hasattr(pkg, "__version__") else Version(_version(package))
85
+ except (TypeError, PackageNotFoundError):
86
+ # this is mocked by Sphinx, so it should return True to generate all summaries
87
+ return True
88
+ if use_base_version:
89
+ pkg_version = Version(pkg_version.base_version)
90
+ return op(pkg_version, Version(version))
91
+
92
+
93
+ class RequirementCache:
94
+ """Boolean-like class to check for requirement and module availability.
95
+
96
+ Args:
97
+ requirement: The requirement to check, version specifiers are allowed.
98
+ module: The optional module to try to import if the requirement check fails.
99
+
100
+ >>> RequirementCache("torch>=0.1")
101
+ Requirement 'torch>=0.1' met
102
+ >>> bool(RequirementCache("torch>=0.1"))
103
+ True
104
+ >>> bool(RequirementCache("torch>100.0"))
105
+ False
106
+ >>> RequirementCache("torch")
107
+ Requirement 'torch' met
108
+ >>> bool(RequirementCache("torch"))
109
+ True
110
+ >>> bool(RequirementCache("unknown_package"))
111
+ False
112
+ >>> bool(RequirementCache(module="torch.utils"))
113
+ True
114
+ >>> bool(RequirementCache(module="unknown_package"))
115
+ False
116
+ >>> bool(RequirementCache(module="unknown.module.path"))
117
+ False
118
+
119
+ """
120
+
121
+ def __init__(self, requirement: str | None = None, module: str | None = None) -> None:
122
+ if not (requirement or module):
123
+ raise ValueError("At least one arguments need to be set.")
124
+ self.requirement = requirement
125
+ self.module = module
126
+
127
+ def _check_requirement(self) -> None:
128
+ if not self.requirement:
129
+ raise ValueError("Requirement name is required.")
130
+ try:
131
+ req = Requirement(self.requirement)
132
+ pkg_version = Version(_version(req.name))
133
+ self.available = req.specifier.contains(pkg_version, prereleases=True) and (
134
+ not req.extras or self._check_extras_available(req)
135
+ )
136
+ except (PackageNotFoundError, InvalidVersion) as ex:
137
+ self.available = False
138
+ self.message = f"{ex.__class__.__name__}: {ex}. HINT: Try running `pip install -U {self.requirement!r}`"
139
+
140
+ if self.available:
141
+ self.message = f"Requirement {self.requirement!r} met"
142
+ else:
143
+ req_include_version = any(c in self.requirement for c in "=<>")
144
+ if not req_include_version or self.module is not None:
145
+ module = self.requirement if self.module is None else self.module
146
+ # Sometimes `importlib.metadata.version` fails but the module is importable
147
+ self.available = module_available(module)
148
+ if self.available:
149
+ self.message = f"Module {module!r} available"
150
+ self.message = (
151
+ f"Requirement {self.requirement!r} not met. HINT: Try running `pip install -U {self.requirement!r}`"
152
+ )
153
+
154
+ def _check_module(self) -> None:
155
+ if not self.module:
156
+ raise ValueError("Module name is required.")
157
+ self.available = module_available(self.module)
158
+ if self.available:
159
+ self.message = f"Module {self.module!r} available"
160
+ else:
161
+ self.message = f"Module not found: {self.module!r}. HINT: Try running `pip install -U {self.module}`"
162
+
163
+ def _check_available(self) -> None:
164
+ if hasattr(self, "available"):
165
+ return
166
+ if self.requirement:
167
+ self._check_requirement()
168
+ if getattr(self, "available", True) and self.module:
169
+ self._check_module()
170
+
171
+ def _check_extras_available(self, requirement: Requirement) -> bool:
172
+ if not requirement.extras:
173
+ return True
174
+
175
+ extra_requirements = self._get_extra_requirements(requirement)
176
+
177
+ if not extra_requirements:
178
+ # The specified extra is not found in the package metadata
179
+ return False
180
+
181
+ # Verify each extra requirement is installed
182
+ for extra_req in extra_requirements:
183
+ try:
184
+ extra_dist = distribution(extra_req.name)
185
+ extra_installed_version = Version(extra_dist.version)
186
+ if extra_req.specifier and not extra_req.specifier.contains(extra_installed_version, prereleases=True):
187
+ return False
188
+ except importlib.metadata.PackageNotFoundError:
189
+ return False
190
+
191
+ return True
192
+
193
+ def _get_extra_requirements(self, requirement: Requirement) -> list[Requirement]:
194
+ dist = distribution(requirement.name)
195
+ # Get the required dependencies for the specified extras
196
+ extra_requirements = dist.metadata.get_all("Requires-Dist") or []
197
+ return [Requirement(r) for r in extra_requirements if any(extra in r for extra in requirement.extras)]
198
+
199
+ def __bool__(self) -> bool:
200
+ """Format as bool."""
201
+ self._check_available()
202
+ return self.available
203
+
204
+ def __str__(self) -> str:
205
+ """Format as string."""
206
+ self._check_available()
207
+ return self.message
208
+
209
+ def __repr__(self) -> str:
210
+ """Format as string."""
211
+ return self.__str__()
212
+
213
+
214
+ class ModuleAvailableCache(RequirementCache):
215
+ """Boolean-like class for check of module availability.
216
+
217
+ >>> ModuleAvailableCache("torch")
218
+ Module 'torch' available
219
+ >>> bool(ModuleAvailableCache("torch.utils"))
220
+ True
221
+ >>> bool(ModuleAvailableCache("unknown_package"))
222
+ False
223
+ >>> bool(ModuleAvailableCache("unknown.module.path"))
224
+ False
225
+
226
+ """
227
+
228
+ def __init__(self, module: str) -> None:
229
+ warnings.warn(
230
+ "`ModuleAvailableCache` is a special case of `RequirementCache`."
231
+ " Please use `RequirementCache(module=...)` instead.",
232
+ DeprecationWarning,
233
+ stacklevel=4,
234
+ )
235
+ super().__init__(module=module)
236
+
237
+
238
+ def get_dependency_min_version_spec(package_name: str, dependency_name: str) -> str:
239
+ """Return the minimum version specifier of a dependency of a package.
240
+
241
+ >>> get_dependency_min_version_spec("pytorch-lightning==1.8.0", "jsonargparse")
242
+ '>=4.12.0'
243
+
244
+ """
245
+ dependencies = metadata.requires(package_name) or []
246
+ for dep in dependencies:
247
+ dependency = Requirement(dep)
248
+ if dependency.name == dependency_name:
249
+ spec = [str(s) for s in dependency.specifier if str(s)[0] == ">"]
250
+ return spec[0] if spec else ""
251
+ raise ValueError(
252
+ "This is an internal error. Please file a GitHub issue with the error message. Dependency "
253
+ f"{dependency_name!r} not found in package {package_name!r}."
254
+ )
255
+
256
+
257
+ class LazyModule(ModuleType):
258
+ """Proxy module that lazily imports the underlying module the first time it is actually used.
259
+
260
+ Args:
261
+ module_name: the fully-qualified module name to import
262
+ callback: a callback function to call before importing the module
263
+
264
+ """
265
+
266
+ def __init__(self, module_name: str, callback: Callable | None = None) -> None:
267
+ super().__init__(module_name)
268
+ self._module: Any = None
269
+ self._callback = callback
270
+
271
+ def __getattr__(self, item: str) -> Any:
272
+ """Lazily import the underlying module and delegate attribute access to it."""
273
+ if self._module is None:
274
+ self._import_module()
275
+
276
+ return getattr(self._module, item)
277
+
278
+ def __dir__(self) -> list[str]:
279
+ """Lazily import the underlying module and return its attributes for introspection (dir())."""
280
+ if self._module is None:
281
+ self._import_module()
282
+
283
+ return dir(self._module)
284
+
285
+ def _import_module(self) -> None:
286
+ # Execute callback, if any
287
+ if self._callback is not None:
288
+ self._callback()
289
+
290
+ # Actually import the module
291
+ self._module = importlib.import_module(self.__name__)
292
+
293
+ # Update this object's dict so that attribute references are efficient
294
+ # (__getattr__ is only called on lookups that fail)
295
+ self.__dict__.update(self._module.__dict__)
296
+
297
+
298
+ def lazy_import(module_name: str, callback: Callable | None = None) -> LazyModule:
299
+ """Return a proxy module object that will lazily import the given module the first time it is used.
300
+
301
+ Example usage:
302
+
303
+ # Lazy version of `import tensorflow as tf`
304
+ tf = lazy_import("tensorflow")
305
+ # Other commands
306
+ # Now the module is loaded
307
+ tf.__version__
308
+
309
+ Args:
310
+ module_name: the fully-qualified module name to import
311
+ callback: a callback function to call before importing the module
312
+
313
+ Returns:
314
+ a proxy module object that will be lazily imported when first used
315
+
316
+ """
317
+ return LazyModule(module_name, callback=callback)
318
+
319
+
320
+ def requires(*module_path_version: str, raise_exception: bool = True) -> Callable[[Callable[P, T]], Callable[P, T]]:
321
+ """Decorator to check optional dependencies at call time with a clear error/warning message.
322
+
323
+ Args:
324
+ module_path_version: Python module paths (e.g., ``"torch.cuda"``) and/or pip-style requirements
325
+ (e.g., ``"torch>=2.0.0"``) to verify.
326
+ raise_exception: If ``True``, raise ``ModuleNotFoundError`` when requirements are not satisfied;
327
+ otherwise emit a warning and proceed to call the function.
328
+
329
+ Example:
330
+ >>> @requires("libpath", raise_exception=bool(int(os.getenv("LIGHTING_TESTING", "0"))))
331
+ ... def my_cwd():
332
+ ... from pathlib import Path
333
+ ... return Path(__file__).parent
334
+
335
+ >>> class MyRndPower:
336
+ ... @requires("math", "random")
337
+ ... def __init__(self):
338
+ ... from math import pow
339
+ ... from random import randint
340
+ ... self._rnd = pow(randint(1, 9), 2)
341
+
342
+ """
343
+
344
+ def decorator(func: Callable[P, T]) -> Callable[P, T]:
345
+ reqs = [
346
+ ModuleAvailableCache(mod_ver) if "." in mod_ver else RequirementCache(mod_ver)
347
+ for mod_ver in module_path_version
348
+ ]
349
+ available = all(map(bool, reqs))
350
+
351
+ @functools.wraps(func)
352
+ def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
353
+ if not available:
354
+ missing = os.linesep.join([repr(r) for r in reqs if not bool(r)])
355
+ msg = f"Required dependencies not available: \n{missing}"
356
+ if raise_exception:
357
+ raise ModuleNotFoundError(msg)
358
+ warnings.warn(msg, stacklevel=2)
359
+ return func(*args, **kwargs)
360
+
361
+ return wrapper
362
+
363
+ return decorator
@@ -160,7 +160,7 @@ def _download_data_target(
160
160
  if input_dir.path:
161
161
  local_path = path.replace(input_dir.path, cache_dir)
162
162
 
163
- if input_dir.url and input_dir.path:
163
+ if input_dir.url and input_dir.path and not os.path.isfile(path):
164
164
  path = path.replace(input_dir.path, input_dir.url)
165
165
 
166
166
  obj = parse.urlparse(path)
@@ -68,6 +68,10 @@ def _create_dataset(
68
68
  client = LightningClient(retry=False)
69
69
 
70
70
  try:
71
+ # Some strings represent protobuf strings, some protouf uint64s
72
+ # The uint64s need a default of None so they're not added to the
73
+ # request body, which avoids a 400 response due to an invalid request.
74
+ # The protobuf string types can default to "" just fine.
71
75
  client.dataset_service_create_dataset(
72
76
  body=DatasetServiceCreateDatasetBody(
73
77
  cloud_space_id=(studio_id if lightning_app_id is None else None) or "",
@@ -77,15 +81,15 @@ def _create_dataset(
77
81
  input_dir=input_dir or "",
78
82
  lightning_app_id=lightning_app_id or "",
79
83
  name=name or "",
80
- size=size or "",
81
- num_bytes=num_bytes or "",
84
+ size=size,
85
+ num_bytes=num_bytes,
82
86
  data_format=(str(data_format) if data_format else data_format) or "",
83
87
  compression=compression or "",
84
- num_chunks=num_chunks or "",
88
+ num_chunks=num_chunks,
85
89
  num_bytes_per_chunk=num_bytes_per_chunk or [],
86
90
  storage_dir=storage_dir,
87
91
  type=dataset_type,
88
- version=version or "",
92
+ version=version,
89
93
  ),
90
94
  project_id=project_id,
91
95
  )