datasets 5.1.0__tar.gz → 5.2.0__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 (211) hide show
  1. {datasets-5.1.0/src/datasets.egg-info → datasets-5.2.0}/PKG-INFO +2 -2
  2. {datasets-5.1.0 → datasets-5.2.0}/setup.py +2 -2
  3. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/__init__.py +1 -1
  4. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/arrow_dataset.py +36 -10
  5. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/builder.py +11 -3
  6. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/combine.py +2 -0
  7. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/features/audio.py +3 -0
  8. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/features/features.py +17 -3
  9. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/features/image.py +3 -0
  10. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/features/mesh.py +7 -4
  11. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/features/nifti.py +3 -0
  12. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/features/video.py +3 -0
  13. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/io/csv.py +3 -0
  14. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/io/json.py +3 -0
  15. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/iterable_dataset.py +126 -42
  16. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/folder_based_builder/folder_based_builder.py +1 -0
  17. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/genbank/genbank.py +20 -7
  18. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/json/json.py +27 -1
  19. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/meshfolder/meshfolder.py +2 -0
  20. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/niftifolder/niftifolder.py +1 -0
  21. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/pandas/pandas.py +8 -6
  22. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/webdataset/webdataset.py +4 -0
  23. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/table.py +5 -1
  24. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/utils/file_utils.py +21 -1
  25. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/utils/json.py +32 -6
  26. {datasets-5.1.0 → datasets-5.2.0/src/datasets.egg-info}/PKG-INFO +2 -2
  27. {datasets-5.1.0 → datasets-5.2.0}/src/datasets.egg-info/requires.txt +1 -1
  28. {datasets-5.1.0 → datasets-5.2.0}/tests/test_arrow_dataset.py +127 -2
  29. {datasets-5.1.0 → datasets-5.2.0}/tests/test_distributed.py +29 -1
  30. {datasets-5.1.0 → datasets-5.2.0}/tests/test_file_utils.py +40 -0
  31. {datasets-5.1.0 → datasets-5.2.0}/tests/test_iterable_dataset.py +130 -50
  32. {datasets-5.1.0 → datasets-5.2.0}/tests/test_load.py +37 -1
  33. {datasets-5.1.0 → datasets-5.2.0}/tests/test_search.py +18 -1
  34. {datasets-5.1.0 → datasets-5.2.0}/tests/test_table.py +10 -0
  35. {datasets-5.1.0 → datasets-5.2.0}/AUTHORS +0 -0
  36. {datasets-5.1.0 → datasets-5.2.0}/LICENSE +0 -0
  37. {datasets-5.1.0 → datasets-5.2.0}/README.md +0 -0
  38. {datasets-5.1.0 → datasets-5.2.0}/pyproject.toml +0 -0
  39. {datasets-5.1.0 → datasets-5.2.0}/setup.cfg +0 -0
  40. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/arrow_reader.py +0 -0
  41. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/arrow_writer.py +0 -0
  42. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/commands/__init__.py +0 -0
  43. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/commands/datasets_cli.py +0 -0
  44. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/commands/delete_from_hub.py +0 -0
  45. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/commands/env.py +0 -0
  46. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/commands/test.py +0 -0
  47. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/config.py +0 -0
  48. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/data_files.py +0 -0
  49. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/dataset_dict.py +0 -0
  50. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/distributed.py +0 -0
  51. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/download/__init__.py +0 -0
  52. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/download/download_config.py +0 -0
  53. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/download/download_manager.py +0 -0
  54. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/download/streaming_download_manager.py +0 -0
  55. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/exceptions.py +0 -0
  56. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/features/__init__.py +0 -0
  57. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/features/_torchcodec.py +0 -0
  58. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/features/bio_sequence.py +0 -0
  59. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/features/bio_structure.py +0 -0
  60. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/features/pdf.py +0 -0
  61. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/features/translation.py +0 -0
  62. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/filesystems/__init__.py +0 -0
  63. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/filesystems/compression.py +0 -0
  64. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/fingerprint.py +0 -0
  65. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/formatting/__init__.py +0 -0
  66. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/formatting/formatting.py +0 -0
  67. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/formatting/jax_formatter.py +0 -0
  68. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/formatting/np_formatter.py +0 -0
  69. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/formatting/polars_formatter.py +0 -0
  70. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/formatting/tf_formatter.py +0 -0
  71. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/formatting/torch_formatter.py +0 -0
  72. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/hub.py +0 -0
  73. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/info.py +0 -0
  74. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/inspect.py +0 -0
  75. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/io/__init__.py +0 -0
  76. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/io/abc.py +0 -0
  77. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/io/generator.py +0 -0
  78. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/io/parquet.py +0 -0
  79. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/io/spark.py +0 -0
  80. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/io/sql.py +0 -0
  81. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/io/text.py +0 -0
  82. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/load.py +0 -0
  83. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/naming.py +0 -0
  84. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/__init__.py +0 -0
  85. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/arrow/__init__.py +0 -0
  86. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/arrow/arrow.py +0 -0
  87. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/audiofolder/__init__.py +0 -0
  88. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/audiofolder/audiofolder.py +0 -0
  89. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/cache/__init__.py +0 -0
  90. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/cache/cache.py +0 -0
  91. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/conll/__init__.py +0 -0
  92. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/conll/conll.py +0 -0
  93. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/csv/__init__.py +0 -0
  94. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/csv/csv.py +0 -0
  95. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/eval/__init__.py +0 -0
  96. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/eval/eval.py +0 -0
  97. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/fasta/__init__.py +0 -0
  98. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/fasta/fasta.py +0 -0
  99. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/fastq/__init__.py +0 -0
  100. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/fastq/fastq.py +0 -0
  101. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/folder_based_builder/__init__.py +0 -0
  102. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/genbank/__init__.py +0 -0
  103. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/generator/__init__.py +0 -0
  104. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/generator/generator.py +0 -0
  105. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/harbor/__init__.py +0 -0
  106. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/harbor/harbor.py +0 -0
  107. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/hdf5/__init__.py +0 -0
  108. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/hdf5/hdf5.py +0 -0
  109. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/iceberg/__init__.py +0 -0
  110. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/iceberg/iceberg.py +0 -0
  111. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/imagefolder/__init__.py +0 -0
  112. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/imagefolder/imagefolder.py +0 -0
  113. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/json/__init__.py +0 -0
  114. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/lance/__init__.py +0 -0
  115. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/lance/lance.py +0 -0
  116. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/meshfolder/__init__.py +0 -0
  117. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/mmcif/__init__.py +0 -0
  118. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/mmcif/mmcif.py +0 -0
  119. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/niftifolder/__init__.py +0 -0
  120. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/pandas/__init__.py +0 -0
  121. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/parquet/__init__.py +0 -0
  122. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/parquet/parquet.py +0 -0
  123. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/pdb/__init__.py +0 -0
  124. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/pdb/pdb.py +0 -0
  125. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/pdffolder/__init__.py +0 -0
  126. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/pdffolder/pdffolder.py +0 -0
  127. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/spark/__init__.py +0 -0
  128. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/spark/spark.py +0 -0
  129. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/sql/__init__.py +0 -0
  130. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/sql/sql.py +0 -0
  131. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/text/__init__.py +0 -0
  132. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/text/text.py +0 -0
  133. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/tsfile/__init__.py +0 -0
  134. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/tsfile/tsfile.py +0 -0
  135. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/videofolder/__init__.py +0 -0
  136. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/videofolder/videofolder.py +0 -0
  137. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/vortex/__init__.py +0 -0
  138. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/vortex/vortex.py +0 -0
  139. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/webdataset/__init__.py +0 -0
  140. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/webdataset/_tenbin.py +0 -0
  141. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/xml/__init__.py +0 -0
  142. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/packaged_modules/xml/xml.py +0 -0
  143. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/parallel/__init__.py +0 -0
  144. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/parallel/parallel.py +0 -0
  145. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/search.py +0 -0
  146. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/splits.py +0 -0
  147. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/streaming.py +0 -0
  148. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/utils/__init__.py +0 -0
  149. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/utils/_dataset_viewer.py +0 -0
  150. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/utils/_dill.py +0 -0
  151. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/utils/_filelock.py +0 -0
  152. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/utils/deprecation_utils.py +0 -0
  153. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/utils/doc_utils.py +0 -0
  154. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/utils/experimental.py +0 -0
  155. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/utils/extract.py +0 -0
  156. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/utils/filelock.py +0 -0
  157. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/utils/hub.py +0 -0
  158. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/utils/info_utils.py +0 -0
  159. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/utils/logging.py +0 -0
  160. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/utils/metadata.py +0 -0
  161. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/utils/patching.py +0 -0
  162. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/utils/py_utils.py +0 -0
  163. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/utils/resources/__init__.py +0 -0
  164. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/utils/resources/creators.json +0 -0
  165. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/utils/resources/languages.json +0 -0
  166. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/utils/resources/multilingualities.json +0 -0
  167. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/utils/resources/readme_structure.yaml +0 -0
  168. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/utils/resources/size_categories.json +0 -0
  169. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/utils/sharding.py +0 -0
  170. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/utils/stratify.py +0 -0
  171. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/utils/tf_utils.py +0 -0
  172. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/utils/tqdm.py +0 -0
  173. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/utils/track.py +0 -0
  174. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/utils/typing.py +0 -0
  175. {datasets-5.1.0 → datasets-5.2.0}/src/datasets/utils/version.py +0 -0
  176. {datasets-5.1.0 → datasets-5.2.0}/src/datasets.egg-info/SOURCES.txt +0 -0
  177. {datasets-5.1.0 → datasets-5.2.0}/src/datasets.egg-info/dependency_links.txt +0 -0
  178. {datasets-5.1.0 → datasets-5.2.0}/src/datasets.egg-info/entry_points.txt +0 -0
  179. {datasets-5.1.0 → datasets-5.2.0}/src/datasets.egg-info/not-zip-safe +0 -0
  180. {datasets-5.1.0 → datasets-5.2.0}/src/datasets.egg-info/top_level.txt +0 -0
  181. {datasets-5.1.0 → datasets-5.2.0}/tests/test_arrow_reader.py +0 -0
  182. {datasets-5.1.0 → datasets-5.2.0}/tests/test_arrow_writer.py +0 -0
  183. {datasets-5.1.0 → datasets-5.2.0}/tests/test_buckets.py +0 -0
  184. {datasets-5.1.0 → datasets-5.2.0}/tests/test_builder.py +0 -0
  185. {datasets-5.1.0 → datasets-5.2.0}/tests/test_data_files.py +0 -0
  186. {datasets-5.1.0 → datasets-5.2.0}/tests/test_dataset_dict.py +0 -0
  187. {datasets-5.1.0 → datasets-5.2.0}/tests/test_dataset_list.py +0 -0
  188. {datasets-5.1.0 → datasets-5.2.0}/tests/test_download_manager.py +0 -0
  189. {datasets-5.1.0 → datasets-5.2.0}/tests/test_exceptions.py +0 -0
  190. {datasets-5.1.0 → datasets-5.2.0}/tests/test_experimental.py +0 -0
  191. {datasets-5.1.0 → datasets-5.2.0}/tests/test_extract.py +0 -0
  192. {datasets-5.1.0 → datasets-5.2.0}/tests/test_filelock.py +0 -0
  193. {datasets-5.1.0 → datasets-5.2.0}/tests/test_filesystem.py +0 -0
  194. {datasets-5.1.0 → datasets-5.2.0}/tests/test_fingerprint.py +0 -0
  195. {datasets-5.1.0 → datasets-5.2.0}/tests/test_fingerprint_tokenizer_stability.py +0 -0
  196. {datasets-5.1.0 → datasets-5.2.0}/tests/test_formatting.py +0 -0
  197. {datasets-5.1.0 → datasets-5.2.0}/tests/test_hub.py +0 -0
  198. {datasets-5.1.0 → datasets-5.2.0}/tests/test_info.py +0 -0
  199. {datasets-5.1.0 → datasets-5.2.0}/tests/test_info_utils.py +0 -0
  200. {datasets-5.1.0 → datasets-5.2.0}/tests/test_inspect.py +0 -0
  201. {datasets-5.1.0 → datasets-5.2.0}/tests/test_metadata_util.py +0 -0
  202. {datasets-5.1.0 → datasets-5.2.0}/tests/test_offline_util.py +0 -0
  203. {datasets-5.1.0 → datasets-5.2.0}/tests/test_parallel.py +0 -0
  204. {datasets-5.1.0 → datasets-5.2.0}/tests/test_patching.py +0 -0
  205. {datasets-5.1.0 → datasets-5.2.0}/tests/test_py_utils.py +0 -0
  206. {datasets-5.1.0 → datasets-5.2.0}/tests/test_sharding_utils.py +0 -0
  207. {datasets-5.1.0 → datasets-5.2.0}/tests/test_splits.py +0 -0
  208. {datasets-5.1.0 → datasets-5.2.0}/tests/test_streaming_download_manager.py +0 -0
  209. {datasets-5.1.0 → datasets-5.2.0}/tests/test_tqdm.py +0 -0
  210. {datasets-5.1.0 → datasets-5.2.0}/tests/test_upstream_hub.py +0 -0
  211. {datasets-5.1.0 → datasets-5.2.0}/tests/test_version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datasets
