iker-python-common 1.0.15__tar.gz → 1.0.17__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 (70) hide show
  1. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/PKG-INFO +1 -1
  2. iker_python_common-1.0.17/resources/unittest/csv/data.csv +7 -0
  3. iker_python_common-1.0.17/resources/unittest/csv/data.tsv +7 -0
  4. iker_python_common-1.0.17/src/iker/common/utils/csv.py +99 -0
  5. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/src/iker/common/utils/randutils.py +20 -0
  6. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/src/iker_python_common.egg-info/PKG-INFO +1 -1
  7. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/src/iker_python_common.egg-info/SOURCES.txt +4 -0
  8. iker_python_common-1.0.17/test/iker_tests/common/utils/csv_test.py +241 -0
  9. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/test/iker_tests/common/utils/randutils_test.py +37 -0
  10. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/.editorconfig +0 -0
  11. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/.github/workflows/pr.yml +0 -0
  12. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/.github/workflows/push.yml +0 -0
  13. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/.gitignore +0 -0
  14. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/MANIFEST.in +0 -0
  15. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/README.md +0 -0
  16. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/VERSION +0 -0
  17. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/pyproject.toml +0 -0
  18. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/resources/unittest/shutils/dir.baz/file.bar.baz +0 -0
  19. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/resources/unittest/shutils/dir.baz/file.foo.bar +0 -0
  20. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/resources/unittest/shutils/dir.baz/file.foo.baz +0 -0
  21. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/resources/unittest/shutils/dir.foo/dir.foo.bar/dir.foo.bar.baz/file.foo.bar.baz +0 -0
  22. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/resources/unittest/shutils/dir.foo/dir.foo.bar/file.bar.baz +0 -0
  23. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/resources/unittest/shutils/dir.foo/dir.foo.bar/file.foo.bar +0 -0
  24. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/resources/unittest/shutils/dir.foo/dir.foo.bar/file.foo.baz +0 -0
  25. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/resources/unittest/shutils/dir.foo/file.bar +0 -0
  26. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/resources/unittest/shutils/dir.foo/file.baz +0 -0
  27. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/resources/unittest/shutils/dir.foo/file.foo +0 -0
  28. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/setup.cfg +0 -0
  29. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/setup.py +0 -0
  30. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/src/iker/common/__init__.py +0 -0
  31. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/src/iker/common/utils/__init__.py +0 -0
  32. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/src/iker/common/utils/argutils.py +0 -0
  33. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/src/iker/common/utils/config.py +0 -0
  34. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/src/iker/common/utils/dbutils.py +0 -0
  35. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/src/iker/common/utils/dockerutils.py +0 -0
  36. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/src/iker/common/utils/dtutils.py +0 -0
  37. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/src/iker/common/utils/funcutils.py +0 -0
  38. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/src/iker/common/utils/jsonutils.py +0 -0
  39. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/src/iker/common/utils/logger.py +0 -0
  40. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/src/iker/common/utils/numutils.py +0 -0
  41. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/src/iker/common/utils/retry.py +0 -0
  42. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/src/iker/common/utils/s3utils.py +0 -0
  43. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/src/iker/common/utils/sequtils.py +0 -0
  44. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/src/iker/common/utils/shutils.py +0 -0
  45. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/src/iker/common/utils/span.py +0 -0
  46. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/src/iker/common/utils/strutils.py +0 -0
  47. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/src/iker/common/utils/testutils.py +0 -0
  48. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/src/iker_python_common.egg-info/dependency_links.txt +0 -0
  49. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/src/iker_python_common.egg-info/not-zip-safe +0 -0
  50. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/src/iker_python_common.egg-info/requires.txt +0 -0
  51. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/src/iker_python_common.egg-info/top_level.txt +0 -0
  52. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/test/iker_tests/__init__.py +0 -0
  53. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/test/iker_tests/common/utils/argutils_test.py +0 -0
  54. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/test/iker_tests/common/utils/config_test.py +0 -0
  55. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/test/iker_tests/common/utils/dbutils_test.py +0 -0
  56. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/test/iker_tests/common/utils/dockerutils_test.py +0 -0
  57. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/test/iker_tests/common/utils/dtutils_test.py +0 -0
  58. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/test/iker_tests/common/utils/funcutils_test.py +0 -0
  59. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/test/iker_tests/common/utils/jsonutils_test.py +0 -0
  60. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/test/iker_tests/common/utils/logger_test.py +0 -0
  61. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/test/iker_tests/common/utils/numutils_test.py +0 -0
  62. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/test/iker_tests/common/utils/retry_test.py +0 -0
  63. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/test/iker_tests/common/utils/s3utils_test.py +0 -0
  64. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/test/iker_tests/common/utils/sequtils_test.py +0 -0
  65. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/test/iker_tests/common/utils/shutils_test.py +0 -0
  66. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/test/iker_tests/common/utils/span_test.py +0 -0
  67. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/test/iker_tests/common/utils/strutils_test.py +0 -0
  68. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/test/iker_tests/common/utils/testutils_test.py +0 -0
  69. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/test/iker_tests/docker_fixtures.py +0 -0
  70. {iker_python_common-1.0.15 → iker_python_common-1.0.17}/test/iker_tests/iker_test.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: iker-python-common
