graphique 1.6__tar.gz → 1.7__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.
- {graphique-1.6 → graphique-1.7}/PKG-INFO +6 -5
- {graphique-1.6 → graphique-1.7}/README.md +3 -2
- {graphique-1.6 → graphique-1.7}/graphique/__init__.py +0 -2
- {graphique-1.6 → graphique-1.7}/graphique/core.py +25 -20
- {graphique-1.6 → graphique-1.7}/graphique/inputs.py +2 -2
- {graphique-1.6 → graphique-1.7}/graphique/interface.py +2 -2
- {graphique-1.6 → graphique-1.7}/graphique/models.py +1 -1
- {graphique-1.6 → graphique-1.7}/graphique/scalars.py +26 -20
- {graphique-1.6 → graphique-1.7}/graphique/shell.py +11 -2
- {graphique-1.6 → graphique-1.7}/graphique.egg-info/PKG-INFO +6 -5
- {graphique-1.6 → graphique-1.7}/graphique.egg-info/SOURCES.txt +1 -0
- graphique-1.7/graphique.egg-info/requires.txt +8 -0
- {graphique-1.6 → graphique-1.7}/pyproject.toml +3 -10
- graphique-1.7/requirements.in +2 -0
- graphique-1.7/tests/test_bench.py +21 -0
- {graphique-1.6 → graphique-1.7}/tests/test_core.py +18 -8
- {graphique-1.6 → graphique-1.7}/tests/test_models.py +1 -3
- graphique-1.6/graphique.egg-info/requires.txt +0 -8
- graphique-1.6/requirements.in +0 -2
- {graphique-1.6 → graphique-1.7}/LICENSE.txt +0 -0
- {graphique-1.6 → graphique-1.7}/graphique/middleware.py +0 -0
- {graphique-1.6 → graphique-1.7}/graphique/py.typed +0 -0
- {graphique-1.6 → graphique-1.7}/graphique/service.py +0 -0
- {graphique-1.6 → graphique-1.7}/graphique.egg-info/dependency_links.txt +0 -0
- {graphique-1.6 → graphique-1.7}/graphique.egg-info/top_level.txt +0 -0
- {graphique-1.6 → graphique-1.7}/setup.cfg +0 -0
- {graphique-1.6 → graphique-1.7}/tests/test_dataset.py +0 -0
- {graphique-1.6 → graphique-1.7}/tests/test_service.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: graphique
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.7
|
|
4
4
|
Summary: GraphQL service for arrow tables and parquet data sets.
|
|
5
5
|
Author-email: Aric Coady <aric.coady@gmail.com>
|
|
6
6
|
License: Copyright 2022 Aric Coady
|
|
@@ -40,8 +40,8 @@ Classifier: Typing :: Typed
|
|
|
40
40
|
Requires-Python: >=3.9
|
|
41
41
|
Description-Content-Type: text/markdown
|
|
42
42
|
License-File: LICENSE.txt
|
|
43
|
-
Requires-Dist: pyarrow>=
|
|
44
|
-
Requires-Dist: strawberry-graphql[asgi,cli]>=0.
|
|
43
|
+
Requires-Dist: pyarrow>=17
|
|
44
|
+
Requires-Dist: strawberry-graphql[asgi,cli]>=0.236
|
|
45
45
|
Provides-Extra: server
|
|
46
46
|
Requires-Dist: uvicorn[standard]; extra == "server"
|
|
47
47
|
Provides-Extra: cli
|
|
@@ -51,9 +51,10 @@ Requires-Dist: tqdm; extra == "cli"
|
|
|
51
51
|