3
- Version: 5.1.0
3
+ Version: 5.2.0
4
4
  Summary: HuggingFace community-driven open-source library of datasets
5
5
  Home-page: https://github.com/huggingface/datasets
6
6
  Download-URL: https://github.com/huggingface/datasets/tags
@@ -33,7 +33,7 @@ Requires-Dist: pandas
33
33
  Requires-Dist: tqdm>=4.66.3
34
34
  Requires-Dist: xxhash
35
35
  Requires-Dist: multiprocess<0.70.20
36
- Requires-Dist: fsspec[http]<=2026.7.0,>=2023.1.0
36
+ Requires-Dist: fsspec<=2026.9.0,>=2023.1.0
37
37
  Requires-Dist: huggingface-hub<3.0,>=1.31.0
38
38
  Requires-Dist: packaging
39
39
  Requires-Dist: pyyaml>=5.1
@@ -124,7 +124,7 @@ REQUIRED_PKGS = [
124
124
  "multiprocess<0.70.20", # to align with dill<0.3.9 (see above)
125
125
  # to save datasets locally or on any filesystem
126
126
  # minimum 2023.1.0 to support protocol=kwargs in fsspec's `open`, `get_fs_token_paths`, etc.: see https://github.com/fsspec/filesystem_spec/pull/1143
127
- "fsspec[http]>=2023.1.0,<=2026.7.0",
127
+ "fsspec>=2023.1.0,<=2026.9.0",
128
128
  # To get datasets from the Datasets Hub on huggingface.co
129
129
  # (also provides the HTTP client: `from huggingface_hub.utils import httpx`)
130
130
  # minimum 1.31.0 for the `huggingface_hub.utils.httpx` re-export
@@ -252,7 +252,7 @@ EXTRAS_REQUIRE = {
252
252
 
253
253
  setup(
254
254
  name="datasets",
255
- version="5.1.0", # expected format is one of x.y.z.dev0, or x.y.z.rc1 or x.y.z (no to dashes, yes to dots)
255
+ version="5.2.0", # expected format is one of x.y.z.dev0, or x.y.z.rc1 or x.y.z (no to dashes, yes to dots)
256
256
  description="HuggingFace community-driven open-source library of datasets",
257
257
  long_description=open("README.md", encoding="utf-8").read(),
258
258
  long_description_content_type="text/markdown",
@@ -12,7 +12,7 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- __version__ = "5.1.0"
15
+ __version__ = "5.2.0"
16
16
 
17
17
  from .arrow_dataset import Column, Dataset
18
18
  from .arrow_reader import ReadInstruction
@@ -855,7 +855,7 @@ class Dataset(DatasetInfoMixin, IndexableMixin, TensorflowDatasetMixin):
855
855
  table = InMemoryTable.from_buffer(buffer)
856
856
 
857
857
  if indices_buffer is not None:
858
- indices_table = InMemoryTable.from_buffer(buffer)
858
+ indices_table = InMemoryTable.from_buffer(indices_buffer)
859
859
  else:
860
860
  indices_table = None
861
861
 
@@ -1830,14 +1830,21 @@ class Dataset(DatasetInfoMixin, IndexableMixin, TensorflowDatasetMixin):
1830
1830
  if self.list_indexes():
1831
1831
  raise ValueError("please remove all the indexes using `dataset.drop_index` before saving a dataset")
1832
1832
 
1833
+ from .arrow_writer import get_writer_batch_size_from_data_size
1834
+
1835
+ dataset_nbytes = self._estimate_nbytes()
1833
1836
  if num_shards is None:
1834
- dataset_nbytes = self._estimate_nbytes()
1835
1837
  max_shard_size = convert_file_size_to_int(max_shard_size or config.MAX_SHARD_SIZE)
1836
1838
  num_shards = int(dataset_nbytes / max_shard_size) + 1
1837
1839
  num_shards = max(num_shards, num_proc or 1)
1838
1840
  # if we have only a few large samples, we should only create as many shards as samples
1839
1841
  num_shards = min(len(self.data), num_shards)
1840
1842
 
1843
+ # use smaller batches for big samples (e.g. images) to stay below the 2GB limit of arrow binary arrays
1844
+ writer_batch_size = config.DEFAULT_MAX_BATCH_SIZE
1845
+ if dataset_nbytes > 0:
1846
+ writer_batch_size = min(writer_batch_size, get_writer_batch_size_from_data_size(len(self), dataset_nbytes))
1847
+
1841
1848
  fs: fsspec.AbstractFileSystem
1842
1849
  fs, _ = url_to_fs(dataset_path, **(storage_options or {}))
1843
1850
 
@@ -1890,6 +1897,7 @@ class Dataset(DatasetInfoMixin, IndexableMixin, TensorflowDatasetMixin):
1890
1897
  "shard": self.shard(num_shards=num_shards, index=shard_idx, contiguous=True),
1891
1898
  "fpath": posixpath.join(dataset_path, f"data-{shard_idx:05d}-of-{num_shards:05d}.arrow"),
1892
1899
  "storage_options": storage_options,
1900
+ "writer_batch_size": writer_batch_size,
1893
1901
  }
1894
1902
  for shard_idx in range(num_shards)
1895
1903
  )
@@ -1931,9 +1939,13 @@ class Dataset(DatasetInfoMixin, IndexableMixin, TensorflowDatasetMixin):
1931
1939
  json.dump(sorted_keys_dataset_info, dataset_info_file, indent=2)
1932
1940
 
1933
1941
  @staticmethod
1934
- def _save_to_disk_single(job_id: int, shard: "Dataset", fpath: str, storage_options: Optional[dict]):
1935
- batch_size = config.DEFAULT_MAX_BATCH_SIZE
1936
-
1942
+ def _save_to_disk_single(
1943
+ job_id: int,
1944
+ shard: "Dataset",
1945
+ fpath: str,
1946
+ storage_options: Optional[dict],
1947
+ writer_batch_size: int,
1948
+ ):
1937
1949
  num_examples_progress_update = 0
1938
1950
  writer = ArrowWriter(
1939
1951
  features=shard.features,
@@ -1943,7 +1955,7 @@ class Dataset(DatasetInfoMixin, IndexableMixin, TensorflowDatasetMixin):
1943
1955
  )
1944
1956
  try:
1945
1957
  _time = time.time()
1946
- for pa_table in shard.with_format("arrow").iter(batch_size):
1958
+ for pa_table in shard.with_format("arrow").iter(writer_batch_size):
1947
1959
  writer.write_table(pa_table)
1948
1960
  num_examples_progress_update += len(pa_table)
1949
1961
  if time.time() > _time + config.PBAR_REFRESH_TIME_INTERVAL:
@@ -3844,7 +3856,14 @@ class Dataset(DatasetInfoMixin, IndexableMixin, TensorflowDatasetMixin):
3844
3856
  processed_inputs.pop(column)
3845
3857
  if check_same_num_examples:
3846
3858
  input_num_examples = len(pa_inputs)
3847
- processed_inputs_num_examples = len(processed_inputs[next(iter(processed_inputs.keys()))])
3859
+ if not isinstance(processed_inputs, Mapping):
3860
+ # pa.Table, pd.DataFrame or pl.DataFrame
3861
+ processed_inputs_num_examples = len(processed_inputs)
3862
+ elif processed_inputs:
3863
+ processed_inputs_num_examples = len(next(iter(processed_inputs.values())))
3864
+ else:
3865
+ # an empty dict doesn't update any column
3866
+ processed_inputs_num_examples = input_num_examples
3848
3867
  if input_num_examples != processed_inputs_num_examples:
3849
3868
  raise DatasetTransformationNotAllowedError(
3850
3869
  "Using `.map` in batched mode on a dataset with attached indexes is allowed only if it doesn't create or remove existing examples. You can first run `.drop_index() to remove your index and then re-add it."
@@ -4690,7 +4709,10 @@ class Dataset(DatasetInfoMixin, IndexableMixin, TensorflowDatasetMixin):
4690
4709
  'text': 'the gorgeously elaborate continuation of " the lord of the rings " trilogy is so huge that a column of words cannot adequately describe co-writer/director peter jackson\'s expanded vision of j . r . r . tolkien\'s middle-earth .'}]
