fugue 0.8.7.dev7__tar.gz → 0.9.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 (181) hide show
  1. {fugue-0.8.7.dev7 → fugue-0.9.0}/PKG-INFO +2 -1
  2. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/collections/sql.py +1 -1
  3. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/dataframe/utils.py +4 -18
  4. fugue-0.9.0/fugue/test/__init__.py +11 -0
  5. fugue-0.9.0/fugue/test/pandas_tester.py +24 -0
  6. fugue-0.9.0/fugue/test/plugins.py +393 -0
  7. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue.egg-info/PKG-INFO +2 -1
  8. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue.egg-info/SOURCES.txt +8 -17
  9. fugue-0.9.0/fugue.egg-info/entry_points.txt +12 -0
  10. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue.egg-info/requires.txt +24 -14
  11. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_dask/_io.py +8 -5
  12. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_dask/_utils.py +4 -4
  13. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_dask/execution_engine.py +11 -0
  14. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_dask/registry.py +2 -0
  15. fugue-0.9.0/fugue_dask/tester.py +24 -0
  16. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_duckdb/__init__.py +0 -5
  17. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_duckdb/_io.py +1 -0
  18. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_duckdb/registry.py +30 -2
  19. fugue-0.9.0/fugue_duckdb/tester.py +49 -0
  20. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_ibis/__init__.py +0 -3
  21. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_ibis/dataframe.py +2 -2
  22. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_ibis/execution_engine.py +14 -7
  23. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_ray/_constants.py +3 -4
  24. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_ray/_utils/dataframe.py +10 -21
  25. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_ray/_utils/io.py +38 -9
  26. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_ray/execution_engine.py +1 -2
  27. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_ray/registry.py +1 -0
  28. fugue-0.9.0/fugue_ray/tester.py +22 -0
  29. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_spark/execution_engine.py +5 -5
  30. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_spark/registry.py +13 -1
  31. fugue-0.9.0/fugue_spark/tester.py +78 -0
  32. fugue-0.9.0/fugue_test/__init__.py +82 -0
  33. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_test/builtin_suite.py +26 -43
  34. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_test/dataframe_suite.py +5 -14
  35. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_test/execution_suite.py +170 -143
  36. fugue-0.9.0/fugue_test/fixtures.py +61 -0
  37. fugue-0.9.0/fugue_version/__init__.py +1 -0
  38. {fugue-0.8.7.dev7 → fugue-0.9.0}/setup.cfg +17 -2
  39. {fugue-0.8.7.dev7 → fugue-0.9.0}/setup.py +28 -30
  40. fugue-0.8.7.dev7/fugue.egg-info/entry_points.txt +0 -17
  41. fugue-0.8.7.dev7/fugue_dask/ibis_engine.py +0 -62
  42. fugue-0.8.7.dev7/fugue_duckdb/ibis_engine.py +0 -56
  43. fugue-0.8.7.dev7/fugue_ibis/execution/ibis_engine.py +0 -49
  44. fugue-0.8.7.dev7/fugue_ibis/execution/pandas_backend.py +0 -54
  45. fugue-0.8.7.dev7/fugue_ibis/extensions.py +0 -203
  46. fugue-0.8.7.dev7/fugue_spark/_utils/__init__.py +0 -0
  47. fugue-0.8.7.dev7/fugue_spark/ibis_engine.py +0 -45
  48. fugue-0.8.7.dev7/fugue_test/__init__.py +0 -0
  49. fugue-0.8.7.dev7/fugue_test/ibis_suite.py +0 -92
  50. fugue-0.8.7.dev7/fugue_test/plugins/__init__.py +0 -0
  51. fugue-0.8.7.dev7/fugue_test/plugins/dask/__init__.py +0 -2
  52. fugue-0.8.7.dev7/fugue_test/plugins/dask/fixtures.py +0 -12
  53. fugue-0.8.7.dev7/fugue_test/plugins/duckdb/__init__.py +0 -2
  54. fugue-0.8.7.dev7/fugue_test/plugins/duckdb/fixtures.py +0 -9
  55. fugue-0.8.7.dev7/fugue_test/plugins/misc/__init__.py +0 -2
  56. fugue-0.8.7.dev7/fugue_test/plugins/misc/fixtures.py +0 -18
  57. fugue-0.8.7.dev7/fugue_test/plugins/ray/__init__.py +0 -2
  58. fugue-0.8.7.dev7/fugue_test/plugins/ray/fixtures.py +0 -9
  59. fugue-0.8.7.dev7/fugue_version/__init__.py +0 -1
  60. {fugue-0.8.7.dev7 → fugue-0.9.0}/LICENSE +0 -0
  61. {fugue-0.8.7.dev7 → fugue-0.9.0}/README.md +0 -0
  62. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/__init__.py +0 -0
  63. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/_utils/__init__.py +0 -0
  64. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/_utils/display.py +0 -0
  65. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/_utils/exception.py +0 -0
  66. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/_utils/interfaceless.py +0 -0
  67. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/_utils/io.py +0 -0
  68. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/_utils/misc.py +0 -0
  69. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/_utils/registry.py +0 -0
  70. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/api.py +0 -0
  71. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/bag/__init__.py +0 -0
  72. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/bag/array_bag.py +0 -0
  73. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/bag/bag.py +0 -0
  74. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/collections/__init__.py +0 -0
  75. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/collections/partition.py +0 -0
  76. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/collections/yielded.py +0 -0
  77. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/column/__init__.py +0 -0
  78. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/column/expressions.py +0 -0
  79. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/column/functions.py +0 -0
  80. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/column/sql.py +0 -0
  81. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/constants.py +0 -0
  82. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/dataframe/__init__.py +0 -0
  83. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/dataframe/api.py +0 -0
  84. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/dataframe/array_dataframe.py +0 -0
  85. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/dataframe/arrow_dataframe.py +0 -0
  86. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/dataframe/dataframe.py +0 -0
  87. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/dataframe/dataframe_iterable_dataframe.py +0 -0
  88. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/dataframe/dataframes.py +0 -0
  89. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/dataframe/function_wrapper.py +0 -0
  90. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/dataframe/iterable_dataframe.py +0 -0
  91. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/dataframe/pandas_dataframe.py +0 -0
  92. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/dataset/__init__.py +0 -0
  93. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/dataset/api.py +0 -0
  94. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/dataset/dataset.py +0 -0
  95. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/dev.py +0 -0
  96. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/exceptions.py +0 -0
  97. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/execution/__init__.py +0 -0
  98. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/execution/api.py +0 -0
  99. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/execution/execution_engine.py +0 -0
  100. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/execution/factory.py +0 -0
  101. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/execution/native_execution_engine.py +0 -0
  102. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/extensions/__init__.py +0 -0
  103. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/extensions/_builtins/__init__.py +0 -0
  104. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/extensions/_builtins/creators.py +0 -0
  105. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/extensions/_builtins/outputters.py +0 -0
  106. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/extensions/_builtins/processors.py +0 -0
  107. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/extensions/_utils.py +0 -0
  108. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/extensions/context.py +0 -0
  109. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/extensions/creator/__init__.py +0 -0
  110. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/extensions/creator/convert.py +0 -0
  111. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/extensions/creator/creator.py +0 -0
  112. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/extensions/outputter/__init__.py +0 -0
  113. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/extensions/outputter/convert.py +0 -0
  114. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/extensions/outputter/outputter.py +0 -0
  115. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/extensions/processor/__init__.py +0 -0
  116. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/extensions/processor/convert.py +0 -0
  117. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/extensions/processor/processor.py +0 -0
  118. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/extensions/transformer/__init__.py +0 -0
  119. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/extensions/transformer/constants.py +0 -0
  120. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/extensions/transformer/convert.py +0 -0
  121. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/extensions/transformer/transformer.py +0 -0
  122. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/plugins.py +0 -0
  123. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/py.typed +0 -0
  124. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/registry.py +0 -0
  125. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/rpc/__init__.py +0 -0
  126. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/rpc/base.py +0 -0
  127. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/rpc/flask.py +0 -0
  128. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/sql/__init__.py +0 -0
  129. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/sql/_utils.py +0 -0
  130. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/sql/_visitors.py +0 -0
  131. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/sql/api.py +0 -0
  132. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/sql/workflow.py +0 -0
  133. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/workflow/__init__.py +0 -0
  134. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/workflow/_checkpoint.py +0 -0
  135. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/workflow/_tasks.py +0 -0
  136. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/workflow/_workflow_context.py +0 -0
  137. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/workflow/api.py +0 -0
  138. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/workflow/input.py +0 -0
  139. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/workflow/module.py +0 -0
  140. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue/workflow/workflow.py +0 -0
  141. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue.egg-info/dependency_links.txt +0 -0
  142. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue.egg-info/top_level.txt +0 -0
  143. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_contrib/__init__.py +0 -0
  144. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_contrib/contrib.py +0 -0
  145. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_contrib/seaborn/__init__.py +0 -0
  146. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_contrib/viz/__init__.py +0 -0
  147. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_contrib/viz/_ext.py +0 -0
  148. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_dask/__init__.py +0 -0
  149. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_dask/_constants.py +0 -0
  150. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_dask/dataframe.py +0 -0
  151. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_duckdb/_utils.py +0 -0
  152. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_duckdb/dask.py +0 -0
  153. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_duckdb/dataframe.py +0 -0
  154. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_duckdb/execution_engine.py +0 -0
  155. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_ibis/_compat.py +0 -0
  156. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_ibis/_utils.py +0 -0
  157. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_notebook/__init__.py +0 -0
  158. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_notebook/env.py +0 -0
  159. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_notebook/nbextension/README.md +0 -0
  160. {fugue-0.8.7.dev7/fugue_ibis/execution → fugue-0.9.0/fugue_notebook/nbextension}/__init__.py +0 -0
  161. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_notebook/nbextension/description.yaml +0 -0
  162. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_notebook/nbextension/main.js +0 -0
  163. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_polars/__init__.py +0 -0
  164. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_polars/_utils.py +0 -0
  165. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_polars/polars_dataframe.py +0 -0
  166. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_polars/registry.py +0 -0
  167. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_ray/__init__.py +0 -0
  168. {fugue-0.8.7.dev7/fugue_notebook/nbextension → fugue-0.9.0/fugue_ray/_utils}/__init__.py +0 -0
  169. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_ray/_utils/cluster.py +0 -0
  170. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_ray/dataframe.py +0 -0
  171. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_spark/__init__.py +0 -0
  172. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_spark/_constants.py +0 -0
  173. {fugue-0.8.7.dev7/fugue_ray → fugue-0.9.0/fugue_spark}/_utils/__init__.py +0 -0
  174. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_spark/_utils/convert.py +0 -0
  175. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_spark/_utils/io.py +0 -0
  176. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_spark/_utils/misc.py +0 -0
  177. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_spark/_utils/partition.py +0 -0
  178. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_spark/dataframe.py +0 -0
  179. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_sql/__init__.py +0 -0
  180. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_sql/exceptions.py +0 -0
  181. {fugue-0.8.7.dev7 → fugue-0.9.0}/fugue_test/bag_suite.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fugue
