spinta 0.2.dev22__py3-none-any.whl → 0.2.dev23__py3-none-any.whl

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.
spinta/cli/admin.py CHANGED
@@ -2,15 +2,14 @@ from __future__ import annotations
2
2
 
3
3
  import logging
4
4
  import pathlib
5
- import sys
6
5
  from typing import Optional, List
7
6
 
8
7
  from typer import Context as TyperContext, Argument
9
8
  from typer import Option
10
- from typer import echo
11
9
 
12
10
  from spinta.cli.helpers.admin.components import ADMIN_SCRIPT_TYPE
13
11
  from spinta.cli.helpers.admin.registry import admin_script_registry
12
+ from spinta.cli.helpers.message import cli_error
14
13
  from spinta.cli.helpers.script.components import ScriptStatusCache
15
14
  from spinta.cli.helpers.script.core import run_specific_script
16
15
  from spinta.cli.helpers.script.helpers import sort_scripts_by_required
@@ -48,26 +47,30 @@ def admin(
48
47
  False,
49
48
  "-c",
50
49
  "--check",
51
- help=("Only runs script checks, skipping execution part (used to find out what scripts are needed to run)."),
50
+ help="Only runs script checks, skipping execution part (used to find out what scripts are needed to run).",
52
51
  ),
53
52
  input_path: Optional[pathlib.Path] = Option(
54
53
  None,
55
54
  "-i",
56
55
  "--input",
57
- help=("Path to input file (some scripts might require extra data). If not given, script will read from stdin."),
56
+ help="Path to input file (some scripts might require extra data). If not given, script will read from stdin.",
57
+ ),
58
+ output_path: Optional[pathlib.Path] = Option(
59
+ None,
60
+ "-o",
61
+ "--output",
62
+ help="Path to output file (some scripts might write extra data). If not given, script will write to stdout.",
58
63
  ),
59
64
  ):
60
65
  context = configure_context(ctx.obj)
61
66
 
62
67
  if force and check_only:
63
- echo("Cannot run force mode with check only mode", err=True)
64
- sys.exit(1)
68
+ cli_error("Cannot run force mode with check only mode")
65
69
 
66
70
  load_config(context, ensure_config_dir=ensure_config_dir)
67
71
 
68
72
  if not scripts:
69
- echo("At least one script needs to be specified", err=True)
70
- sys.exit(1)
73
+ cli_error("At least one script needs to be specified")
71
74
 
72
75
  script_objects = {}
73
76
  for script in scripts:
@@ -86,5 +89,6 @@ def admin(
86
89
  script_name=script,
87
90
  check_only=check_only,
88
91
  input_path=input_path,
92
+ output_path=output_path,
89
93
  status_cache=status_cache,
90
94
  )
@@ -15,3 +15,4 @@ class AdminScript(ScriptBase):
15
15
  class Script(enum.Enum):
16
16
  DEDUPLICATE = "deduplicate"
17
17
  CHANGELOG = "changelog"
18
+ ENUM_LIST = "enum_list"
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  from spinta.cli.helpers.admin.components import AdminScript, Script, ADMIN_SCRIPT_TYPE
4
4
  from spinta.cli.helpers.admin.scripts.changelog import migrate_changelog_duplicates, cli_requires_changelog_migrations
5
5
  from spinta.cli.helpers.admin.scripts.deduplicate import migrate_duplicates, cli_requires_deduplicate_migrations
6
+ from spinta.cli.helpers.admin.scripts.enums import gather_invalid_enum_values
6
7
  from spinta.cli.helpers.script.components import ScriptTarget, ScriptTag
7
8
  from spinta.cli.helpers.script.registry import script_registry
8
9
  from spinta.cli.helpers.upgrade.components import Script as UpgradeScript, UPGRADE_SCRIPT_TYPE
@@ -29,3 +30,6 @@ script_registry.register(
29
30
  targets={ScriptTarget.BACKEND.value},
30
31
  )
31
32
  )