4691
4710
  ```
4692
4711
  """
4693
- return self.select(range(n))
4712
+ # Clamp `n` to the dataset length so taking more elements than available
4713
+ # returns the whole dataset instead of raising an IndexError, matching
4714
+ # the behavior of `IterableDataset.take`.
4715
+ return self.select(range(min(n, len(self))))
4694
4716
 
4695
4717
  @transmit_format
4696
4718
  @fingerprint_transform(inplace=False, ignore_kwargs=["load_from_cache_file", "indices_cache_file_name"])
@@ -5722,7 +5744,7 @@ class Dataset(DatasetInfoMixin, IndexableMixin, TensorflowDatasetMixin):
5722
5744
  k for k, v in self._info.features.items() if require_decoding(v, ignore_decode_attribute=True)
5723
5745
  ]
5724
5746
 
5725
- if decodable_columns:
5747
+ if decodable_columns and len(self) > 0:
5726
5748
  # Approximate the space needed to store the bytes from the external files by analyzing the first 1000 examples
5727
5749
  extra_nbytes = 0
5728
5750
 
@@ -6010,12 +6032,14 @@ class Dataset(DatasetInfoMixin, IndexableMixin, TensorflowDatasetMixin):
6010
6032
  logger.warning(
6011
6033
  f"Setting num_proc from {num_jobs} back to 1 for the {split} split to disable multiprocessing as it only contains one shard."
6012
6034
  )
6035
+ num_proc = None
6013
6036
  num_jobs = 1
6014
6037
  elif num_shards < num_jobs:
6015
6038
  logger.warning(
6016
6039
  f"Setting num_proc from {num_jobs} to {num_shards} for the {split} split as it only contains {num_shards} shards."
6017
6040
  )
6018
6041
  num_proc = num_shards
6042
+ num_jobs = num_shards
6019
6043
  kwargs_iterable = [
6020
6044
  {
6021
6045
  "self": self.shard(num_shards=num_jobs, index=job_id, contiguous=True),
@@ -7115,7 +7139,9 @@ def _concatenate_map_style_datasets(
7115
7139
  if info is None:
7116
7140
  info = DatasetInfo.from_merge([dset.info for dset in dsets])
7117
7141
  fingerprint = update_fingerprint(
7118
- "".join(dset._fingerprint for dset in dsets), _concatenate_map_style_datasets, {"info": info, "split": split}
7142
+ "".join(dset._fingerprint for dset in dsets),
7143
+ _concatenate_map_style_datasets,
7144
+ {"info": info, "split": split, "axis": axis},
7119
7145
  )
7120
7146
 
7121
7147
  # Make final concatenated dataset
@@ -1175,15 +1175,23 @@ class DatasetBuilder:
1175
1175
  return datasets
1176
1176
 
1177
1177
  def _as_streaming_dataset_single(
1178
- self,
1179
- splits_generator,
1178
+ self, splits_generator, decode_num_threads: Optional[int] = None
1180
1179
  ) -> IterableDataset:
1181
1180
  ex_iterable = self._get_examples_iterable_for_split(splits_generator)
1182
1181
  # add auth to be able to access and decode audio/image files from private repositories.
1183
1182
  token_per_repo_id = {self.repo_id: self.token} if self.repo_id else {}
1184
- return IterableDataset(
1183
+ ds = IterableDataset(
1185
1184
  ex_iterable, info=self.info, split=splits_generator.name, token_per_repo_id=token_per_repo_id
1186
1185
  )
1186
+ # Read and decode media examples (Image/Audio/Video/Mesh/...) in worker threads by default: opening and
1187
+ # reading a remote file costs network round trips rather than CPU time, so streaming remote media data is
1188
+ # latency bound without any overlap. Datasets without decodable features (text, tabular, parquet, ...) and
1189
+ # datasets read from the local disk are returned unchanged.
1190
+ if decode_num_threads is None and hasattr(self.config, "decode_num_threads"):
1191
+ decode_num_threads = self.config.decode_num_threads
1192
+ if self.base_path and self.base_path.startswith("hf://") and decode_num_threads:
1193
+ ds = ds.decode(num_threads=decode_num_threads)
1194
+ return ds
1187
1195
 
1188
1196
  @abc.abstractmethod
1189
1197
  def _split_generators(self, dl_manager: Union[DownloadManager, StreamingDownloadManager]):
@@ -180,6 +180,8 @@ def concatenate_datasets(
180
180
 
181
181
  * if axis=0, the resulting dataset's `num_shards` is the sum of each dataset's `num_shards`.
182
182
  * if axis=1, the resulting dataset has one (1) shard to not misalign data.
183
+ * if the datasets were split with [`datasets.distributed.split_dataset_by_node`], they must all be split the same way,
184
+ and the resulting dataset keeps that split.
183
185
 
184
186
  Args:
185
187
  dsets (`List[datasets.Dataset]` or `List[datasets.IterableDataset]`):
@@ -188,6 +188,9 @@ class Audio:
188
188
  if not self.decode:
189
189
  raise RuntimeError("Decoding is disabled for this feature. Please use Audio(decode=True) instead.")
190
190
 
191
+ if not isinstance(value, dict):
192
+ return value # decoding is idempotent: a value that is already an audio decoder is returned as is
193
+
191
194
  path, bytes = (value["path"], value["bytes"]) if value["bytes"] is not None else (value["path"], None)
192
195
  if path is None and bytes is None:
193
196
  raise ValueError(f"An audio sample should have one of 'path' or 'bytes' but both are None in {value}.")
@@ -1234,7 +1234,15 @@ class ClassLabel:
1234
1234
  raise ValueError(
1235
1235
  f"Class label {min_max['max']} greater than configured num_classes {self.num_classes}"
1236
1236
  )
1237
- elif isinstance(storage, pa.StringArray):
1237
+ elif (
1238
+ pa.types.is_string(storage.type)
1239
+ or pa.types.is_large_string(storage.type)
1240
+ or pa.types.is_string_view(storage.type)
1241
+ ):
1242
+ # Check the Arrow type rather than the array class: `pa.LargeStringArray` and
1243
+ # `pa.StringViewArray` are not subclasses of `pa.StringArray`, and pandas 3
1244
+ # produces `large_string` for plain string columns, so an isinstance check let
1245
+ # those fall through to an int64 cast of the label names.
1238
1246
  storage = pa.array(
1239
1247
  [self._strval2int(label) if label is not None else None for label in storage.to_pylist()]
1240
1248
  )
@@ -1974,8 +1982,14 @@ class Features(dict):
1974
1982
  for column_name, feature in self.items():
1975
1983
  self[column_name] = _visit(feature, _check_old_list)
1976
1984
 
1977
- __setitem__ = keep_features_dicts_synced(dict.__setitem__)
1978
- __delitem__ = keep_features_dicts_synced(dict.__delitem__)
1985
+ def __setitem__(self, column_name: str, feature: FeatureType):
1986
+ super().__setitem__(column_name, feature)
1987
+ self._column_requires_decoding[column_name] = require_decoding(feature)
1988
+
1989
+ def __delitem__(self, column_name: str):
1990
+ super().__delitem__(column_name)
1991
+ self._column_requires_decoding.pop(column_name, None)
1992
+
1979
1993
  update = keep_features_dicts_synced(dict.update)
1980
1994
  setdefault = keep_features_dicts_synced(dict.setdefault)
1981
1995
  pop = keep_features_dicts_synced(dict.pop)
@@ -166,6 +166,9 @@ class Image:
166
166
  if token_per_repo_id is None:
167
167
  token_per_repo_id = {}
168
168
 
169
+ if not isinstance(value, (dict, str)):
170
+ return value # decoding is idempotent: a value that is already an image is returned as is
171
+
169
172
  path, bytes_ = value["path"], value["bytes"]
170
173
  if bytes_ is None:
171
174
  if path is None:
@@ -41,7 +41,7 @@ class Mesh:
41
41
  decode (`bool`, defaults to `True`):
42
42
  Whether to decode the mesh data. If `False`,
43
43
  returns the underlying dictionary in the format `{"path": mesh_path, "bytes": mesh_bytes}`.
44
- Mesh decoding uses `trimesh` and supports `.glb`, `.ply`, and `.stl` files.
44
+ Mesh decoding uses `trimesh` and supports `.glb`, `.gltf`, `.ply`, and `.stl` files.
45
45
  """
46
46
 
47
47
  decode: bool = True
@@ -116,6 +116,9 @@ class Mesh:
116
116
  if token_per_repo_id is None:
117
117
  token_per_repo_id = {}
118
118
 
119
+ if not isinstance(value, (dict, str)):
120
+ return value # decoding is idempotent: a value that is already a mesh is returned as is
121
+
119
122
  path, bytes_ = value["path"], value["bytes"]
120
123
  if bytes_ is None:
121
124
  if path is None:
@@ -123,7 +126,7 @@ class Mesh:
123
126
  if is_local_path(path):
124
127
  file_type = _infer_mesh_file_type(path)
125
128
  if file_type is None:
126
- raise ValueError("A mesh path should have a .glb, .ply, or .stl extension.")
129
+ raise ValueError("A mesh path should have a .glb, .gltf, .ply, or .stl extension.")
127
130
  return trimesh.load(path, file_type=file_type)
128
131
  source_url = path.split("::")[-1]
129
132
  pattern = (
@@ -138,7 +141,7 @@ class Mesh:
138
141
  file_type = _infer_mesh_file_type(path)
139
142
  if file_type is None:
140
143
  raise ValueError(
141
- "Decoding mesh bytes requires a 'path' value with a .glb, .ply, or .stl extension "
144
+ "Decoding mesh bytes requires a 'path' value with a .glb, .gltf, .ply, or .stl extension "
142
145
  "to infer the mesh file type."
143
146
  )
144
147
  return trimesh.load(BytesIO(bytes_), file_type=file_type)
@@ -276,7 +279,7 @@ class Mesh:
276
279
 
277
280
 
278
281
  def _infer_mesh_file_type(path: Optional[str]) -> Optional[str]:
279
- supported_file_types = {"glb", "ply", "stl"}
282
+ supported_file_types = {"glb", "gltf", "ply", "stl"}
280
283
  if path is None:
281
284
  return None
282
285
  path_without_archive = path.split("::", 1)[0]
@@ -174,6 +174,9 @@ class Nifti:
174
174
  if token_per_repo_id is None:
175
175
  token_per_repo_id = {}
176
176
 
177
+ if not isinstance(value, (dict, str)):
178
+ return value # decoding is idempotent: a value that is already a NIfTI image is returned as is
179
+
177
180
  path, bytes_ = value["path"], value["bytes"]
178
181
  if bytes_ is None:
179
182
  if path is None:
@@ -184,6 +184,9 @@ class Video:
184
184
  if token_per_repo_id is None:
185
185
  token_per_repo_id = {}
186
186
 
187
+ if not isinstance(value, (dict, str)):
188
+ return value # decoding is idempotent: a value that is already a video decoder is returned as is
189
+
187
190
  if isinstance(value, str):
188
191
  path, bytes_ = value, None
189
192
  else:
@@ -115,6 +115,9 @@ class CsvDatasetWriter:
115
115
 
116
116
  Caller is responsible for opening and closing the handle.
117
117
  """
118
+ if len(self.dataset) == 0:
119
+ return file_obj.write(self._batch_csv((0, header, index, to_csv_kwargs)))
120
+
118
121
  written = 0
119
122
 
120
123
  if self.num_proc is None or self.num_proc == 1:
@@ -163,6 +163,9 @@ class JsonDatasetWriter:
163
163
 
164
164
  Caller is responsible for opening and closing the handle.
165
165
  """
166
+ if len(self.dataset) == 0 and not lines:
167
+ return file_obj.write(self._batch_json((0, orient, lines, to_json_kwargs)))
168
+
166
169
  written = 0
167
170
 
168
171
  if self.num_proc is None or self.num_proc == 1:
@@ -3,10 +3,10 @@ import concurrent.futures
3
3
  import contextlib
4
4
  import inspect
5
5
  import itertools
6
- import multiprocessing.pool
7
6
  import re
8
7
  import sys
9
8
  import tempfile
9
+ import threading
10
10
  import time
11
11
  from collections import Counter
12
12
  from collections.abc import Iterable, Iterator
@@ -1407,6 +1407,10 @@ class MappedExamplesIterable(_BaseExamplesIterable):
1407
1407
  max_num_running_async_map_functions_in_parallel or config.MAX_NUM_RUNNING_ASYNC_MAP_FUNCTIONS_IN_PARALLEL
1408
1408
  )
1409
1409
  self.is_batch_accumulate_arrow_table_function = is_batch_accumulate_arrow_table_function
1410
+ # Set by IterableDataset.decode when it enables threaded decoding: since examples must be decoded exactly
1411
+ # once, a later `.decode()` call reconfigures decoding from `decoding_dataset` (the dataset `.decode()` was
1412
+ # called on) rather than stacking a second decoding step on top of this one.
1413
+ self.decoding_pool: Optional[_LazyThreadPoolExecutor] = None
1410
1414
  # sanity checks
1411
1415
  if formatting and formatting.is_table:
1412
1416
  # batch_size should match for iter_arrow
@@ -1604,16 +1608,12 @@ class MappedExamplesIterable(_BaseExamplesIterable):
1604
1608
  for i, key_example in inputs_iterator:
1605
1609
  indices.append(i)
1606
1610
  tasks.append(loop.create_task(async_apply_function(key_example, i)))
1607
- # keep the total active tasks under a certain number
1611
+ # keep the total active tasks under a certain number, waiting for the oldest task to complete.
1612
+ # Polling the whole task list with asyncio.wait can't be used for that: it returns as soon as any
1613
+ # task in the list is done, so as soon as one example is slower than the examples created after it
1614
+ # (e.g. a large remote file), every wait returns instantly and this spins at 100% CPU, taking the
1615
+ # GIL away from the worker threads that were doing the actual work.
1608
1616
  if len(tasks) >= self.max_num_running_async_map_functions_in_parallel:
1609
- done, pending = loop.run_until_complete(
1610
- asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
1611
- )
1612
- while tasks and len(pending) >= self.max_num_running_async_map_functions_in_parallel:
1613
- done, pending = loop.run_until_complete(
1614
- asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
1615
- )
1616
- if len(tasks) >= 10 * self.max_num_running_async_map_functions_in_parallel:
1617
1617
  loop.run_until_complete(tasks[0])
1618
1618
  # yield finished tasks
1619
1619
  while tasks and tasks[0].done():
@@ -4517,7 +4517,7 @@ class IterableDataset(DatasetInfoMixin):
4517
4517
  token_per_repo_id=self._token_per_repo_id,
4518
4518
  )
4519
4519
 
4520
- def decode(self, enable: bool = True, num_threads: int = 0) -> "IterableDataset":
4520
+ def decode(self, enable: bool = True, num_threads: Optional[int] = None) -> "IterableDataset":
4521
4521
  """
4522
4522
  Enable or disable the dataset features decoding for audio, image, video.
4523
4523
 
@@ -4527,8 +4527,13 @@ class IterableDataset(DatasetInfoMixin):
4527
4527
  * image -> PIL.Image
4528
4528
  * video -> torchcodec.decoders.VideoDecoder
4529
4529
 
4530
- You can enable multithreading using `num_threads`. This is especially useful to speed up remote
4531
- data streaming. However it can be slower than `num_threads=0` for local data on fast disks.
4530
+ Multithreading can be enabled with `num_threads`: every media file is then read and decoded in a worker
4531
+ thread, so that the network round trips of consecutive examples overlap. This is especially useful to speed
4532
+ up remote data streaming, and [`load_dataset`] already enables it by default when streaming a remote dataset
4533
+ that has decodable features (see `HF_DATASETS_DECODE_NUM_THREADS`). Datasets read from the local disk are
4534
+ streamed with `num_threads=0`, since multithreading can be slower than decoding in the main thread for local
4535
+ data on fast disks. Since examples are decoded in advance, every thread in flight can keep one example in
4536
+ memory: lower `num_threads` to use less memory.
4532
4537
 
4533
4538
  Disabling decoding is useful if you want to iterate on the paths or bytes of the media files
4534
4539
  without actually decoding their content. To disable decoding you can use `.decode(False)`, which
@@ -4538,8 +4543,10 @@ class IterableDataset(DatasetInfoMixin):
4538
4543
  Args:
4539
4544
  enable (`bool`, defaults to `True`):
4540
4545
  Enable or disable features decoding.
4541
- num_threads (`int`, defaults to `0`):
4542
- Enable multithreading for features decoding.
4546
+ num_threads (`int`, *optional*, defaults to `None`):
4547
+ Enable multithreading for features decoding. Note that [`load_dataset`] already uses a few threads
4548
+ by default when streaming a dataset on HF with decodable features.
4549
+ By default, `num_threads` is set to 32 if at least one feature require decoding, and 0 otherwise.
4543
4550
 
4544
4551
  Returns:
4545
4552
  `IterableDataset`: A copy of the dataset with casted features.
@@ -4575,9 +4582,9 @@ class IterableDataset(DatasetInfoMixin):
4575
4582
  >>> from datasets import load_dataset
4576
4583
  >>> from tqdm import tqdm
4577
4584
  >>> ds = load_dataset("sshh12/planet-textures", split="train", streaming=True)
4578
- >>> num_threads = min(32, (os.cpu_count() or 1) + 4)
4579
- >>> ds = ds.decode(num_threads=num_threads)
4580
- >>> for _ in tqdm(ds): # 20 times faster !
4585
+ >>> # already threaded by default with 32 threads, but more threads can help going even faster
4586
+ >>> ds = ds.decode(num_threads=64)
4587
+ >>> for _ in tqdm(ds): # 2 times faster !
4581
4588
  ... ...
4582
4589
  ```