3
- Version: 0.8.7.dev7
3
+ Version: 0.9.0
4
4
  Summary: An abstraction layer for distributed computation
5
5
  Home-page: http://github.com/fugue-project/fugue
6
6
  Author: The Fugue Development Team
@@ -292,6 +292,7 @@ Classifier: Programming Language :: Python :: 3
292
292
  Classifier: Programming Language :: Python :: 3.8
293
293
  Classifier: Programming Language :: Python :: 3.9
294
294
  Classifier: Programming Language :: Python :: 3.10
295
+ Classifier: Programming Language :: Python :: 3.11
295
296
  Classifier: Programming Language :: Python :: 3 :: Only
296
297
  Requires-Python: >=3.8
297
298
  Description-Content-Type: text/markdown
@@ -15,7 +15,7 @@ class TempTableName:
15
15
  """Generating a temporary, random and globaly unique table name"""
16
16
 
17
17
  def __init__(self):
18
- self.key = "_" + str(uuid4())[:5]
18
+ self.key = "_" + str(uuid4())[:5].upper()
19
19
 
20
20
  def __repr__(self) -> str:
21
21
  return _TEMP_TABLE_EXPR_PREFIX + self.key + _TEMP_TABLE_EXPR_SUFFIX
@@ -21,22 +21,6 @@ normalize_dataframe_column_names = normalize_column_names
21
21
  rename_dataframe_column_names = rename