|
|
52
52
|
[](https://pepy.tech/project/graphique)
|
|
53
53
|

|
|
54
|
-
[](https://github.com/coady/graphique/actions/workflows/build.yml)
|
|
55
55
|
[](https://codecov.io/gh/coady/graphique/)
|
|
56
|
-
[](https://github.com/coady/graphique/actions/workflows/github-code-scanning/codeql)
|
|
57
|
+
[](https://codspeed.io/coady/graphique)
|
|
57
58
|
[](https://github.com/astral-sh/ruff)
|
|
58
59
|
[](https://mypy-lang.org/)
|
|
59
60
|
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|

|
|
3
3
|
[](https://pepy.tech/project/graphique)
|
|
4
4
|

|
|
5
|
-
[](https://github.com/coady/graphique/actions/workflows/build.yml)
|
|
6
6
|
[](https://codecov.io/gh/coady/graphique/)
|
|
7
|
-
[](https://github.com/coady/graphique/actions/workflows/github-code-scanning/codeql)
|
|
8
|
+
[](https://codspeed.io/coady/graphique)
|
|
8
9
|
[](https://github.com/astral-sh/ruff)
|
|
9
10
|
[](https://mypy-lang.org/)
|
|
10
11
|
|
|
@@ -12,7 +12,7 @@ import inspect
|
|
|
12
12
|
import itertools
|
|
13
13
|
import operator
|
|
14
14
|
import json
|
|
15
|
-
from collections.abc import Callable, Iterable, Iterator, Sequence
|
|
15
|
+
from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence
|
|
16
16
|
from dataclasses import dataclass
|
|
17
17
|
from typing import Optional, Union, get_type_hints
|
|
18
18
|
import numpy as np
|
|
@@ -419,14 +419,16 @@ class Table(pa.Table):
|
|
|
419
419
|
counts: alias for optional row counts
|
|
420
420
|
**funcs: aggregate funcs with columns options
|
|
421
421
|
"""
|
|
422
|
-
if isinstance(self, pa.Table):
|
|
423
|
-
self = self.unify_dictionaries()
|
|
424
422
|
prefix = 'hash_' if names else ''
|
|
425
423
|
aggs = [agg.astuple(prefix + func) for func in funcs for agg in funcs[func]]
|
|
426
424
|
columns = list(names) + [agg[0] for agg in aggs]
|
|
427
425
|
if counts:
|
|
428
426
|
aggs.append(([], 'hash_count_all', None, counts))
|
|
429
|
-
|
|
427
|
+
if isinstance(self, pa.Table):
|
|
428
|
+
decl = Declaration('table_source', self.unify_dictionaries())
|
|
429
|
+
else:
|
|
430
|
+
decl = Declaration.scan(self, columns=columns)
|
|
431
|
+
return decl.aggregate(aggs, names).to_table(use_threads=False)
|
|
430
432
|
|
|
431
433
|
def aggregate(self, counts: str = '', **funcs: Sequence[Agg]) -> dict:
|
|
432
434
|
"""Return aggregated scalars as a row of data."""
|
|
@@ -630,8 +632,8 @@ class Table(pa.Table):
|
|
|
630
632
|
return f'{size:n} {prefix}B'
|
|
631
633
|
|
|
632
634
|
|
|
633
|
-
class
|
|
634
|
-
"""[Acero](https://arrow.apache.org/docs/python/api/acero.html) engine
|
|
635
|
+
class Declaration(ac.Declaration):
|
|
636
|
+
"""[Acero](https://arrow.apache.org/docs/python/api/acero.html) engine declaration."""
|
|
635
637
|
|
|
636
638
|
option_map = {
|
|
637
639
|
'table_source': ac.TableSourceNodeOptions,
|
|
@@ -643,21 +645,24 @@ class Declarations(list):
|
|
|
643
645
|
'hashjoin': ac.HashJoinNodeOptions,
|
|
644
646
|
}
|
|
645
647
|
|
|
646
|
-
def __init__(self,
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
648
|
+
def __init__(self, name, *args, inputs=None, **options):
|
|
649
|
+
super().__init__(name, self.option_map[name](*args, **options), inputs)
|
|
650
|
+
|
|
651
|
+
@classmethod
|
|
652
|
+
def scan(cls, dataset: ds.Dataset, columns=None) -> Self:
|
|
653
|
+
"""Return source node from a dataset."""
|
|
654
|
+
expr = dataset._scan_options.get('filter')
|
|
655
|
+
self = cls('scan', dataset, filter=expr, columns=columns)
|
|
656
|
+
if expr is not None:
|
|
657
|
+
self = self.filter(expr)
|
|
658
|
+
if isinstance(columns, Mapping):
|
|
659
|
+
self = self.project(columns.values(), columns)
|
|
660
|
+
elif columns is not None:
|
|
661
|
+
self = self.project(map(pc.field, columns))
|
|
657
662
|
return self
|
|
658
663
|
|
|
664
|
+
def apply(self, name: str, *args, **options) -> Self:
|
|
665
|
+
return type(self)(name, *args, inputs=[self], **options)
|
|
666
|
+
|
|
659
667
|
def __getattr__(self, name: str) -> Callable:
|
|
660
668
|
return functools.partial(self.apply, name)
|
|
661
|
-
|
|
662
|
-
def to_table(self, use_threads: bool = False) -> pa.Table:
|
|
663
|
-
return ac.Declaration.from_sequence(self).to_table(use_threads)
|
|
@@ -15,9 +15,9 @@ import pyarrow.dataset as ds
|
|
|
15
15
|
import strawberry
|
|
16
16
|
from strawberry import UNSET
|
|
17
17
|
from strawberry.annotation import StrawberryAnnotation
|
|
18
|
-
from strawberry.arguments import StrawberryArgument
|
|
18
|
+
from strawberry.types.arguments import StrawberryArgument
|
|
19
19
|
from strawberry.schema_directive import Location
|
|
20
|
-
from strawberry.field import StrawberryField
|
|
20
|
+
from strawberry.types.field import StrawberryField
|
|
21
21
|
from strawberry.scalars import JSON
|
|
22
22
|
from typing_extensions import Self
|
|
23
23
|
from .core import Agg
|
|
@@ -91,11 +91,11 @@ class Dataset:
|
|
|
91
91
|
|
|
92
92
|
@classmethod
|
|
93
93
|
@no_type_check
|
|
94
|
-
def resolve_reference(cls, info, **keys) -> Self:
|
|
94
|
+
def resolve_reference(cls, info: Info, **keys) -> Self:
|
|
95
95
|
"""Return table from federated keys."""
|
|
96
96
|
self = getattr(info.root_value, cls.field)
|
|
97
97
|
queries = {name: Filter(eq=[keys[name]]) for name in keys}
|
|
98
|
-
return self.filter(
|
|
98
|
+
return self.filter(info, **queries)
|
|
99
99
|
|
|
100
100
|
def columns(self, info: Info) -> dict:
|
|
101
101
|
"""fields for each column"""
|
|
@@ -14,7 +14,7 @@ import pyarrow as pa
|
|
|
14
14
|
import pyarrow.compute as pc
|
|
15
15
|
import strawberry
|
|
16
16
|
from strawberry import Info
|
|
17
|
-
from strawberry.field import StrawberryField
|
|
17
|
+
from strawberry.types.field import StrawberryField
|
|
18
18
|
from .core import Column as C
|
|
19
19
|
from .inputs import links
|
|
20
20
|
from .scalars import Long, scalar_map, type_map
|
|
@@ -24,35 +24,41 @@ def parse_duration(value: str):
|
|
|
24
24
|
parts = re.split(r'(-?\d+\.?\d*)', d_val.lower() + t_val)
|
|
25
25
|
if parts.pop(0) != 'p':
|
|
26
26
|
raise ValueError("Duration format must start with `P`")
|
|
27
|
+
multipliers = {'y': 12, 'w': 7, 'H': 3600, 'M': 60}
|
|
27
28
|
for num, key in zip(parts[::2], parts[1::2]):
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if
|
|
38
|
-
return
|
|
39
|
-
return
|
|
29
|
+
value = (float if '.' in num else int)(num) * multipliers.get(key, 1)
|
|
30
|
+
if key in 'ym':
|
|
31
|
+
months += value
|
|
32
|
+
elif key in 'wd':
|
|
33
|
+
days += value
|
|
34
|
+
elif key in 'HMS':
|
|
35
|
+
seconds += value
|
|
36
|
+
else:
|
|
37
|
+
raise ValueError(f"Invalid duration field: {key.upper()}")
|
|
38
|
+
if set(d_val).isdisjoint('YM'):
|
|
39
|
+
return timedelta(days, seconds)
|
|
40
|
+
return pa.MonthDayNano([months, days, int(seconds * 1_000_000_000)])
|
|
40
41
|
|
|
41
42
|
|
|
42
43
|
@functools.singledispatch
|
|
43
|
-
def duration_isoformat(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
def duration_isoformat(months: int, days: int, seconds: int, fraction: str = '.') -> str:
|
|
45
|
+
minutes, seconds = divmod(seconds, 60)
|
|
46
|
+
items = zip('YMDHM', divmod(months, 12) + (days,) + divmod(minutes, 60))
|
|
47
|
+
year, month, day, hour, minute = (f'{value}{key}' if value else '' for key, value in items)
|
|
48
|
+
fraction = fraction.rstrip('0').rstrip('.')
|
|
49
|
+
return f'P{year}{month}{day}T{hour}{minute}{seconds}{fraction}S'
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@duration_isoformat.register
|
|
53
|
+
def _(td: timedelta) -> str: # type: ignore
|
|
54
|
+
return duration_isoformat(0, td.days, td.seconds, f'.{td.microseconds:06}')
|
|
47
55
|
|
|
48
56
|
|
|
49
57
|
@duration_isoformat.register
|
|
50
58
|
def _(mdn: pa.MonthDayNano) -> str:
|
|
51
|
-
months = f'{mdn.months}M' if mdn.months else ''
|
|
52
|
-
days = f'{mdn.days}D' if mdn.days else ''
|
|
53
59
|
seconds, nanoseconds = divmod(mdn.nanoseconds, 1_000_000_000)
|
|
54
|
-
|
|
55
|
-
return
|
|
60
|
+
value = duration_isoformat(mdn.months, mdn.days, seconds, f'.{nanoseconds:09}')
|
|
61
|
+
return value if mdn.months else value.replace('P', 'P0M')
|
|
56
62
|
|
|
57
63
|
|
|
58
64
|
Long = strawberry.scalar(int, name='Long', description="64-bit int", parse_value=parse_long)
|
|
@@ -12,6 +12,8 @@ progress on the second pass.
|
|
|
12
12
|
import shutil
|
|
13
13
|
from pathlib import Path
|
|
14
14
|
from typing import Annotated
|
|
15
|
+
import numpy as np
|
|
16
|
+
import pyarrow.compute as pc
|
|
15
17
|
import pyarrow.dataset as ds
|
|
16
18
|
import typer # type: ignore
|
|
17
19
|
from tqdm import tqdm # type: ignore
|
|
@@ -22,12 +24,19 @@ def sort_key(name: str) -> tuple:
|
|
|
22
24
|
return name.lstrip('-'), ('descending' if name.startswith('-') else 'ascending')
|
|
23
25
|
|
|
24
26
|
|
|
25
|
-
def write_batches(
|
|
26
|
-
|
|
27
|
+
def write_batches(
|
|
28
|
+
scanner: ds.Scanner, base_dir: str, *partitioning: str, indices: str = '', **options
|
|
29
|
+
):
|
|
30
|
+
"""Partition dataset by batches.
|
|
31
|
+
|
|
32
|
+
Optionally include original indices.
|
|
33
|
+
"""
|
|
27
34
|
options.update(format='parquet', partitioning=partitioning)
|
|
28
35
|
options.update(partitioning_flavor='hive', existing_data_behavior='overwrite_or_ignore')
|
|
29
36
|
with tqdm(total=scanner.count_rows(), desc="Batches") as pbar:
|
|
30
37
|
for index, batch in enumerate(scanner.to_batches()):
|
|
38
|
+
if indices:
|
|
39
|
+
batch = batch.append_column(indices, pc.add(np.arange(len(batch)), pbar.n))
|
|
31
40
|
options['basename_template'] = f'part-{index}-{{i}}.parquet'
|
|
32
41
|
ds.write_dataset(batch, base_dir, **options)
|
|
33
42
|
pbar.update(len(batch))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: graphique
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.7
|
|
4
4
|
Summary: GraphQL service for arrow tables and parquet data sets.
|
|
5
5
|
Author-email: Aric Coady <aric.coady@gmail.com>
|
|
6
6
|
License: Copyright 2022 Aric Coady
|
|
@@ -40,8 +40,8 @@ Classifier: Typing :: Typed
|
|
|
40
40
|
Requires-Python: >=3.9
|
|
41
41
|
Description-Content-Type: text/markdown
|
|
42
42
|
License-File: LICENSE.txt
|
|
43
|
-
Requires-Dist: pyarrow>=
|
|
44
|
-
Requires-Dist: strawberry-graphql[asgi,cli]>=0.
|
|
43
|
+
Requires-Dist: pyarrow>=17
|
|
44
|
+
Requires-Dist: strawberry-graphql[asgi,cli]>=0.236
|
|
45
45
|
Provides-Extra: server
|
|
46
46
|
Requires-Dist: uvicorn[standard]; extra == "server"
|
|
47
47
|
Provides-Extra: cli
|
|
@@ -51,9 +51,10 @@ Requires-Dist: tqdm; extra == "cli"
|
|
|
51
51
|

|
|
52
52
|
[](https://pepy.tech/project/graphique)
|
|
53
53
|

|
|
54
|
-
[](https://github.com/coady/graphique/actions/workflows/build.yml)
|
|
55
55
|
[](https://codecov.io/gh/coady/graphique/)
|
|
56
|
-
[](https://github.com/coady/graphique/actions/workflows/github-code-scanning/codeql)
|
|
57
|
+
[](https://codspeed.io/coady/graphique)
|
|
57
58
|
[](https://github.com/astral-sh/ruff)
|
|
58
59
|
[](https://mypy-lang.org/)
|
|
59
60
|
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["setuptools>=61"
|
|
2
|
+
requires = ["setuptools>=61"]
|
|
3
3
|
build-backend = "setuptools.build_meta"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "graphique"
|
|
7
|
-
|
|
7
|
+
version = "1.7"
|
|
8
|
+
dynamic = ["dependencies"]
|
|
8
9
|
description = "GraphQL service for arrow tables and parquet data sets."
|
|
9
10
|
readme = "README.md"
|
|
10
11
|
requires-python = ">=3.9"
|
|
@@ -39,22 +40,14 @@ Issues = "https://github.com/coady/graphique/issues"
|
|
|
39
40
|
server = ["uvicorn[standard]"]
|
|
40
41
|
cli = ["tqdm"]
|
|
41
42
|
|
|
42
|
-
[tool.setuptools]
|
|
43
|
-
packages = ["graphique"]
|
|
44
|
-
|
|
45
43
|
[tool.setuptools.dynamic]
|
|
46
|
-
version = {attr = "graphique.__version__"}
|
|
47
44
|
dependencies = {file = "requirements.in"}
|
|
48
45
|
|
|
49
|
-
[tool.setuptools.package-data]
|
|
50
|
-
graphique = ["py.typed"]
|
|
51
|
-
|
|
52
46
|
[tool.ruff]
|
|
53
47
|
line-length = 100
|
|
54
48
|
extend-include = ["*.ipynb"]
|
|
55
49
|
|
|
56
50
|
[tool.ruff.format]
|
|
57
|
-
preview = true
|
|
58
51
|
quote-style = "preserve"
|
|
59
52
|
|
|
60
53
|
[[tool.mypy.overrides]]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from graphique.core import Table as T
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@pytest.mark.benchmark
|
|
6
|
+
def test_group(table):
|
|
7
|
+
T.group(table, 'state', 'county', 'city')
|
|
8
|
+
T.runs(table, 'state', 'county', 'city')
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.mark.benchmark
|
|
12
|
+
def test_rank(table):
|
|
13
|
+
T.ranked(table, 1, 'state', 'county', 'city')
|
|
14
|
+
T.ranked(table, 10, 'state', 'county', 'city')
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@pytest.mark.benchmark
|
|
18
|
+
def test_sort(table):
|
|
19
|
+
T.sort(table, 'state', 'county', 'city', length=1)
|
|
20
|
+
T.sort(table, 'state', 'county', 'city', length=10)
|
|
21
|
+
T.sort(table, 'state', 'county', 'city')
|
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
import pyarrow as pa
|
|
2
2
|
import pyarrow.compute as pc
|
|
3
|
+
import pyarrow.dataset as ds
|
|
3
4
|
import pytest
|
|
4
|
-
from graphique.core import Agg, ListChunk, Column as C, Table as T
|
|
5
|
+
from graphique.core import Agg, Declaration, ListChunk, Column as C, Table as T
|
|
5
6
|
from graphique.scalars import parse_duration, duration_isoformat
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
def test_duration():
|
|
9
|
-
assert duration_isoformat(parse_duration('P1Y1M1DT1H1M1.1S')) == '
|
|
10
|
-
assert duration_isoformat(parse_duration('P1M1DT1H1M1.1S')) == '
|
|
11
|
-
assert duration_isoformat(parse_duration('P1DT1H1M1.1S')) == '
|
|
12
|
-
assert duration_isoformat(parse_duration('PT1H1M1.1S')) == '
|
|
13
|
-
assert duration_isoformat(parse_duration('PT1M1.1S')) == '
|
|
14
|
-
assert duration_isoformat(parse_duration('PT1.1S')) == 'PT1.
|
|
10
|
+
assert duration_isoformat(parse_duration('P1Y1M1DT1H1M1.1S')) == 'P1Y1M1DT1H1M1.1S'
|
|
11
|
+
assert duration_isoformat(parse_duration('P1M1DT1H1M1.1S')) == 'P1M1DT1H1M1.1S'
|
|
12
|
+
assert duration_isoformat(parse_duration('P1DT1H1M1.1S')) == 'P1DT1H1M1.1S'
|
|
13
|
+
assert duration_isoformat(parse_duration('PT1H1M1.1S')) == 'PT1H1M1.1S'
|
|
14
|
+
assert duration_isoformat(parse_duration('PT1M1.1S')) == 'PT1M1.1S'
|
|
15
|
+
assert duration_isoformat(parse_duration('PT1.1S')) == 'PT1.1S'
|
|
15
16
|
assert duration_isoformat(parse_duration('PT1S')) == 'PT1S'
|
|
16
17
|
assert duration_isoformat(parse_duration('P0D')) == 'PT0S'
|
|
17
18
|
assert duration_isoformat(parse_duration('PT0S')) == 'PT0S'
|
|
18
|
-
assert duration_isoformat(parse_duration('P-1DT-1H')) == 'P-
|
|
19
|
+
assert duration_isoformat(parse_duration('P-1DT-1H')) == 'P-2DT23H0S'
|
|
20
|
+
assert duration_isoformat(parse_duration('P0MT')) == 'P0MT0S'
|
|
21
|
+
assert duration_isoformat(parse_duration('P0YT')) == 'P0MT0S'
|
|
19
22
|
with pytest.raises(ValueError):
|
|
20
23
|
duration_isoformat(parse_duration('T1H'))
|
|
21
24
|
with pytest.raises(ValueError):
|
|
@@ -87,6 +90,13 @@ def test_membership():
|
|
|
87
90
|
assert C.index(array, 1, start=2) == -1
|
|
88
91
|
|
|
89
92
|
|
|
93
|
+
def test_declaration(table):
|
|
94
|
+
dataset = ds.dataset(table).filter(pc.field('state') == 'CA')
|
|
95
|
+
assert Declaration.scan(dataset).to_table()['state'].unique().to_pylist() == ['CA']
|
|
96
|
+
(column,) = Declaration.scan(dataset, columns={'_': pc.field('state')}).to_table()
|
|
97
|
+
assert column.unique().to_pylist() == ['CA']
|
|
98
|
+
|
|
99
|
+
|
|
90
100
|
def test_group(table):
|
|
91
101
|
groups = T.group(table, 'state', list=[Agg('county'), Agg('city')])
|
|
92
102
|
assert len(groups) == 52
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import sys
|
|
2
1
|
import pytest
|
|
3
2
|
|
|
4
3
|
|
|
@@ -167,7 +166,6 @@ def test_numeric(executor):
|
|
|
167
166
|
assert data == {'scan': {'columns': {'int64': {'values': [0, None]}}}}
|
|
168
167
|
|
|
169
168
|
|
|
170
|
-
@pytest.mark.skipif(sys.version_info < (3, 9), reason="requires python3.9 or pytz")
|
|
171
169
|
def test_datetime(executor):
|
|
172
170
|
for name in ('timestamp', 'date32'):
|
|
173
171
|
data = executor(
|
|
@@ -230,7 +228,7 @@ def test_duration(executor):
|
|
|
230
228
|
data = executor("""{ scan(columns: {alias: "diff", temporal:
|
|
231
229
|
{monthDayNanoIntervalBetween: [{name: "timestamp"}, {name: "timestamp"}]}})
|
|
232
230
|
{ column(name: "diff") { ... on DurationColumn { values } } } }""")
|
|
233
|
-
assert data == {'scan': {'column': {'values': ['
|
|
231
|
+
assert data == {'scan': {'column': {'values': ['P0MT0S', None]}}}
|
|
234
232
|
|
|
235
233
|
|
|
236
234
|
def test_list(executor):
|
graphique-1.6/requirements.in
DELETED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|