33
+ script_registry.register(
34
+ AdminScript(name=Script.ENUM_LIST.value, run=gather_invalid_enum_values, targets={ScriptTarget.BACKEND.value})
35
+ )
@@ -0,0 +1,148 @@
1
+ import csv
2
+ import pathlib
3
+ import sys
4
+ from contextlib import nullcontext
5
+ from typing import Iterator
6
+
7
+ from multipledispatch import dispatch
8
+ from dataclasses import dataclass, field
9
+
10
+ from spinta import commands
11
+ from spinta.backends import Backend
12
+ from spinta.backends.postgresql.components import PostgreSQL
13
+ from spinta.cli.helpers.script.helpers import ensure_store_is_loaded
14
+ from spinta.components import Context, Model, Property
15
+ from spinta.core.ufuncs import Expr
16
+ from spinta.manifests.components import Manifest
17
+
18
+ import sqlalchemy as sa
19
+
20
+ from spinta.ufuncs.resultbuilder.components import EnumResultBuilder
21
+
22
+
23
+ @dispatch(Backend, Property)
24
+ def gather_unique_property_values() -> list:
25
+ raise NotImplementedError
26
+
27
+
28
+ @dispatch(PostgreSQL, Property)
29
+ def gather_unique_property_values(backend: PostgreSQL, prop: Property) -> list:
30
+ table = backend.get_table(prop)
31
+ column = backend.get_column(table, prop)
32
+ result = backend.engine.execute(sa.select(column).distinct()).scalars().all()
33
+ return result
34
+
35
+
36
+ @dispatch(Property)
37
+ def gather_unique_property_values(prop: Property) -> list:
38
+ return gather_unique_property_values(prop.dtype.backend, prop)
39
+
40
+
41
+ def get_models_with_enums(context: Context, manifest: Manifest) -> Iterator[Model]:
42
+ for model in commands.get_models(context, manifest).values():
43
+ for prop in model.flatprops.values():
44
+ if prop.enum:
45
+ yield model
46
+ break
47
+
48
+
49
+ @dataclass
50
+ class InvalidEnumProperty:
51
+ prop: Property
52
+ invalid_values: list = field(default_factory=list)
53
+
54
+ def add_invalid_value(self, value: object):
55
+ if value not in self.invalid_values:
56
+ self.invalid_values.append(value)
57
+
58
+
59
+ @dataclass
60
+ class InvalidEnumModel:
61
+ model: Model
62
+ enum_props: dict[str, InvalidEnumProperty] = field(default_factory=dict)
63
+
64
+ def add_invalid_value(self, prop: Property, value: object):
65
+ self.get_prop(prop).add_invalid_value(value)
66
+
67
+ def get_prop(self, prop: Property) -> InvalidEnumProperty:
68
+ if prop.place not in self.enum_props:
69
+ self.enum_props[prop.place] = InvalidEnumProperty(prop=prop)
70
+ return self.enum_props[prop.place]
71
+
72
+
73
+ def gather_invalid_enum_values(context: Context, output_path: pathlib.Path | None = None, **kwargs):
74
+ store = ensure_store_is_loaded(context)
75
+ manifest = store.manifest
76
+ invalid_models = {}
77
+ with context:
78
+ for model in get_models_with_enums(context, manifest):
79
+ enum_model = InvalidEnumModel(model=model)
80
+ for prop in model.flatprops.values():
81
+ if not prop.enum:
82
+ continue
83
+
84
+ enum_contains_expr = any(isinstance(enum.prepare, Expr) for enum in prop.enum.values())
85
+ values = gather_unique_property_values(prop)
86
+ for value in values:
87
+ if value is None and not prop.dtype.required:
88
+ continue
89
+ elif value is None:
90
+ enum_model.add_invalid_value(prop, value)
91
+ continue
92
+
93
+ if str(value) in prop.enum:
94
+ continue
95
+
96
+ check_value = value
97
+ if enum_contains_expr:
98
+ for enum in prop.enum.values():
99
+ env = EnumResultBuilder(context).init(value)
100
+ val = env.resolve(enum.prepare)
101
+ if env.has_value_changed:
102
+ check_value = val
103
+ break
104
+
105
+ if str(check_value) not in prop.enum:
106
+ enum_model.add_invalid_value(prop, value)
107
+
108
+ if enum_model.enum_props:
109
+ invalid_models[model.model_type()] = enum_model
110
+
111
+ output_invalid_enums_to_csv(invalid_models, output_path)
112
+
113
+
114
+ def output_invalid_enums_to_csv(invalid_models: dict, output_path: pathlib.Path | None = None):
115
+ stream_ctx = open(output_path, "w") if output_path else nullcontext(sys.stdout)
116
+ with stream_ctx as stream:
117
+ writer = csv.DictWriter(stream, fieldnames=["model", "property", "invalid_value"], lineterminator="\n")
118
+ writer.writeheader()
119
+ for model_key, model in sorted(invalid_models.items()):
120
+ model_written = False
121
+
122
+ for prop_name, prop in model.enum_props.items():
123
+ it = iter(prop.invalid_values)
124
+ first = next(it, None)
125
+ if first is None:
126
+ continue
127
+
128
+ model_name = ""
129
+ if not model_written:
130
+ model_name = model_key
131
+ model_written = True
132
+
133
+ writer.writerow(
134
+ {
135
+ "model": model_name,
136
+ "property": prop_name,
137
+ "invalid_value": first,
138
+ }
139
+ )
140
+
141
+ for value in it:
142
+ writer.writerow(
143
+ {
144
+ "model": "",
145
+ "property": "",
146
+ "invalid_value": value,
147
+ }
148
+ )
@@ -1,3 +1,5 @@
1
+ import sys
2
+
1
3
  import tqdm