3
- Version: 1.0.15
3
+ Version: 1.0.17
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3.11
6
6
  Classifier: Programming Language :: Python :: 3.12
@@ -0,0 +1,7 @@
1
+ dummy_str,dummy_bool,dummy_int,dummy_float,dummy_datetime,dummy_params
2
+ foo,True,1,1.0,2020-01-01T00:00:00,
3
+ bar,False,-1,-1.0,2020-01-01T00:00:00,key_1=value_1;key_2;!key_3
4
+ baz,\N,100,inf,2020-01-01T00:00:00,\N
5
+ ,\N,-100,-inf,2020-01-01T00:00:00,
6
+ \N,\N,0,nan,2020-01-01T00:00:00,\N
7
+ \N,\N,\N,\N,\N,\N
@@ -0,0 +1,7 @@
1
+ dummy_str dummy_bool dummy_int dummy_float dummy_datetime dummy_params
2
+ foo True 1 1.0 2020-01-01T00:00:00
3
+ bar False -1 -1.0 2020-01-01T00:00:00 key_1:value_1#key_2#-key_3
4
+ baz <null> 100 inf 2020-01-01T00:00:00 <null>
5
+ <null> -100 -inf 2020-01-01T00:00:00
6
+ <null> <null> 0 nan 2020-01-01T00:00:00 <null>
7
+ <null> <null> <null> <null> <null> <null>
@@ -0,0 +1,99 @@
1
+ import os
2
+ from collections.abc import Callable, Generator, Iterable, Mapping, Sequence
3
+ from typing import Any
4
+
5
+ __all__ = [
6
+ "CSVColumn",
7
+ "CSVView",
8
+ "column",
9
+ "view",
10
+ ]
11
+
12
+
13
+ class CSVColumn(object):
14
+ def __init__(
15
+ self,
16
+ name: str,
17
+ loader: Callable[[str], Any] = str,
18
+ dumper: Callable[[Any], str] = str,
19
+ null_str: str = "",
20
+ ):
21
+ self.name = name
22
+ self.loader = loader
23
+ self.dumper = dumper
24
+ self.null_str = null_str
25
+
26
+
27
+ class CSVView(object):
28
+ def __init__(self, schema: Sequence[CSVColumn], *, row_delim: str = "\n", col_delim: str = ","):
29
+ self.schema = schema
30
+ self.row_delim = row_delim
31
+ self.col_delim = col_delim
32
+
33
+ def load_lines(
34
+ self,
35
+ lines: Iterable[str],
36
+ has_header: bool = True,
37
+ ret_dict: bool = False,
38
+ ) -> Generator[list[Any] | dict[str, Any], None, None]:
39
+ rows_iter = iter(lines)
40
+ if has_header:
41
+ header_row = next(rows_iter)
42
+ header_cols = header_row.split(self.col_delim)
43
+ if len(self.schema) != len(header_cols):
44
+ raise ValueError("size of the schema is not identical to size of the columns")
45
+ for c, header_col in zip(self.schema, header_cols):
46
+ if c.name != header_col:
47
+ raise ValueError("name of the schema is not equal to the name of the columns")
48
+ for row in rows_iter:
49
+ cols = row.split(self.col_delim)
50
+ if len(self.schema) != len(cols):
51
+ continue
52
+ if ret_dict:
53
+ yield {c.name: None if col == c.null_str else c.loader(col) for c, col in zip(self.schema, cols)}
54
+ else:
55
+ yield [None if col == c.null_str else c.loader(col) for c, col in zip(self.schema, cols)]
56
+
57
+ def dump_lines(
58
+ self,
59
+ data: Iterable[Sequence[Any] | Mapping[str, Any]],
60
+ has_header: bool = True,
61
+ ) -> Generator[str, None, None]:
62
+ if has_header:
63
+ yield self.col_delim.join(c.name for c in self.schema)
64
+ for cols in data:
65
+ if isinstance(cols, Sequence):
66
+ if len(self.schema) != len(cols):
67
+ raise ValueError("size of the schema is not identical to size of the columns")
68
+ yield self.col_delim.join(c.null_str if col is None else c.dumper(col)
69
+ for c, col in zip(self.schema, cols))
70
+ if isinstance(cols, Mapping):
71
+ yield self.col_delim.join(c.null_str if cols.get(c.name) is None else c.dumper(cols.get(c.name))
72
+ for c in self.schema)
73
+
74
+ def load_file(
75
+ self,
76
+ file_path: os.PathLike | str,
77
+ has_header: bool = True,
78
+ ret_dict: bool = False,
79
+ **kwargs,
80
+ ) -> Generator[list[Any] | dict[str, Any], None, None]:
81
+ with open(file_path, mode="r", **kwargs) as fh:
82
+ lines = fh.read().split(self.row_delim)
83
+ yield from self.load_lines(lines, has_header, ret_dict)
84
+
85
+ def dump_file(
86
+ self,
87
+ data: Iterable[Sequence[Any] | Mapping[str, Any]],
88
+ file_path: os.PathLike | str,
89
+ has_header: bool = True,
90
+ **kwargs,
91
+ ) -> None:
92
+ with open(file_path, mode="w", **kwargs) as fh:
93
+ for line in self.dump_lines(data, has_header):
94
+ fh.write(line)
95
+ fh.write(self.row_delim)
96
+
97
+
98
+ column = CSVColumn
99
+ view = CSVView
@@ -3,9 +3,12 @@ import math
3
3
  import random