22
22
 
23
23
 
24
- def _pa_type_eq(t1: pa.DataType, t2: pa.DataType) -> bool:
25
- # should ignore the name difference of list
26
- # e.g. list<item: string> == list<l: string>
27
- if pa.types.is_list(t1) and pa.types.is_list(t2): # pragma: no cover
28
- return _pa_type_eq(t1.value_type, t2.value_type)
29
- return t1 == t2
30
-
31
-
32
- def _schema_eq(s1: Schema, s2: Schema) -> bool:
33
- if s1 == s2:
34
- return True
35
- return s1.names == s2.names and all(
36
- _pa_type_eq(f1.type, f2.type) for f1, f2 in zip(s1.fields, s2.fields)
37
- )
38
-
39
-
40
24
  def _df_eq(
41
25
  df: DataFrame,
42
26
  data: Any,
@@ -46,6 +30,7 @@ def _df_eq(
46
30
  check_schema: bool = True,
47
31
  check_content: bool = True,
48
32
  no_pandas: bool = False,
33
+ equal_type_groups: Optional[List[List[Any]]] = None,
49
34
  throw=False,
50
35
  ) -> bool:
51
36
  """Compare if two dataframes are equal. Is for internal, unit test
@@ -66,6 +51,7 @@ def _df_eq(
66
51
  :param no_pandas: if true, it will compare the string representations of the
67
52
  dataframes, otherwise, it will convert both to pandas dataframe to compare,
68
53
  defaults to False
54
+ :param equal_type_groups: the groups to treat as equal types, defaults to None.
69
55
  :param throw: if to throw error if not equal, defaults to False
70
56
  :return: if they equal
71
57
  """
@@ -78,8 +64,8 @@ def _df_eq(
78
64
  assert (
79
65
  df1.count() == df2.count()
80
66
  ), f"count mismatch {df1.count()}, {df2.count()}"
81
- assert not check_schema or _schema_eq(
82
- df.schema, df2.schema
67
+ assert not check_schema or df.schema.is_like(
68
+ df2.schema, equal_groups=equal_type_groups
83
69
  ), f"schema mismatch {df.schema.pa_schema}, {df2.schema.pa_schema}"
84
70
  if not check_content:
85
71
  return True
@@ -0,0 +1,11 @@
1
+ # flake8: noqa
2
+ from .pandas_tester import NativeTestBackend, PandasTestBackend
3
+ from .plugins import (
4
+ FugueTestBackend,
5
+ FugueTestContext,
6
+ FugueTestSuite,
7
+ extract_conf,
8
+ fugue_test_backend,
9
+ fugue_test_suite,
10
+ with_backend,
11
+ )
@@ -0,0 +1,24 @@
1
+ from contextlib import contextmanager
2
+ from typing import Any, Dict, Iterator
3
+
4
+ from .plugins import FugueTestBackend, fugue_test_backend
5
+
6
+
7
+ @fugue_test_backend
8
+ class PandasTestBackend(FugueTestBackend):
9
+ name = "pandas"
10
+
11
+ @classmethod
12
+ @contextmanager
13
+ def session_context(cls, session_conf: Dict[str, Any]) -> Iterator[Any]:
14
+ yield "pandas" # pragma: no cover
15
+
16
+
17
+ @fugue_test_backend
18
+ class NativeTestBackend(FugueTestBackend):
19
+ name = "native"
20
+
21
+ @classmethod
22
+ @contextmanager
23
+ def session_context(cls, session_conf: Dict[str, Any]) -> Iterator[Any]:
24
+ yield "native" # pragma: no cover
@@ -0,0 +1,393 @@
1
+ from contextlib import contextmanager
2
+ from dataclasses import dataclass
3
+ from pathlib import Path
4
+ from typing import Any, Dict, Iterator, List, Optional, Tuple, Type
5
+ from fugue.dataframe.utils import _df_eq
6
+ from triad import assert_or_throw, run_once
7
+ from triad.utils.entry_points import load_entry_point
8
+
9
+ try:
10
+ import pytest
11
+
12
+ _HAS_PYTEST = True
13
+ except ImportError: # pragma: no cover
14
+ _HAS_PYTEST = False
15
+
16
+
17
+ _FUGUE_TEST_BACKENDS: Dict[str, Type["FugueTestBackend"]] = {}
18
+ _FUGUE_TEST_ALL_INI_CONF: Dict[str, Any] = {}
19
+ _FUGUE_TEST_INI_FUGUE_CONF: Dict[str, Any] = {}
20
+
21
+
22
+ def _set_global_conf(conf: Dict[str, Any]) -> None:
23
+ global _FUGUE_TEST_ALL_INI_CONF, _FUGUE_TEST_INI_FUGUE_CONF
24
+ _FUGUE_TEST_ALL_INI_CONF = conf
25
+ _FUGUE_TEST_INI_FUGUE_CONF = _extract_fugue_conf(conf)
26
+
27
+
28
+ def _get_all_ini_conf() -> Dict[str, Any]:
29
+ return _FUGUE_TEST_ALL_INI_CONF
30
+
31
+
32
+ @run_once
33
+ def _load_all_backends() -> None:
34
+ from fugue.constants import FUGUE_ENTRYPOINT
35
+
36
+ load_entry_point(FUGUE_ENTRYPOINT)
37
+
38
+
39
+ def with_backend(ctx: Any, *other: Any, skip_missing: bool = False) -> Any:
40
+ """The decorator to set the backend context for the test function
41
+
42
+ :param ctx: the object to specify the backend
43
+ :param other: more objects to specify the additional backends
44
+ :param skip_missing: skip if an object can't be converted to a backend,
45
+ defaults to False
46
+
47
+ .. admonition:: Examples
48
+
49
+ .. code-block:: python
50
+
51
+ import fugue.test as ft
52
+ import fugue.api as fa
53
+
54
+ @ft.with_backend("pandas", "spark")
55
+ def test_spark(path_fixture):
56
+ # test load and count under spark and pandas
57
+ df = fa.load(path_fixture)
58
+ assert fa.count(df) == 3
59
+
60
+ .. note::
61
+
62
+ ``ctx`` can be a string or a tuple of string and dict. If it contains a
63
+ dict, the dict will be merged with the default configuration for the backend.
64
+
65
+ the configuration of each backend is extracted from the pytest ini configuration
66
+ under ``fugue_test_conf``. The configuration must prefixed with the backend
67
+ name.
68
+
69
+ ``fugue_test_conf`` is defined at the parallel place where you define pytest
70
+ ``addopts``. For example, if you define them in ``setup.cfg``, then it
71
+ looks like:
72
+
73
+ .. code-block:: ini
74
+
75
+ [tool:pytest]
76
+ addopts =
77
+ -p pytest_cov
78
+ --cov=fugue
79
+ fugue_test_conf =
80
+ fugue.test.dummy=dummy
81
+ fugue.test:bool=true
82
+ # ray backend settings
83
+ ray.num_cpus:int=2
84
+ # dask backend settings
85
+ dask.processes:bool=true
86
+ dask.n_workers:int=3
87
+ dask.threads_per_worker:int=1
88
+ """
89
+ import pytest
90
+
91
+ _load_all_backends()
92
+
93
+ _ctx = _construct_parameterized_fixture([ctx] + list(other), skip_missing)
94
+ return lambda f: pytest.mark.parametrize("backend_context", _ctx, indirect=True)(
95
+ pytest.mark.usefixtures("backend_context")(f)
96
+ )
97
+
98
+
99
+ def fugue_test_backend(cls: Type["FugueTestBackend"]) -> Type["FugueTestBackend"]:
100
+ """The decorator to register a Fugue test backend. Most Fugue users don't need
101
+ to use this decorator.
102
+
103
+ :param cls: the FugueTestBackend class
104
+
105
+ .. admonition:: Examples
106
+
107
+ .. code-block:: python
108
+
109
+ import fugue.test as ft
110
+
111
+ @ft.fugue_test_backend
112
+ class MyBackend(ft.FugueTestBackend):
113
+ name = "my_backend" # the name of the backend
114
+
115
+ @classmethod
116
+ @contextmanager
117
+ def session_context(cls, session_conf: Dict[str, Any]) -> Iterator[Any]:
118
+ # create a session object
119
+ yield session
120
+
121
+ """
122
+ assert_or_throw(
123
+ issubclass(cls, FugueTestBackend),
124
+ ValueError(f"{cls} is not a FugueTestBackend"),
125
+ )
126
+ name = cls.name.strip().lower()
127
+ assert_or_throw(
128
+ name != "" and name != "fugue",
129
+ ValueError(f"Fugue test backend name cannot be empty or fugue: {cls}"),
130
+ )
131
+ assert_or_throw(
132
+ name not in _FUGUE_TEST_BACKENDS,
133
+ ValueError(f"Duplicate Fugue test backend name: {name}"),
134
+ )
135
+ _FUGUE_TEST_BACKENDS[name] = cls
136
+ return cls
137
+
138
+
139
+ class FugueTestSuite:
140
+ """The base class for Fugue test suite. Most Fugue users don't need to use this
141
+ class or the decorator :func:`~.fugue_test_suite`. The dccorator and the class
142
+ are often used together.
143
+
144
+ .. admonition:: Examples
145
+
146
+ .. code-block:: python
147
+
148
+ import fugue.test as ft
149
+
150
+ @ft.fugue_test_suite("pandas")
151
+ class MyTest(ft.FugueTestSuite):
152
+ def test_pandas(self):
153
+ # test pandas
154
+ pass
155
+
156
+ def test_spark(self):
157
+ # test spark
158
+ pass
159
+ """
160
+
161
+ backend: Any
162
+ tmp_path: Path
163
+ equal_type_groups: Any = None
164
+
165
+ __test__ = False
166
+ _test_context: Any = None
167
+
168
+ if _HAS_PYTEST:
169
+
170
+ @pytest.fixture(autouse=True)
171
+ def init_builtin_per_func_context(self, tmp_path):
172
+ self.tmp_path = tmp_path
173
+
174
+ @property
175
+ def context(self) -> "FugueTestContext":
176
+ """The ``FugueTestContext`` object"""
177
+ return self._test_context
178
+
179
+ @property
180
+ def engine(self) -> Any:
181
+ """The engine object inside the ``FugueTestContext``"""
182
+ return self.context.engine
183
+
184
+ def get_equal_type_groups(self) -> Optional[List[List[Any]]]:
185
+ return None # pragma: no cover
186
+
187
+ def df_eq(self, *args: Any, **kwargs: Any) -> bool:
188
+ """A wrapper of :func:`~fugue.dataframe.utils.df_eq`"""
189
+ if "equal_type_groups" not in kwargs:
190
+ kwargs["equal_type_groups"] = self.equal_type_groups
191
+ return _df_eq(*args, **kwargs)
192
+
193
+
194
+ def fugue_test_suite(backend: Any, mark_test: Optional[bool] = None) -> Any:
195
+ def deco(cls: Type["FugueTestSuite"]) -> Type["FugueTestSuite"]:
196
+ import pytest
197
+
198
+ assert_or_throw(
199
+ issubclass(cls, FugueTestSuite),
200
+ ValueError(f"{cls} is not a FugueTestSuite"),
201
+ )
202
+ if mark_test is not None:
203
+ cls.__test__ = mark_test
204
+ c, extra_conf = _parse_backend(backend)
205
+ return pytest.mark.parametrize(
206
+ "backend_context", [pytest.param((c, extra_conf), id=c)], indirect=True
207
+ )(pytest.mark.usefixtures("_class_backend_context")(cls))
208
+
209
+ return deco
210
+
211
+
212
+ @dataclass
213
+ class FugueTestContext:
214
+ """The context object for Fugue test
215
+
216
+ :param engine: the Fugue ExecutionEngine object
217
+ :param session: the original session wrapped by the Fugue ExecutionEngine
218
+ :param name: the backend name
219
+ """
220
+
221
+ engine: Any
222
+ session: Any
223
+ name: str
224
+
225
+
226
+ class FugueTestBackend:
227
+ """The base class for Fugue test backend. Most Fugue users don't need to use this
228
+ class or the decorator :func:`~.fugue_test_backend`. The dccorator and the class
229
+ are often used together.
230
+
231
+ .. admonition:: Examples
232
+
233
+ .. code-block:: python
234
+
235
+ import fugue.test as ft
236
+
237
+ @ft.fugue_test_backend
238
+ class MyBackend(ft.FugueTestBackend):
239
+ name = "my_backend" # the name of the backend
240
+
241
+ @classmethod
242
+ @contextmanager
243
+ def session_context(cls, session_conf: Dict[str, Any]) -> Iterator[Any]:
244
+ # create a session object
245
+ yield session
246
+ """
247
+
248
+ name = ""
249
+ default_session_conf: Dict[str, Any] = {}
250
+ default_fugue_conf: Dict[str, Any] = {}
251
+ session_conf: Dict[str, Any] = {}
252
+ fugue_conf: Dict[str, Any] = {}
253
+
254
+ @classmethod
255
+ def transform_session_conf(cls, conf: Dict[str, Any]) -> Dict[str, Any]:
256
+ """Extract and transform the ``conf`` and keep the ones specific to
257
+ this backend. The default implementation will extract the configuration
258
+ prefixed with ``<backend name>.`` and remove the prefix.
259
+
260
+ :param conf: the raw configuration
261
+ :return: the transformed configuration
262
+ """
263
+ return extract_conf(conf, cls.name + ".", remove_prefix=True)
264
+
265
+ @classmethod
266
+ @contextmanager
267
+ def session_context(cls, session_conf: Dict[str, Any]) -> Iterator[Any]:
268
+ """Create the backend native session object
269
+
270
+ :param session_conf: the configuration for this backend
271
+ :yield: the session object
272
+ """
273
+ raise NotImplementedError # pragma: no cover
274
+
275
+ @classmethod
276
+ @contextmanager
277
+ def generate_session_fixture(cls) -> Iterator[Any]:
278
+ """Generate the session fixture for this backend from the default backend
279
+ configuration and then the pytest ini configuration under ``fugue_test_conf``
280
+
281
+ :yield: the session object
282
+ """
283
+ session_conf = _merge_dicts(
284
+ cls.default_session_conf,
285
+ cls.transform_session_conf(_FUGUE_TEST_ALL_INI_CONF),
286
+ )
287
+ with cls.session_context(session_conf) as session:
288
+ yield session
289
+
290
+ @classmethod
291
+ @contextmanager
292
+ def generate_context_fixture(
293
+ cls, session: object, extra_fugue_conf: Dict[str, Any]
294
+ ) -> Iterator[FugueTestContext]:
295
+ """Generate the ``FugueTestContext`` fixture for this backend from the
296
+ default backend configuration and then the pytest ini configuration under
297
+ ``fugue_test_conf`` and then ``extra_fugue_conf``.
298
+
299
+ :param session: the session object
300
+ :param extra_fugue_conf: the extra fugue configuration
301
+
302
+ :yield: the ``FugueTestContext`` object
303
+ """
304
+ import fugue.api as fa
305
+
306
+ fugue_conf = _merge_dicts(
307
+ cls.default_fugue_conf,
308
+ _FUGUE_TEST_INI_FUGUE_CONF,
309
+ _extract_fugue_conf(extra_fugue_conf),
310
+ )
311
+ with fa.engine_context(session, fugue_conf) as engine:
312
+ yield FugueTestContext(engine=engine, session=session, name=cls.name)
313
+
314
+
315
+ def extract_conf(
316
+ conf: Dict[str, Any], prefix: str, remove_prefix: bool
317
+ ) -> Dict[str, Any]:
318
+ """Extract the configuration prefixed with ``prefix`` and optionally remove the
319
+ prefix
320
+
321
+ :param conf: the raw configuration
322
+ :param prefix: the prefix
323
+ :param remove_prefix: whether to remove the prefix
324
+
325
+ :return: the extracted configuration
326
+ """
327
+ res: Dict[str, Any] = {}
328
+ for k, v in conf.items():
329
+ if k.startswith(prefix):
330
+ if remove_prefix:
331
+ k = k[len(prefix) :]
332
+ res[k] = v
333
+ return res
334
+
335
+
336
+ @contextmanager
337
+ def _make_backend_context(obj: Any, session: Any) -> Iterator[Any]:
338
+ _load_all_backends()
339
+ key, extra_conf = _parse_backend(obj)
340
+ assert_or_throw(
341
+ key in _FUGUE_TEST_BACKENDS,
342
+ lambda: ValueError(
343
+ f"Undefined Fugue test backend: {key}, "
344
+ f"available backends: {list(_FUGUE_TEST_BACKENDS.keys())}"
345
+ ),
346
+ )
347
+ with _FUGUE_TEST_BACKENDS[key].generate_context_fixture(session, extra_conf) as ctx:
348
+ yield ctx
349
+
350
+
351
+ def _extract_fugue_conf(conf: Dict[str, Any]) -> Dict[str, Any]:
352
+ return extract_conf(conf, "fugue.", remove_prefix=False)
353
+
354
+
355
+ def _construct_parameterized_fixture(ctx: List[Any], skip_missing: bool) -> List[Any]:
356
+ import pytest
357
+
358
+ _ctx: List[Tuple[str, Dict[str, Any]]] = []
359
+ for x in ctx:
360
+ c, extra_conf = _parse_backend(x)
361
+ if c not in _FUGUE_TEST_BACKENDS:
362
+ if not skip_missing:
363
+ raise ValueError(
364
+ f"Undefined Fugue test backend: {c}, "
365
+ f"available backends: {list(_FUGUE_TEST_BACKENDS.keys())}"
366
+ )
367
+ else:
368
+ _ctx.append(
369
+ pytest.param(
370
+ c,
371
+ marks=pytest.mark.xfail(
372
+ reason="Undefined Fugue test backend", run=False
373
+ ),
374
+ id=c,
375
+ )
376
+ )
377
+ else:
378
+ _ctx.append(pytest.param((c, extra_conf), id=c))
379
+ return _ctx
380
+
381
+
382
+ def _merge_dicts(*dicts: Dict[str, Any]) -> Dict[str, Any]:
383
+ res: Dict[str, Any] = {}
384
+ for d in dicts:
385
+ res.update(d)
386
+ return res
387
+
388
+
389
+ def _parse_backend(ctx: Any) -> Tuple[str, Dict[str, Any]]:
390
+ if isinstance(ctx, str):
391
+ return ctx, {}
392
+ else:
393
+ return ctx[0], ctx[1]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fugue
3
- Version: 0.8.7.dev7
3
+ Version: 0.9.0
4
4
  Summary: An abstraction layer for distributed computation
5
5
  Home-page: http://github.com/fugue-project/fugue
6
6
  Author: The Fugue Development Team
@@ -292,6 +292,7 @@ Classifier: Programming Language :: Python :: 3
292
292
  Classifier: Programming Language :: Python :: 3.8
293
293
  Classifier: Programming Language :: Python :: 3.9
294
294
  Classifier: Programming Language :: Python :: 3.10
295
+ Classifier: Programming Language :: Python :: 3.11
295
296
  Classifier: Programming Language :: Python :: 3 :: Only
296
297
  Requires-Python: >=3.8
297
298
  Description-Content-Type: text/markdown
@@ -81,6 +81,9 @@ fugue/sql/_utils.py
81
81
  fugue/sql/_visitors.py
82
82
  fugue/sql/api.py
83
83
  fugue/sql/workflow.py
84
+ fugue/test/__init__.py
85
+ fugue/test/pandas_tester.py
86
+ fugue/test/plugins.py
84
87
  fugue/workflow/__init__.py
85
88
  fugue/workflow/_checkpoint.py
86
89
  fugue/workflow/_tasks.py
@@ -100,25 +103,21 @@ fugue_dask/_io.py
100
103
  fugue_dask/_utils.py
101
104
  fugue_dask/dataframe.py
102
105
  fugue_dask/execution_engine.py
103
- fugue_dask/ibis_engine.py
104
106
  fugue_dask/registry.py
107
+ fugue_dask/tester.py
105
108
  fugue_duckdb/__init__.py
106
109
  fugue_duckdb/_io.py
107
110
  fugue_duckdb/_utils.py
108
111
  fugue_duckdb/dask.py
109
112
  fugue_duckdb/dataframe.py
110
113
  fugue_duckdb/execution_engine.py
111
- fugue_duckdb/ibis_engine.py
112
114
  fugue_duckdb/registry.py
115
+ fugue_duckdb/tester.py
113
116
  fugue_ibis/__init__.py
114
117
  fugue_ibis/_compat.py
115
118
  fugue_ibis/_utils.py
116
119
  fugue_ibis/dataframe.py
117
120
  fugue_ibis/execution_engine.py
118
- fugue_ibis/extensions.py
119
- fugue_ibis/execution/__init__.py
120
- fugue_ibis/execution/ibis_engine.py
121
- fugue_ibis/execution/pandas_backend.py
122
121
  fugue_notebook/__init__.py
123
122
  fugue_notebook/env.py
124
123
  fugue_notebook/nbextension/README.md
@@ -134,6 +133,7 @@ fugue_ray/_constants.py
134
133
  fugue_ray/dataframe.py
135
134
  fugue_ray/execution_engine.py
136
135
  fugue_ray/registry.py
136
+ fugue_ray/tester.py
137
137
  fugue_ray/_utils/__init__.py
138
138
  fugue_ray/_utils/cluster.py
139
139
  fugue_ray/_utils/dataframe.py
@@ -142,8 +142,8 @@ fugue_spark/__init__.py
142
142
  fugue_spark/_constants.py
143
143
  fugue_spark/dataframe.py
144
144
  fugue_spark/execution_engine.py
145
- fugue_spark/ibis_engine.py
146
145
  fugue_spark/registry.py
146
+ fugue_spark/tester.py
147
147
  fugue_spark/_utils/__init__.py
148
148
  fugue_spark/_utils/convert.py
149
149
  fugue_spark/_utils/io.py
@@ -156,14 +156,5 @@ fugue_test/bag_suite.py
156
156
  fugue_test/builtin_suite.py
157
157
  fugue_test/dataframe_suite.py
158
158
  fugue_test/execution_suite.py
159
- fugue_test/ibis_suite.py
160
- fugue_test/plugins/__init__.py
161
- fugue_test/plugins/dask/__init__.py
162
- fugue_test/plugins/dask/fixtures.py
163
- fugue_test/plugins/duckdb/__init__.py
164
- fugue_test/plugins/duckdb/fixtures.py
165
- fugue_test/plugins/misc/__init__.py
166
- fugue_test/plugins/misc/fixtures.py
167
- fugue_test/plugins/ray/__init__.py
168
- fugue_test/plugins/ray/fixtures.py
159
+ fugue_test/fixtures.py
169
160
  fugue_version/__init__.py
@@ -0,0 +1,12 @@
1
+ [fugue.plugins]
2
+ dask = fugue_dask.registry [dask]
3
+ duckdb = fugue_duckdb.registry [duckdb]
4
+ ibis = fugue_ibis [ibis]
5
+ polars = fugue_polars.registry [polars]
6
+ ray = fugue_ray.registry [ray]
7
+ spark = fugue_spark.registry [spark]
8
+
9
+ [pytest11]
10
+ fugue_test = fugue_test
11
+ fugue_test_fixtures = fugue_test.fixtures
12
+
@@ -1,41 +1,50 @@
1
- triad==0.9.2.dev8
1
+ triad>=0.9.6
2
2
  adagio>=0.2.4
3
- qpd>=0.4.4
4
- fugue-sql-antlr>=0.1.6
5
- sqlglot
6
- jinja2
7
3
 
8
4
  [all]
5
+ qpd>=0.4.4
6
+ fugue-sql-antlr>=0.2.0
9
7
  sqlglot
10
8
  jinja2
11
- fugue-sql-antlr[cpp]>=0.1.6
12
9
  pyspark>=3.1.1
13
10
  dask[dataframe,distributed]>=2023.5.0
14
11
  dask-sql
15
- ray[data]>=2.4.0
12
+ ray[data]>=2.5.0
16
13
  notebook
17
14
  jupyterlab
18
15
  ipython>=7.10.0
19
16
  duckdb>=0.5.0
20
17
  pyarrow>=6.0.1
21
- pandas>=2.0.2
22
- ibis-framework<6,>=3.2.0
18
+ pandas<2.2,>=2.0.2
19
+ ibis-framework
23
20
  polars
24
21
 
25
22
  [cpp_sql_parser]
26
- fugue-sql-antlr[cpp]>=0.1.6
23
+ fugue-sql-antlr[cpp]>=0.2.0
27
24
 
28
25
  [dask]
29
26
  dask[dataframe,distributed]>=2023.5.0
30
27
  pyarrow>=7.0.0
31
28
  pandas>=2.0.2
32
29
 
30
+ [dask:python_version >= "3.11.9"]
31
+ dask[dataframe,distributed]>=2024.4.0
32
+
33
33
  [duckdb]
34
+ qpd>=0.4.4
35
+ fugue-sql-antlr>=0.2.0
36
+ sqlglot
37
+ jinja2
34
38
  duckdb>=0.5.0
35
39
  numpy
36
40
 
37
41
  [ibis]
38
- ibis-framework<6,>=3.2.0
42
+ qpd>=0.4.4
43
+ fugue-sql-antlr>=0.2.0
44
+ sqlglot
45
+ jinja2
46
+ ibis-framework
47
+ pandas<2.2
39
48
 
40
49
  [notebook]
41
50
  notebook
@@ -46,15 +55,16 @@ ipython>=7.10.0
46
55
  polars
47
56
 
48
57
  [ray]
49
- ray[data]>=2.4.0
58
+ ray[data]>=2.5.0
50
59
  duckdb>=0.5.0
51
- pyarrow>=6.0.1
60
+ pyarrow>=7.0.0
61
+ pandas<2.2
52
62
 
53
63
  [spark]
54
64
  pyspark>=3.1.1
55
65
 
56
66
  [sql]
57
67
  qpd>=0.4.4
58
- fugue-sql-antlr>=0.1.6
68
+ fugue-sql-antlr>=0.2.0
59
69
  sqlglot
60
70
  jinja2