2
4
  from click import echo
3
5
  from click.exceptions import Exit
@@ -9,7 +11,9 @@ def cli_error(message: str):
9
11
 
10
12
 
11
13
  def cli_message(message: str, progress_bar: tqdm.tqdm = None):
14
+ # https://pubs.opengroup.org/onlinepubs/9799919799/
15
+ # This documentation states that `stderr` should be used for diagnostic messages (in our case status)
12
16
  if progress_bar is not None:
13
- progress_bar.write(message)
17
+ progress_bar.write(message, file=sys.stderr)
14
18
  else:
15
- echo(message)
19
+ echo(message, err=True)
@@ -31,7 +31,7 @@ class _ScriptMeta(type):
31
31
 
32
32
  class ScriptBase(metaclass=_ScriptMeta):
33
33
  """
34
- Represents an script with optional preconditions and target constraints.
34
+ Represents a script with optional preconditions and target constraints.
35
35
 
36
36
  Targets and tags are mainly used to be able to filter specific scripts. Main use case would be to set target as
37
37
  specific object, like sqlalchemy keymap and tag as migration, so then you could filter all database migration
@@ -1,7 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from click import echo
4
-
3
+ from spinta.cli.helpers.message import cli_message
5
4
  from spinta.cli.helpers.script.components import ScriptStatus, ScriptBase, ScriptStatusCache
6
5
  from spinta.cli.helpers.script.helpers import sort_scripts_by_required, script_check_status_message
7
6
  from spinta.cli.helpers.script.registry import script_registry
@@ -54,7 +53,7 @@ def run_specific_script(
54
53
  if force:
55
54
  status = ScriptStatus.FORCED
56
55
 
57
- echo(script_check_status_message(script_name, status))
56
+ cli_message(script_check_status_message(script_name, status))
58
57
  if status in (ScriptStatus.FORCED, ScriptStatus.REQUIRED) and not check_only:
59
58
  script.run(context, destructive=destructive, **kwargs)
60
59
  if status_cache is not None:
@@ -73,7 +72,7 @@ def check_script(
73
72
  script = script.value
74
73
 
75
74
  if not script_registry.contains(script_type, script):
76
- echo(f"Warning: {script_type!r} script {script!r} was not found", err=True)
75
+ cli_message(f"Warning: {script_type!r} script {script!r} was not found")
77
76
  return ScriptStatus.SKIPPED
78
77
 
79
78
  script = script_registry.get(script_type, script)
@@ -92,9 +91,8 @@ def check_script(
92
91
  ScriptStatus.REQUIRED,
93
92
  ScriptStatus.SKIPPED,
94
93
  ):
95
- echo(
96
- f"Warning: {required_script_type!r} script {required_script!r} requirement is not met for {script.name!r} script",
97
- err=True,
94
+ cli_message(
95
+ f"Warning: {script_type!r} script {required_script!r} requirement is not met for {script.name!r} script",
98
96
  )
99
97
  return ScriptStatus.SKIPPED
100
98
 
@@ -2,8 +2,8 @@ from __future__ import annotations
2
2
 
3
3
  from collections import defaultdict, deque
4
4
 
5
- from typer import echo
6
5
 
6
+ from spinta.cli.helpers.message import cli_message
7
7
  from spinta.cli.helpers.script.components import ScriptStatus
8
8
  from spinta.cli.helpers.upgrade.components import UpgradeScript
9
9
  from spinta.components import Context, Store
@@ -44,7 +44,7 @@ def sort_scripts_by_required(scripts: dict[str, UpgradeScript]) -> dict:
44
44
 
45
45
  if len(result) != len(data):
46
46
  unresolved = set(data) - set(result)
47
- echo(f"Warning: Dependency cycle detected or unresolved dependencies in: {unresolved}", err=True)
47
+ cli_message(f"Warning: Dependency cycle detected or unresolved dependencies in: {unresolved}")
48
48
  # Extend results, potentially might cause errors, because of cycles
49
49
  result.extend(unresolved)
50
50
 
@@ -21,7 +21,9 @@ from spinta.typing import ObjectData
21
21
  from spinta.ufuncs.querybuilder.components import QueryParams
22
22
 
23
23
 
24
- def _get_data_soap(url: str, backend: Soap, soap_request_body: dict, extra_headers: dict) -> list[dict]:
24
+ def _get_data_soap(
25
+ url: str, backend: Soap, soap_request_body: dict, extra_headers: dict, source: str | None = None
26
+ ) -> list[dict]:
25
27
  for key, value in soap_request_body.items():
26
28
  if isinstance(value, MakeCDATA):
27
29
  soap_request_body[key] = value()
@@ -38,6 +40,15 @@ def _get_data_soap(url: str, backend: Soap, soap_request_body: dict, extra_heade
38
40
  except zeep.exceptions.Error as e:
39
41
  raise UnexpectedErrorReadingData(exception=type(e).__name__, message=str(e))
40
42
 
43
+ if isinstance(response_data, dict):
44
+ for part in source.split("/"):
45
+ if not part:
46
+ continue
47
+ if part in response_data:
48
+ response_data = response_data[part]
49
+ else:
50
+ response_data = []
51
+ break
41
52
  if response_data and not isinstance(response_data, list):
42
53
  response_data = [response_data]
43
54
 
@@ -137,6 +148,7 @@ def getall(
137
148
  backend=backend,
138
149
  soap_request_body=builder.soap_request_body,
139
150
  extra_headers=http_headers,
151
+ source=model.external.name,
140
152
  )
141
153
  .flatten()
142
154
  .to_dataframe(meta=meta)
spinta/testing/csv.py CHANGED
@@ -1,8 +1,13 @@
1
- from typing import List
1
+ import pathlib
2
2
 
3
3
  from requests.models import Response
4
4
 
5
5
 
6
- def parse_csv(resp: Response) -> List[List[str]]:
6
+ def parse_csv(resp: Response) -> list[list[str]]:
7
7
  resp.raise_for_status()
8
8
  return [line.strip().split(",") for line in resp.text.splitlines()]
9
+
10
+
11
+ def read_csv(path: pathlib.Path) -> list[list[str]]:
12
+ with path.open("r") as f:
13
+ return [line.strip().split(",") for line in f.readlines()]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spinta
3
- Version: 0.2.dev22
3
+ Version: 0.2.dev23
4
4
  Summary: A platform for describing, extracting, transforming, loading and serving open data.
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -173,18 +173,19 @@ spinta/backends/postgresql/ufuncs/result/ufuncs.py,sha256=n96VPXwU0vxS8lZu-8s_zK
173
173
  spinta/backends/postgresql/ufuncs/ufuncs.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
174
174
  spinta/backends/types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
175
175
  spinta/cli/__init__.py,sha256=3DWdLNWcfRk0mVZPwqKhQfRc47w716jhFjHLXSFTH-c,21
176
- spinta/cli/admin.py,sha256=HT_aEn4tqUXckKWUXPV8VEoMXe1ZW3strNGoEimCd9A,2902
176
+ spinta/cli/admin.py,sha256=FYFiMjWTcRGYZiS3GYcbF8h16XuYuq63VI9qToDGXP0,3123
177
177
  spinta/cli/auth.py,sha256=WDwn7p5JDwTq3xS9pki3xykmRrd1D7VxRkyv7nGjvIY,6036
178
178
  spinta/cli/config.py,sha256=U8WjKkHo9B0TczZuZLUhWmoCyh5YI8EW5loTqFJyGao,1736
179
179
  spinta/cli/data.py,sha256=nOlVj2GEZIvs0oRjTRibmlR0DlLh-GUs3nefZVrLS0c,8288
180
180
  spinta/cli/get.py,sha256=GUzj7hg3X0W06EIARveLL93vxKTWfVAUNvtQfmcxepA,1191
181
181
  spinta/cli/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
182
182
  spinta/cli/helpers/admin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
183
- spinta/cli/helpers/admin/components.py,sha256=4cE2vAJtqzm2w8ZPsyMAKMHYq3FBeugHms_ca1Yzu0U,307
184
- spinta/cli/helpers/admin/registry.py,sha256=yi_HJLpxd6KeyTmfLrN5ePmmTYZeHP1Wa-5BwCknw4o,1303
183
+ spinta/cli/helpers/admin/components.py,sha256=ir_CXA-r6GOdHx2nLT5Jiy-9vmxWj-6Ypz5xf27tbz0,335
184
+ spinta/cli/helpers/admin/registry.py,sha256=aJH9rJAeTfBaLadGE7x266TvBHSPhMIowCMjQRq8Ynw,1524
185
185
  spinta/cli/helpers/admin/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
186
186
  spinta/cli/helpers/admin/scripts/changelog.py,sha256=r5heUIoGxILLUB2qqIpliN1JWYcGB6WmPfbVpOzWYm4,13771
187
187
  spinta/cli/helpers/admin/scripts/deduplicate.py,sha256=kn4vR7ZFC2-uihRIjYTY0en1BCGEM915mGE8IPI1K_8,9128
188
+ spinta/cli/helpers/admin/scripts/enums.py,sha256=OiNvg3KGLk5QnwgwyHp8nqoT5rf_d0XzX-Ma_f4yWco,5176
188
189
  spinta/cli/helpers/auth.py,sha256=qg35WJBCPHpQVLMOnvLKRqNcD6dSSX6NqldlauAuXBU,1196
189
190
  spinta/cli/helpers/data.py,sha256=T1t0X01beFthJQnlKFXWHMYxXjnZaPMxsbCP0wlJgHg,5530
190
191
  spinta/cli/helpers/errors.py,sha256=ILUm-sHIr4_bNlaonMuEaUJaPcYmOHz589M_sxpRzCo,412
@@ -198,7 +199,7 @@ spinta/cli/helpers/export/commands.py,sha256=obYO_6bXTfpJCkHn_fV9dsIwzhQwPQVUPhk
198
199
  spinta/cli/helpers/export/components.py,sha256=a5Q9qYym5KeZ0lyb5RXmKVND5EzQdf-PaeezzQMecXQ,1773
199
200
  spinta/cli/helpers/export/helpers.py,sha256=OpuPxrz3PNc9hKZROaavxdXbHsiiYoVXWWtxvETM0II,4737
200
201
  spinta/cli/helpers/manifest.py,sha256=8R39le_edqfXnC1ar_Q79JgEz05gBko64SarOjSixBs,1317
201
- spinta/cli/helpers/message.py,sha256=sSKYNeDp0rql7fKuheYU9xAJX4g-sfaUJahpcLjnP1g,317
202
+ spinta/cli/helpers/message.py,sha256=EwcZtC4JkUFsXcNmw6URfqPR2TQMLTIBD6EvfojMZ4M,518
202
203
  spinta/cli/helpers/migrate.py,sha256=fhd4tra_jqYyEEMbh-2GBXQ3d_RGWt3HSfAmvSimiPo,460
203
204
  spinta/cli/helpers/push/__init__.py,sha256=KEllTU28EFUKU9qGagCeaT6E49zXSb6HTQYzN6G8NAY,2141
204
205
  spinta/cli/helpers/push/components.py,sha256=FeAPb4dOxv4Kok_Sf-3hpqbiHLPeyJ4J7r-D5oKHxao,2235
@@ -210,9 +211,9 @@ spinta/cli/helpers/push/sync.py,sha256=dSH5TuDTQwbl5qaVLGW5a4vV1eiq2Yo8dB4huRCKM
210
211
  spinta/cli/helpers/push/utils.py,sha256=Jc9TNaosbYp2TecumSVDSP59EDsdcoUfXwAihV4xI30,5846
211
212
  spinta/cli/helpers/push/write.py,sha256=mqKDHq_naooWGW_okpyj4jKjutwF7Gt3ssLhGB3mhyA,13563
212
213
  spinta/cli/helpers/script/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
213
- spinta/cli/helpers/script/components.py,sha256=mhUq2MG64TMY88HWhwwT9yvGKyo4bYR2W1LW8k9ot_k,4418
214
- spinta/cli/helpers/script/core.py,sha256=U41UigkfrPeSWZsPAK7SVXiBAsSeQzcSQsWMWpUtpOM,3717
215
- spinta/cli/helpers/script/helpers.py,sha256=ILjTrUknrL_bBHRtf57Y4PQr-m6VpS3zfaaaT5e_-1M,2201
214
+ spinta/cli/helpers/script/components.py,sha256=OSmIuQ7wG20vdgYoRZKrkiHkw4YM8bV5puV7EdP348k,4417
215
+ spinta/cli/helpers/script/core.py,sha256=KltI0rHaowz__j5AbpTPTlVc-j3SAiGarJwGPyO1jEM,3716
216
+ spinta/cli/helpers/script/helpers.py,sha256=ubx9BhhrPqEWIVrYC2LUTgwILI3I1WDGd8e68RIBzkU,2226
216
217
  spinta/cli/helpers/script/registry.py,sha256=h-wPrE7fOCD7B9hmqWTHNPHf1GyuEZuvJd9VmlB5khE,3266
217
218
  spinta/cli/helpers/store.py,sha256=LLXQtbuU0tjBB3f1DmWqmy57htY3pjog4tmJZDu9IrA,5507
218
219
  spinta/cli/helpers/sync/__init__.py,sha256=zarobjW3JbLgtYl9TWKusrFyzten5qRbwDo3447Kyrc,140
@@ -291,7 +292,7 @@ spinta/datasets/backends/dataframe/backends/memory/__init__.py,sha256=47DEQpj8HB
291
292
  spinta/datasets/backends/dataframe/backends/memory/components.py,sha256=tByPlAR0hp8WsL0i5JxAPbOh7bdawlw9UQEfT5SUIEo,226
292
293
  spinta/datasets/backends/dataframe/backends/soap/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
293
294
  spinta/datasets/backends/dataframe/backends/soap/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
294
- spinta/datasets/backends/dataframe/backends/soap/commands/read.py,sha256=BXdfQPkgayP35YyAuTJk8JZCD6LKgqc8dSrSBvkKjUo,5300
295
+ spinta/datasets/backends/dataframe/backends/soap/commands/read.py,sha256=5pVdcSQ47QH9RXgu6_sw8E0eMLW9colZ-6AgeWwnh_I,5667
295
296
  spinta/datasets/backends/dataframe/backends/soap/components.py,sha256=TtJFhUfJ6MZTbCUxQC_yiADEGPL8uGVVX9nDVrGiJoA,1451
296
297
  spinta/datasets/backends/dataframe/backends/soap/ufuncs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
297
298
  spinta/datasets/backends/dataframe/backends/soap/ufuncs/components.py,sha256=VmKOHUeeCBcYz3xSh6qj657VeU7BgBL6X8DoSLmEMLE,886
@@ -614,7 +615,7 @@ spinta/testing/cli.py,sha256=HPwUHeo2W5q5dRl56kIytJIxvG7WTdi52N_CMylEuvM,1840
614
615
  spinta/testing/client.py,sha256=q_SLseq_hLN7O8-pxWJmNfJqeoMCGQrfWPy8Fh-oqQ8,10040
615
616
  spinta/testing/config.py,sha256=o40GCI0qAeQGK1u-VrxtnUh45m0o-tPHySJ0oQmN4j4,2919
616
617
  spinta/testing/context.py,sha256=9lDGKEN1wV58TW9KlrJyuzCo2vMer_HSP2MKQsKPZqI,3382
617
- spinta/testing/csv.py,sha256=XxAXwlQLd9yHmNi7m3oClPJJcDCnMX2qqOAz4WrAbgA,214
618
+ spinta/testing/csv.py,sha256=JnrB3xwQDvd0EL7fQN9CcvLs9Bspydq0mdqAE11QlPM,357
618
619
  spinta/testing/data.py,sha256=YHIypRRy1InkFwgx-jvLUaCCAvY1rA-QeHPYn4bL1iE,7259
619
620
  spinta/testing/datasets.py,sha256=gzPQoq_1XEzN1v9ghESNEFN5GPrwE622uifObDaBKMs,1478
620
621
  spinta/testing/dtypes.py,sha256=5qGBuCjDJywTJSvJmvA62DvY8H-dJFti441wtGkrvO0,6288
@@ -740,8 +741,8 @@ spinta/utils/tree.py,sha256=iF8eOSBagUoDmdJGNQgsYB_gshsFajmiUALXqU9luHE,591
740
741
  spinta/utils/types.py,sha256=lfYSxKGPuPeUsO14d2OYodtbRY3zsa-o-z8HveVH3t0,801
741
742
  spinta/utils/units.py,sha256=CFFLv1NHYsoSSzwiar3zXYmt4m3sccW5niUgkZQgo3k,747
742
743
  spinta/utils/url.py,sha256=b6sqQEpmCdT3oV4vGDzXnN8w415InAYjIW_o2djhQS8,2950
743
- spinta-0.2.dev22.dist-info/METADATA,sha256=P_BrClJT9fCuAKLPRw8wd5wxV1SPvk-bASxDI1Su2JM,10214
744
- spinta-0.2.dev22.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
745
- spinta-0.2.dev22.dist-info/entry_points.txt,sha256=-jdsOQZcMu3rUOwgIJNS3gZS4rwWPACuXXy128F676w,46
746
- spinta-0.2.dev22.dist-info/licenses/LICENSE,sha256=JKmjfBLapeFWNI_qdVr5bXGlsuMPa6nRarKPK5davKw,1071
747
- spinta-0.2.dev22.dist-info/RECORD,,
744
+ spinta-0.2.dev23.dist-info/METADATA,sha256=InX-K2DKF6mZUaQ5Kn7jEHA58o2JHRM38T1o_sM5ptU,10214
745
+ spinta-0.2.dev23.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
746
+ spinta-0.2.dev23.dist-info/entry_points.txt,sha256=-jdsOQZcMu3rUOwgIJNS3gZS4rwWPACuXXy128F676w,46
747
+ spinta-0.2.dev23.dist-info/licenses/LICENSE,sha256=JKmjfBLapeFWNI_qdVr5bXGlsuMPa6nRarKPK5davKw,1071
748
+ spinta-0.2.dev23.dist-info/RECORD,,