4
4
  import string
5
5
  import sys
6
+ from collections.abc import Callable, Sequence
7
+ from typing import TypeVar
6
8
 
7
9
  from iker.common.utils.dtutils import dt_utc_max, dt_utc_min
8
10
  from iker.common.utils.funcutils import memorized, singleton
11
+ from iker.common.utils.sequtils import head_or_none
9
12
 
10
13
  __all__ = [
11
14
  "max_int",
@@ -14,6 +17,8 @@ __all__ = [
14
17
  "randomizer",
15
18
  ]
16
19
 
20
+ T = TypeVar("T")
21
+
17
22
 
18
23
  @singleton
19
24
  def max_int() -> int:
@@ -142,6 +147,21 @@ class Randomizer(object):
142
147
 
143
148
  return generate_json_object(0)
144
149
 
150
+ def sample(self, population: Sequence[T], count_func: Callable[[T], int] = None, k: int = None) -> list[T] | T:
151
+ counts = list(map(count_func, population)) if callable(count_func) else None
152
+ result = self.random.sample(population, counts=counts, k=k or 1)
153
+ return result if k is not None else head_or_none(result)
154
+
155
+ def choose(self, population: Sequence[T], weight_func: Callable[[T], float] = None, k: int = None) -> list[T] | T:
156
+ weights = list(map(weight_func, population)) if callable(weight_func) else None
157
+ result = self.random.choices(population, weights=weights, k=k or 1)
158
+ return result if k is not None else head_or_none(result)
159
+
160
+ def shuffle(self, data: Sequence[T]) -> list[T]:
161
+ clone = list(item for item in data)
162
+ self.random.shuffle(clone)
163
+ return clone
164
+
145
165
 
146
166
  @memorized
147
167
  def randomizer(seed: int = 0) -> Randomizer:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: iker-python-common
3
- Version: 1.0.15
3
+ Version: 1.0.17
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3.11
6
6
  Classifier: Programming Language :: Python :: 3.12
@@ -7,6 +7,8 @@ pyproject.toml
7
7
  setup.py
8
8
  .github/workflows/pr.yml
9
9
  .github/workflows/push.yml
10
+ resources/unittest/csv/data.csv
11
+ resources/unittest/csv/data.tsv
10
12
  resources/unittest/shutils/dir.baz/file.bar.baz
11
13
  resources/unittest/shutils/dir.baz/file.foo.bar
12
14
  resources/unittest/shutils/dir.baz/file.foo.baz
@@ -21,6 +23,7 @@ src/iker/common/__init__.py
21
23
  src/iker/common/utils/__init__.py
22
24
  src/iker/common/utils/argutils.py
23
25
  src/iker/common/utils/config.py
26
+ src/iker/common/utils/csv.py
24
27
  src/iker/common/utils/dbutils.py
25
28
  src/iker/common/utils/dockerutils.py
26
29
  src/iker/common/utils/dtutils.py
@@ -47,6 +50,7 @@ test/iker_tests/docker_fixtures.py
47
50
  test/iker_tests/iker_test.py
48
51
  test/iker_tests/common/utils/argutils_test.py
49
52
  test/iker_tests/common/utils/config_test.py
53
+ test/iker_tests/common/utils/csv_test.py
50
54
  test/iker_tests/common/utils/dbutils_test.py
51
55
  test/iker_tests/common/utils/dockerutils_test.py
52
56
  test/iker_tests/common/utils/dtutils_test.py
@@ -0,0 +1,241 @@
1
+ import datetime
2
+ import itertools
3
+ import math
4
+ import os.path
5
+ import unittest
6
+
7
+ import ddt
8
+
9
+ from iker.common.utils import csv
10
+ from iker.common.utils.dtutils import dt_format_iso, dt_parse_iso
11
+ from iker.common.utils.jsonutils import json_compare
12
+ from iker.common.utils.strutils import make_params_string, parse_params_string
13
+ from iker.common.utils.strutils import parse_bool
14
+
15
+ from iker_tests import resources_directory
16
+
17
+
18
+ @ddt.ddt
19
+ class CSVTest(unittest.TestCase):
20
+
21
+ def test_lines(self):
22
+ view = csv.view(
23
+ [
24
+ csv.column("dummy_str", loader=str, dumper=str, null_str=r"\N"),
25
+ csv.column("dummy_bool", loader=parse_bool, dumper=str, null_str=r"\N"),
26
+ csv.column("dummy_int", loader=int, dumper=str, null_str=r"\N"),
27
+ csv.column("dummy_float", loader=float, dumper=str, null_str=r"\N"),
28
+ csv.column("dummy_datetime",
29
+ loader=lambda x: dt_parse_iso(x).timestamp(),
30
+ dumper=lambda x: dt_format_iso(datetime.datetime.fromtimestamp(x, tz=datetime.timezone.utc)),
31
+ null_str=r"\N"),
32
+ csv.column("dummy_params",
33
+ loader=lambda x: parse_params_string(x, delim=";", kv_delim="=", neg_prefix="!"),
34
+ dumper=lambda x: make_params_string(x, delim=";", kv_delim="=", neg_prefix="!"),
35
+ null_str=r"\N"),
36
+ ],
37
+ )
38
+
39
+ lines = [
40
+ r"dummy_str,dummy_bool,dummy_int,dummy_float,dummy_datetime,dummy_params",
41
+ r"foo,True,1,1.0,2020-01-01T00:00:00,",
42
+ r"bar,False,-1,-1.0,2020-01-01T00:00:00,key_1=value_1;key_2;!key_3",
43
+ r"baz,\N,100,inf,2020-01-01T00:00:00,\N",
44
+ r",\N,-100,-inf,2020-01-01T00:00:00,",
45
+ r"\N,\N,0,nan,2020-01-01T00:00:00,\N",
46
+ r"\N,\N,\N,\N,\N,\N",
47
+ ]
48
+
49
+ lines_no_header = [
50
+ r"foo,True,1,1.0,2020-01-01T00:00:00,",
51
+ r"bar,False,-1,-1.0,2020-01-01T00:00:00,key_1=value_1;key_2;!key_3",
52
+ r"baz,\N,100,inf,2020-01-01T00:00:00,\N",
53
+ r",\N,-100,-inf,2020-01-01T00:00:00,",
54
+ r"\N,\N,0,nan,2020-01-01T00:00:00,\N",
55
+ r"\N,\N,\N,\N,\N,\N",
56
+ ]
57
+
58
+ time = dt_parse_iso("2020-01-01T00:00:00").timestamp()
59
+
60
+ list_data = [
61
+ ["foo", True, 1, 1.0, time, {}],
62
+ ["bar", False, -1, -1.0, time, {"key_1": "value_1", "key_2": True, "key_3": False}],
63
+ ["baz", None, 100, math.inf, time, None],
64
+ ["", None, -100, -math.inf, time, {}],
65
+ [None, None, 0, math.nan, time, None],
66
+ [None, None, None, None, None, None],
67
+ ]
68
+
69
+ dict_data = [
70
+ {
71
+ "dummy_str": "foo",
72
+ "dummy_bool": True,
73
+ "dummy_int": 1,
74
+ "dummy_float": 1.0,
75
+ "dummy_datetime": time,
76
+ "dummy_params": {},
77
+ },
78
+ {
79
+ "dummy_str": "bar",
80
+ "dummy_bool": False,
81
+ "dummy_int": -1,
82
+ "dummy_float": -1.0,
83
+ "dummy_datetime": time,
84
+ "dummy_params": {"key_1": "value_1", "key_2": True, "key_3": False},
85
+ },
86
+ {
87
+ "dummy_str": "baz",
88
+ "dummy_bool": None,
89
+ "dummy_int": 100,
90
+ "dummy_float": math.inf,
91
+ "dummy_datetime": time,
92
+ "dummy_params": None,
93
+ },
94
+ {
95
+ "dummy_str": "",
96
+ "dummy_bool": None,
97
+ "dummy_int": -100,
98
+ "dummy_float": -math.inf,
99
+ "dummy_datetime": time,
100
+ "dummy_params": {},
101
+ },
102
+ {
103
+ "dummy_str": None,
104
+ "dummy_bool": None,
105
+ "dummy_int": 0,
106
+ "dummy_float": math.nan,
107
+ "dummy_datetime": time,
108
+ "dummy_params": None,
109
+ },
110
+ {
111
+ "dummy_str": None,
112
+ "dummy_bool": None,
113
+ "dummy_int": None,
114
+ "dummy_float": None,
115
+ "dummy_datetime": None,
116
+ "dummy_params": None,
117
+ },
118
+ ]
119
+
120
+ for a, e in zip(view.load_lines(lines, has_header=True), list_data):
121
+ self.assertTrue(json_compare(a, e))
122
+
123
+ for a, e in zip(view.load_lines(lines, has_header=True, ret_dict=True), dict_data):
124
+ self.assertTrue(json_compare(a, e))
125
+
126
+ for a, e in zip(view.dump_lines(list_data, has_header=True), lines):
127
+ self.assertTrue(json_compare(a, e))
128
+
129
+ for a, e in zip(view.dump_lines(dict_data, has_header=True), lines):
130
+ self.assertTrue(json_compare(a, e))
131
+
132
+ for a, e in zip(view.load_lines(lines_no_header, has_header=False), list_data):
133
+ self.assertTrue(json_compare(a, e))
134
+
135
+ for a, e in zip(view.load_lines(lines_no_header, has_header=False, ret_dict=True), dict_data):
136
+ self.assertTrue(json_compare(a, e))
137
+
138
+ for a, e in zip(view.dump_lines(list_data, has_header=False), lines_no_header):
139
+ self.assertTrue(json_compare(a, e))
140
+
141
+ for a, e in zip(view.dump_lines(dict_data, has_header=False), lines_no_header):
142
+ self.assertTrue(json_compare(a, e))
143
+
144
+ for a, e in zip(view.dump_lines(view.load_lines(lines, has_header=True, ret_dict=True), has_header=True),
145
+ lines):
146
+ self.assertTrue(json_compare(a, e))
147
+
148
+ for a, e in zip(view.load_lines(view.dump_lines(list_data, has_header=True), has_header=True, ret_dict=True),
149
+ dict_data):
150
+ self.assertTrue(json_compare(a, e))
151
+
152
+ for a, e in zip(view.dump_lines(view.load_lines(lines_no_header, has_header=False, ret_dict=True),
153
+ has_header=False),
154
+ lines_no_header):
155
+ self.assertTrue(json_compare(a, e))
156
+
157
+ for a, e in zip(view.load_lines(view.dump_lines(list_data, has_header=False), has_header=False, ret_dict=True),
158
+ dict_data):
159
+ self.assertTrue(json_compare(a, e))
160
+
161
+ for a, e in zip(itertools.chain(view.load_lines(lines, has_header=True),
162
+ view.load_lines(lines, has_header=True, ret_dict=True)),
163
+ list_data + dict_data):
164
+ self.assertTrue(json_compare(a, e))
165
+
166
+ for a, e in zip(itertools.chain(view.dump_lines(list_data, has_header=True),
167
+ view.dump_lines(dict_data, has_header=True)),
168
+ lines + lines):
169
+ self.assertTrue(json_compare(a, e))
170
+
171
+ for a, e in zip(itertools.chain(view.load_lines(lines, has_header=True),
172
+ view.load_lines(lines, has_header=True, ret_dict=True),
173
+ view.load_lines(lines_no_header, has_header=False),
174
+ view.load_lines(lines_no_header, has_header=False, ret_dict=True)),
175
+ list_data + dict_data + list_data + dict_data):
176
+ self.assertTrue(json_compare(a, e))
177
+
178
+ for a, e in zip(itertools.chain(view.dump_lines(list_data, has_header=True),
179
+ view.dump_lines(dict_data, has_header=True),
180
+ view.dump_lines(list_data, has_header=False),
181
+ view.dump_lines(dict_data, has_header=False)),
182
+ lines + lines + lines_no_header + lines_no_header):
183
+ self.assertTrue(json_compare(a, e))
184
+
185
+ def test_file(self):
186
+ view_csv = csv.view(
187
+ [
188
+ csv.column("dummy_str", loader=str, dumper=str, null_str=r"\N"),
189
+ csv.column("dummy_bool", loader=parse_bool, dumper=str, null_str=r"\N"),
190
+ csv.column("dummy_int", loader=int, dumper=str, null_str=r"\N"),
191
+ csv.column("dummy_float", loader=float, dumper=str, null_str=r"\N"),
192
+ csv.column("dummy_datetime",
193
+ loader=lambda x: dt_parse_iso(x).timestamp(),
194
+ dumper=lambda x: dt_format_iso(datetime.datetime.fromtimestamp(x, tz=datetime.timezone.utc)),
195
+ null_str=r"\N"),
196
+ csv.column("dummy_params",
197
+ loader=lambda x: parse_params_string(x, delim=";", kv_delim="=", neg_prefix="!"),
198
+ dumper=lambda x: make_params_string(x, delim=";", kv_delim="=", neg_prefix="!"),
199
+ null_str=r"\N"),
200
+ ],
201
+ )
202
+
203
+ view_tsv = csv.view(
204
+ [
205
+ csv.column("dummy_str", loader=str, dumper=str, null_str=r"<null>"),
206
+ csv.column("dummy_bool", loader=parse_bool, dumper=str, null_str=r"<null>"),
207
+ csv.column("dummy_int", loader=int, dumper=str, null_str=r"<null>"),
208
+ csv.column("dummy_float", loader=float, dumper=str, null_str=r"<null>"),
209
+ csv.column("dummy_datetime",
210
+ loader=lambda x: dt_parse_iso(x).timestamp(),
211
+ dumper=lambda x: dt_format_iso(datetime.datetime.fromtimestamp(x, tz=datetime.timezone.utc)),
212
+ null_str=r"<null>"),
213
+ csv.column("dummy_params",
214
+ loader=lambda x: parse_params_string(x, delim="#", kv_delim=":", neg_prefix="-"),
215
+ dumper=lambda x: make_params_string(x, delim="#", kv_delim=":", neg_prefix="-"),
216
+ null_str=r"<null>"),
217
+ ],
218
+ col_delim="\t",
219
+ )
220
+
221
+ time = dt_parse_iso("2020-01-01T00:00:00").timestamp()
222
+
223
+ data = [
224
+ ["foo", True, 1, 1.0, time, {}],
225
+ ["bar", False, -1, -1.0, time, {"key_1": "value_1", "key_2": True, "key_3": False}],
226
+ ["baz", None, 100, math.inf, time, None],
227
+ ["", None, -100, -math.inf, time, {}],
228
+ [None, None, 0, math.nan, time, None],
229
+ [None, None, None, None, None, None],
230
+ ]
231
+
232
+ for c, t, d in zip(view_csv.load_file(os.path.join(resources_directory, "unittest/csv/data.csv"),
233
+ has_header=True,
234
+ encoding="utf-8"),
235
+ view_tsv.load_file(os.path.join(resources_directory, "unittest/csv/data.tsv"),
236
+ has_header=True,
237
+ encoding="utf-8"),
238
+ data):
239
+ self.assertTrue(json_compare(c, d))
240
+ self.assertTrue(json_compare(t, d))
241
+ self.assertTrue(json_compare(c, t))
@@ -1,3 +1,4 @@
1
+ import itertools
1
2
  import unittest
2
3
 
3
4
  import ddt
@@ -130,3 +131,39 @@ class RandUtilsTest(unittest.TestCase):
130
131
  result = randomizer().random_time(begin, end)
131
132
 
132
133
  self.assertTrue(dt_utc_min().timetz() <= begin <= result <= end <= dt_utc_max().timetz())
134
+
135
+ def test_sample(self):
136
+ for _ in range(1000):
137
+ population = list(itertools.accumulate(randomizer().next_int(1, 10)
138
+ for _ in range(randomizer().next_int(100, 200))))
139
+ count_func = lambda x: 1 + x % 3
140
+ k = randomizer().next_int(1, len(population))
141
+
142
+ result = randomizer().sample(population, count_func, k)
143
+
144
+ self.assertTrue(len(result), k)
145
+ self.assertTrue(all(elem in population for elem in result))
146
+ self.assertTrue(all(result.count(elem) <= count_func(elem) for elem in result))
147
+
148
+ def test_choose(self):
149
+ for _ in range(1000):
150
+ population = list(itertools.accumulate(randomizer().next_int(1, 10)
151
+ for _ in range(randomizer().next_int(100, 200))))
152
+ weight_func = lambda x: float(1 + x % 3)
153
+ k = randomizer().next_int(1, len(population))
154
+
155
+ result = randomizer().choose(population, weight_func, k)
156
+
157
+ self.assertTrue(len(result), k)
158
+ self.assertTrue(all(elem in population for elem in result))
159
+
160
+ def test_shuffle(self):
161
+ for _ in range(1000):
162
+ data = list(itertools.accumulate(randomizer().next_int(1, 10)
163
+ for _ in range(randomizer().next_int(100, 200))))
164
+
165
+ result = randomizer().shuffle(data)
166
+
167
+ self.assertIsNot(result, data)
168
+ self.assertTrue(len(result), len(data))
169
+ self.assertTrue(all(elem in data for elem in result))