4583
4590
  """
@@ -4586,28 +4593,36 @@ class IterableDataset(DatasetInfoMixin):
4586
4593
  "Features decoding is only available for datasets with known features, but features are Unknown. "
4587
4594
  "Please set the datasets features with `ds = ds.cast(features)`."
4588
4595
  )
4589
- ds = self
4590
-
4591
- def set_decoding(decode: bool, feature):
4592
- if hasattr(feature, "decode"):
4593
- feature.decode = decode
4596
+ if num_threads is None:
4597
+ num_threads = 32 if require_decoding(self.features) else 0
4594
4598
 
4595
- if enable and num_threads > 0:
4596
- disabled_decoding_features = self.features.copy()
4597
- enabled_decoding_features = self.features.copy()
4599
+ ds = self
4600
+ # If decoding is already configured, start again from the dataset it was configured on: examples must be
4601
+ # decoded exactly once, so `.decode()` replaces the previous decoding step instead of adding a new one.
4602
+ if isinstance(ds._ex_iterable, MappedExamplesIterable) and ds._ex_iterable.decoding_pool is not None:
4603
+ # The original dataset may still have active iterators using its decoding pool.
4604
+ ds = IterableDataset(
4605
+ ds._ex_iterable.ex_iterable,
4606
+ info=ds.info,
4607
+ split=ds.split,
4608
+ formatting=ds._formatting,
4609
+ distributed=ds._distributed,
4610
+ token_per_repo_id=ds._token_per_repo_id,
4611
+ )
4598
4612
 
4599
- _visit(disabled_decoding_features, partial(set_decoding, False))
4600
- _visit(enabled_decoding_features, partial(set_decoding, True))
4613
+ if enable and num_threads:
4614
+ disabled_decoding_features = _features_with_decoding(self.features, False)
4615
+ enabled_decoding_features = _features_with_decoding(self.features, True)
4601
4616
  ds = ds.cast(disabled_decoding_features)
4602
- pool = multiprocessing.pool.ThreadPool(num_threads)
4603
- func = partial(_apply_async, pool, enabled_decoding_features.decode_example)
4617
+ pool = _LazyThreadPoolExecutor(num_threads)
4618
+ decode_example = partial(enabled_decoding_features.decode_example, token_per_repo_id=ds._token_per_repo_id)
4619
+ func = partial(_apply_async, pool, decode_example)
4604
4620
  ds = ds.map(func, features=enabled_decoding_features)
4605
4621
  assert isinstance(ds._ex_iterable, MappedExamplesIterable)
4622
+ ds._ex_iterable.decoding_pool = pool
4606
4623
  ds._ex_iterable.max_num_running_async_map_functions_in_parallel = 2 * num_threads
4607
4624
  else:
4608
- features = ds.features.copy()
4609
- _visit(features, partial(set_decoding, enable))
4610
- ds = ds.cast(features)
4625
+ ds = ds.cast(_features_with_decoding(ds.features, enable))
4611
4626
  return ds
4612
4627
 
4613
4628
  def _step(self, step: int, offset: int) -> "IterableDataset":
@@ -4663,10 +4678,20 @@ class IterableDataset(DatasetInfoMixin):
4663
4678
  Whether to drop the last incomplete batch.
4664
4679
 
4665
4680
  Example:
4681
+
4682
+ Fixed batch size:
4683
+
4666
4684
  ```py
4667
- >>> ds = load_dataset("some_dataset", streaming=True)
4685
+ >>> ds = load_dataset("some_dataset", streaming=True, split="train")
4668
4686
  >>> batched_ds = ds.batch(batch_size=32)
4669
4687
  ```
4688
+
4689
+ Batches of successive samples with identical value for "my_column":
4690
+
4691
+ ```py
4692
+ >>> ds = load_dataset("some_dataset", streaming=True, split="train")
4693
+ >>> batched_ds = ds.batch(by_column="my_column")
4694
+ ```
4670
4695
  """
