dynamicio 7.2.0__tar.gz → 7.2.1__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.
- {dynamicio-7.2.0 → dynamicio-7.2.1}/PKG-INFO +5 -4
- {dynamicio-7.2.0 → dynamicio-7.2.1}/README.md +2 -2
- {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/__init__.py +6 -4
- {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/config/pydantic/__init__.py +1 -0
- {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/config/pydantic/io_resources.py +20 -2
- dynamicio-7.2.1/dynamicio/mixins/__init__.py +8 -0
- dynamicio-7.2.1/dynamicio/mixins/with_athena.py +55 -0
- {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/mixins/with_kafka.py +5 -0
- {dynamicio-7.2.0 → dynamicio-7.2.1}/pyproject.toml +5 -4
- dynamicio-7.2.0/dynamicio/mixins/__init__.py +0 -16
- {dynamicio-7.2.0 → dynamicio-7.2.1}/LICENSE +0 -0
- {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/__main__.py +0 -0
- {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/cli.py +0 -0
- {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/config/__init__.py +0 -0
- {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/config/io_config.py +0 -0
- {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/config/pydantic/config.py +0 -0
- {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/config/pydantic/table_schema.py +0 -0
- {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/core.py +0 -0
- {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/errors.py +0 -0
- {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/metrics.py +0 -0
- {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/mixins/utils.py +0 -0
- {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/mixins/with_local.py +0 -0
- {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/mixins/with_postgres.py +0 -0
- {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/mixins/with_s3.py +0 -0
- {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/py.typed +0 -0
- {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/validations.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: dynamicio
|
|
3
|
-
Version: 7.2.
|
|
3
|
+
Version: 7.2.1
|
|
4
4
|
Summary: Panda's wrapper for IO operations
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Keywords: data,pandas,io
|
|
@@ -18,7 +18,7 @@ Requires-Dist: awscli (>=1.22.24)
|
|
|
18
18
|
Requires-Dist: boto3 (>=1.20.24)
|
|
19
19
|
Requires-Dist: confluent-kafka (>=2.4.0,<2.5.0)
|
|
20
20
|
Requires-Dist: fastparquet (>=0.8.0)
|
|
21
|
-
Requires-Dist: fsspec (==2022.
|
|
21
|
+
Requires-Dist: fsspec (==2022.11.0)
|
|
22
22
|
Requires-Dist: libcst (==1.5.1)
|
|
23
23
|
Requires-Dist: logzero (>=1.7.0)
|
|
24
24
|
Requires-Dist: magic-logger (>=1.0.2)
|
|
@@ -27,6 +27,7 @@ Requires-Dist: numpy (>=1.24.0,<2.0.0)
|
|
|
27
27
|
Requires-Dist: pandas (>=1.5.1)
|
|
28
28
|
Requires-Dist: psycopg2-binary (>=2.9.3,<2.10.0)
|
|
29
29
|
Requires-Dist: pyarrow (>=10.0.1)
|
|
30
|
+
Requires-Dist: pyathena
|
|
30
31
|
Requires-Dist: pydantic (>=1.9.2,<3)
|
|
31
32
|
Requires-Dist: python-json-logger (>=2.0.1,<2.1.0)
|
|
32
33
|
Requires-Dist: s3fs (==0.4.2)
|
|
@@ -34,8 +35,8 @@ Requires-Dist: simplejson (>=3.17.2,<3.18.0)
|
|
|
34
35
|
Requires-Dist: tables (>=3.10.1)
|
|
35
36
|
Description-Content-Type: text/markdown
|
|
36
37
|
|
|
37
|
-
[](https://github.com/VorTECHsa/dynamicio/actions/workflows/build_and_publish.yml)
|
|
39
|
+
[](https://codecov.io/gh/VorTECHsa/dynamicio)
|
|
39
40
|
[<img src="https://img.shields.io/badge/slack-@vortexa/dynamicio_public-purple.svg?logo=slack">](https://join.slack.com/share/enQtMzg2Nzk3ODY3MzEzNi0yNTU1ZmIyN2JkMGFhZjhhZWVjNzA2OWUzNWIyMjMyYmYzZmE4MzBjYWQ3YjdhNjU1MGU2NjFkNzMyZDllMzE2?raw=True)
|
|
40
41
|
|
|
41
42
|
<img src="https://github.com/VorTECHsa/dynamicio/blob/master/docs/images/logo-transparent.png?raw=True" width="500"> <img src="https://github.com/VorTECHsa/dynamicio/blob/master/docs/images/wrapped-panda.png?raw=True" width="100">
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
[](https://github.com/VorTECHsa/dynamicio/actions/workflows/build_and_publish.yml)
|
|
2
|
+
[](https://codecov.io/gh/VorTECHsa/dynamicio)
|
|
3
3
|
[<img src="https://img.shields.io/badge/slack-@vortexa/dynamicio_public-purple.svg?logo=slack">](https://join.slack.com/share/enQtMzg2Nzk3ODY3MzEzNi0yNTU1ZmIyN2JkMGFhZjhhZWVjNzA2OWUzNWIyMjMyYmYzZmE4MzBjYWQ3YjdhNjU1MGU2NjFkNzMyZDllMzE2?raw=True)
|
|
4
4
|
|
|
5
5
|
<img src="https://github.com/VorTECHsa/dynamicio/blob/master/docs/images/logo-transparent.png?raw=True" width="500"> <img src="https://github.com/VorTECHsa/dynamicio/blob/master/docs/images/wrapped-panda.png?raw=True" width="100">
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
"""A package for wrapping your I/O operations."""
|
|
2
|
+
# pylint: disable=abstract-method
|
|
2
3
|
import os
|
|
3
4
|
from contextlib import suppress
|
|
4
5
|
|
|
5
6
|
from magic_logger import logger
|
|
6
7
|
|
|
7
8
|
try:
|
|
8
|
-
from importlib.metadata import
|
|
9
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
9
10
|
except ImportError:
|
|
10
|
-
from importlib_metadata import
|
|
11
|
+
from importlib_metadata import PackageNotFoundError, version # type: ignore
|
|
11
12
|
|
|
12
13
|
with suppress(Exception):
|
|
13
14
|
try:
|
|
@@ -15,13 +16,14 @@ with suppress(Exception):
|
|
|
15
16
|
except PackageNotFoundError:
|
|
16
17
|
__version__ = "0.0.0"
|
|
17
18
|
|
|
19
|
+
# Application Imports
|
|
18
20
|
from dynamicio.core import DynamicDataIO
|
|
19
|
-
from dynamicio.mixins import WithKafka, WithLocal, WithLocalBatch, WithPostgres, WithS3File, WithS3PathPrefix
|
|
21
|
+
from dynamicio.mixins import WithAthena, WithKafka, WithLocal, WithLocalBatch, WithPostgres, WithS3File, WithS3PathPrefix
|
|
20
22
|
|
|
21
23
|
os.environ["LC_CTYPE"] = "en_US.UTF" # Set your locale to a unicode-compatible one
|
|
22
24
|
|
|
23
25
|
|
|
24
|
-
class UnifiedIO(WithS3File, WithS3PathPrefix, WithLocalBatch, WithLocal, WithKafka, WithPostgres, DynamicDataIO): # type: ignore
|
|
26
|
+
class UnifiedIO(WithS3File, WithS3PathPrefix, WithLocalBatch, WithLocal, WithKafka, WithAthena, WithPostgres, DynamicDataIO): # type: ignore
|
|
25
27
|
"""A unified io composed of dynamicio.mixins."""
|
|
26
28
|
|
|
27
29
|
|
|
@@ -9,6 +9,7 @@ from typing import Mapping, Optional, Union
|
|
|
9
9
|
import pydantic
|
|
10
10
|
from pydantic import BaseModel
|
|
11
11
|
|
|
12
|
+
# Application Imports
|
|
12
13
|
import dynamicio.config.pydantic.table_schema as table_spec
|
|
13
14
|
|
|
14
15
|
|
|
@@ -65,9 +66,10 @@ class IOBinding(BaseModel):
|
|
|
65
66
|
DataBackendType.s3_path_prefix: S3PathPrefixEnvironment,
|
|
66
67
|
DataBackendType.kafka: KafkaDataEnvironment,
|
|
67
68
|
DataBackendType.postgres: PostgresDataEnvironment,
|
|
69
|
+
DataBackendType.athena: AthenaDataEnvironment,
|
|
68
70
|
}
|
|
69
71
|
out_dict = {}
|
|
70
|
-
for
|
|
72
|
+
for env_name, env_data in info.items():
|
|
71
73
|
base_obj: IOEnvironment = IOEnvironment(**env_data)
|
|
72
74
|
override_cls = config_cls_overrides.get(base_obj.data_backend_type)
|
|
73
75
|
if override_cls:
|
|
@@ -82,7 +84,7 @@ class IOBinding(BaseModel):
|
|
|
82
84
|
if not isinstance(values, Mapping):
|
|
83
85
|
raise ValueError(f"IOBinding must be a dict at the top level. (got {values!r} instead)")
|
|
84
86
|
remapped_value = {"environments": {}}
|
|
85
|
-
for
|
|
87
|
+
for key, value in values.items():
|
|
86
88
|
if key in ("__binding_name__", "schema"):
|
|
87
89
|
# Passthrough params
|
|
88
90
|
remapped_value[key] = value
|
|
@@ -270,4 +272,20 @@ class PostgresDataEnvironment(IOEnvironment):
|
|
|
270
272
|
postgres: PostgresDataSubSection
|
|
271
273
|
|
|
272
274
|
|
|
275
|
+
class AthenaDataSubSection(BaseModel):
|
|
276
|
+
"""AWS Athena configuration section."""
|
|
277
|
+
|
|
278
|
+
s3_staging_dir: str
|
|
279
|
+
region_name: str
|
|
280
|
+
|
|
281
|
+
# Optional fields, one must be provided via YAML or mixin options
|
|
282
|
+
query: Optional[str] = None
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
class AthenaDataEnvironment(IOEnvironment):
|
|
286
|
+
"""Parent section for Athena source config."""
|
|
287
|
+
|
|
288
|
+
athena: AthenaDataSubSection
|
|
289
|
+
|
|
290
|
+
|
|
273
291
|
IOBinding.update_forward_refs()
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""Default dynamicio mixins module."""
|
|
2
|
+
|
|
3
|
+
# Application Imports
|
|
4
|
+
from dynamicio.mixins.with_athena import WithAthena
|
|
5
|
+
from dynamicio.mixins.with_kafka import WithKafka
|
|
6
|
+
from dynamicio.mixins.with_local import WithLocal, WithLocalBatch
|
|
7
|
+
from dynamicio.mixins.with_postgres import WithPostgres
|
|
8
|
+
from dynamicio.mixins.with_s3 import WithS3File, WithS3PathPrefix
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# pylint: disable=no-member, protected-access, too-few-public-methods
|
|
2
|
+
|
|
3
|
+
"""This module provides mixins that support AWS Athena I/O."""
|
|
4
|
+
import inspect
|
|
5
|
+
from typing import Any, MutableMapping
|
|
6
|
+
|
|
7
|
+
import pandas as pd
|
|
8
|
+
from magic_logger import logger
|
|
9
|
+
from pyathena import connect
|
|
10
|
+
from pyathena.connection import Connection
|
|
11
|
+
from pyathena.pandas.cursor import PandasCursor
|
|
12
|
+
from pyathena.pandas.result_set import AthenaPandasResultSet
|
|
13
|
+
|
|
14
|
+
# Application Imports
|
|
15
|
+
from dynamicio.config.pydantic import AthenaDataEnvironment
|
|
16
|
+
from dynamicio.mixins.utils import allow_options
|
|
17
|
+
|
|
18
|
+
allowed_athena_options = set(inspect.signature(AthenaPandasResultSet.__init__).parameters.keys()) - {"self"}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class WithAthena:
|
|
22
|
+
"""Handles I/O operations for AWS Athena."""
|
|
23
|
+
|
|
24
|
+
sources_config: AthenaDataEnvironment
|
|
25
|
+
options: MutableMapping[str, Any]
|
|
26
|
+
|
|
27
|
+
def _read_from_athena(self) -> pd.DataFrame:
|
|
28
|
+
"""Reads data from AWS Athena.
|
|
29
|
+
|
|
30
|
+
Expected config:
|
|
31
|
+
- query
|
|
32
|
+
- s3_staging_dir
|
|
33
|
+
- region_name
|
|
34
|
+
"""
|
|
35
|
+
cfg = self.sources_config.athena
|
|
36
|
+
query = self.options.pop("query", None)
|
|
37
|
+
|
|
38
|
+
assert query, "A 'query' must be provided for Athena read"
|
|
39
|
+
|
|
40
|
+
conn = connect(s3_staging_dir=cfg.s3_staging_dir, region_name=cfg.region_name, cursor_class=PandasCursor)
|
|
41
|
+
return self._run_query(conn, query, **self.options)
|
|
42
|
+
|
|
43
|
+
@allow_options(allowed_athena_options)
|
|
44
|
+
def _run_query(self, conn: Connection, query: str, **options: Any) -> pd.DataFrame:
|
|
45
|
+
logger.info(f"[athena] Executing query: {query}")
|
|
46
|
+
cursor = conn.cursor()
|
|
47
|
+
cursor.execute(query)
|
|
48
|
+
|
|
49
|
+
rows = cursor.fetchall(**options)
|
|
50
|
+
columns = [col[0] for col in cursor.description]
|
|
51
|
+
return pd.DataFrame(rows, columns=columns)
|
|
52
|
+
|
|
53
|
+
def _write_to_athena(self, df: pd.DataFrame):
|
|
54
|
+
"""Athena does not support direct writing. Raise NotImplementedError."""
|
|
55
|
+
raise NotImplementedError("Athena does not support direct writes via pandas. Consider writing to S3 or using Glue instead.")
|
|
@@ -8,6 +8,7 @@ import simplejson
|
|
|
8
8
|
from confluent_kafka import Producer
|
|
9
9
|
from magic_logger import logger
|
|
10
10
|
|
|
11
|
+
# Application Imports
|
|
11
12
|
from dynamicio.config.pydantic import DataframeSchema, KafkaDataEnvironment
|
|
12
13
|
from dynamicio.mixins import utils
|
|
13
14
|
|
|
@@ -242,6 +243,10 @@ class WithKafka:
|
|
|
242
243
|
"transactional.id",
|
|
243
244
|
}
|
|
244
245
|
|
|
246
|
+
def _read_from_kafka(self, df: pd.DataFrame):
|
|
247
|
+
"""Kafka is not ideal for reading files as part of batch ETL jobs. Raise NotImplementedError."""
|
|
248
|
+
raise NotImplementedError("Dynamicio does not support reading from Kafka Topics.")
|
|
249
|
+
|
|
245
250
|
def _write_to_kafka(self, df: pd.DataFrame) -> None:
|
|
246
251
|
"""Given a dataframe where each row is a message to be sent to a Kafka Topic, iterate through all rows and send them to a Kafka topic.
|
|
247
252
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "dynamicio"
|
|
3
|
-
version = "7.2.
|
|
3
|
+
version = "7.2.1" # dummy, will be overridden by git tag
|
|
4
4
|
description = "Panda's wrapper for IO operations"
|
|
5
5
|
authors = [
|
|
6
6
|
"Christos Hadjinikolis <christos.hadjinikolis@gmail.com>",
|
|
@@ -20,8 +20,9 @@ classifiers = [
|
|
|
20
20
|
python = ">=3.10,<3.13"
|
|
21
21
|
awscli = ">=1.22.24"
|
|
22
22
|
boto3 = ">=1.20.24"
|
|
23
|
+
pyathena = "*"
|
|
23
24
|
fastparquet = ">=0.8.0"
|
|
24
|
-
fsspec = "==2022.
|
|
25
|
+
fsspec = "==2022.11.0"
|
|
25
26
|
confluent-kafka = "~2.4.0"
|
|
26
27
|
logzero = ">=1.7.0"
|
|
27
28
|
magic-logger = ">=1.0.2"
|
|
@@ -40,7 +41,7 @@ tables = ">=3.10.1"
|
|
|
40
41
|
libcst = "==1.5.1"
|
|
41
42
|
|
|
42
43
|
[tool.poetry.group.dev.dependencies]
|
|
43
|
-
black = "==
|
|
44
|
+
black = "==24.3.0"
|
|
44
45
|
coverage-badge = "==1.1.0"
|
|
45
46
|
flake8-import-order = "*"
|
|
46
47
|
flake8-print = "*"
|
|
@@ -65,7 +66,7 @@ yamllint = "==1.28.0"
|
|
|
65
66
|
pdoc3 = "==0.10.0"
|
|
66
67
|
|
|
67
68
|
[tool.poetry.group.build.dependencies]
|
|
68
|
-
setuptools = "==
|
|
69
|
+
setuptools = "==70.0.0"
|
|
69
70
|
twine = "==4.0.1"
|
|
70
71
|
wheel = "==0.38.4"
|
|
71
72
|
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
"""Default dynamicio mixins module"""
|
|
2
|
-
|
|
3
|
-
from dynamicio.mixins.with_kafka import (
|
|
4
|
-
WithKafka,
|
|
5
|
-
)
|
|
6
|
-
from dynamicio.mixins.with_local import (
|
|
7
|
-
WithLocal,
|
|
8
|
-
WithLocalBatch,
|
|
9
|
-
)
|
|
10
|
-
from dynamicio.mixins.with_postgres import (
|
|
11
|
-
WithPostgres,
|
|
12
|
-
)
|
|
13
|
-
from dynamicio.mixins.with_s3 import (
|
|
14
|
-
WithS3File,
|
|
15
|
-
WithS3PathPrefix,
|
|
16
|
-
)
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|