4671
4696
  if batch_size is None and by_column is None:
4672
4697
  raise ValueError("IterableDataset.batch() misses `batch_size` or `by_column` argument.")
@@ -4682,7 +4707,7 @@ class IterableDataset(DatasetInfoMixin):
4682
4707
  partial(_batch_accumulate_arrow_table_by_columns, columns=columns),
4683
4708
  with_indices=True,
4684
4709
  batched=True,
4685
- batch_size=batch_size,
4710
+ batch_size=batch_size or config.DEFAULT_MAX_BATCH_SIZE,
4686
4711
  drop_last_batch=drop_last_batch,
4687
4712
  features=features,
4688
4713
  is_batch_accumulate_arrow_table_function=True,
@@ -5532,6 +5557,11 @@ def _concatenate_iterable_datasets(
5532
5557
  _check_if_features_can_be_aligned([dset.features for dset in dsets])
5533
5558
  else:
5534
5559
  _check_column_names([col_name for dset in dsets for col_name in dset.features])
5560
+ for i, dset in enumerate(dsets):
5561
+ if dsets[0]._distributed != dset._distributed:
5562
+ raise ValueError(
5563
+ f"Datasets should be identically split_by_node before concatenating, but got {dsets[0]._distributed}!={dset._distributed} at index 0 and {i}"
5564
+ )
5535
5565
 
5536
5566
  # Check format is consistent; if so, will set format for concatenated dataset
5537
5567
  if all(dset._formatting is None for dset in dsets):
@@ -5586,6 +5616,7 @@ def _concatenate_iterable_datasets(
5586
5616
  split=split,
5587
5617
  token_per_repo_id=token_per_repo_id,
5588
5618
  formatting=formatting,
5619
+ distributed=dsets[0]._distributed,
5589
5620
  )
5590
5621
 
5591
5622
 
@@ -5729,13 +5760,66 @@ def _split_by_node_iterable_dataset(
5729
5760
  )
5730
5761
 
5731
5762
 
5732
- async def _apply_async(pool, func, x):
5733
- future = pool.apply_async(func, (x,))
5734
- while True:
5735
- if future.ready():
5736
- return future.get()
5737
- else:
5738
- await asyncio.sleep(0)
5763
+ def _features_with_decoding(features: Features, decode: bool) -> Features:
5764
+ """Return a copy of `features` where media features (Image/Audio/Video/Mesh/...) are set to decode or not."""
5765
+
5766
+ def _set_decode_flag(feature):
5767
+ if hasattr(feature, "decode"):
5768
+ feature.decode = decode
5769
+
5770
+ features = features.copy()
5771
+ _visit(features, _set_decode_flag)
5772
+ # Features caches which columns require decoding: re-assign every column to refresh it after the flags change
5773
+ for column_name, feature in list(features.items()):
5774
+ features[column_name] = feature
5775
+ return features
5776
+
5777
+
5778
+ class _LazyThreadPoolExecutor:
5779
+ """A `concurrent.futures.ThreadPoolExecutor` that is created on first use instead of upfront.
5780
+
5781
+ Threaded decoding holds one of these in the dataset, and datasets get pickled all the time (to send them to
5782
+ `to_torch(num_workers=...)` worker processes for example). Creating the pool when examples are actually read
5783
+ keeps the dataset picklable: every process that iterates it creates its own pool.
5784
+ """
5785
+
5786
+ def __init__(self, max_workers: int):
5787
+ self.max_workers = max_workers
5788
+ self._pool: Optional[concurrent.futures.ThreadPoolExecutor] = None
5789
+ self._lock = threading.Lock()
5790
+
5791
+ def submit(self, fn, *args, **kwargs):
5792
+ with self._lock:
5793
+ if self._pool is None:
5794
+ self._pool = concurrent.futures.ThreadPoolExecutor(
5795
+ self.max_workers, thread_name_prefix="datasets_decode"
5796
+ )
5797
+ return self._pool.submit(fn, *args, **kwargs)
5798
+
5799
+ def shutdown(self, wait: bool = True, *, cancel_futures: bool = False):
5800
+ with self._lock:
5801
+ pool, self._pool = self._pool, None
5802
+ if pool is not None:
5803
+ pool.shutdown(wait=wait, cancel_futures=cancel_futures)
5804
+
5805
+ def __getstate__(self):
5806
+ return {"max_workers": self.max_workers}
5807
+
5808
+ def __setstate__(self, state):
5809
+ self.max_workers = state["max_workers"]
5810
+ self._pool = None
5811
+ self._lock = threading.Lock()
5812
+
5813
+
5814
+ async def _apply_async(pool: "_LazyThreadPoolExecutor", func, x):
5815
+ """Run `func(x)` in a worker thread and await its result without blocking the event loop.
5816
+
5817
+ The worker future is wrapped rather than polled in a `while not future.ready(): await asyncio.sleep(0)` loop:
5818
+ spinning wakes up the event loop thousands of times per second, which burns CPU and starves the worker threads
5819
+ that do the actual work (mostly network reads), making threaded decoding slower than plain sequential decoding.
5820
+ """
5821
+
5822
+ return await asyncio.wrap_future(pool.submit(func, x))
5739
5823
 
5740
5824
 
5741
5825
  def _batch_fn(unbatched):
@@ -34,6 +34,7 @@ class FolderBasedBuilderConfig(datasets.BuilderConfig):
34
34
  drop_metadata: bool = None
35
35
  metadata_filenames: list[str] = None
36
36
  filters: Optional[Union[ds.Expression, list[tuple], list[list[tuple]]]] = None
37
+ decode_num_threads: int = 32
37
38
 
38
39
  def __post_init__(self):
39
40
  super().__post_init__()