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.
Files changed (26) hide show
  1. {dynamicio-7.2.0 → dynamicio-7.2.1}/PKG-INFO +5 -4
  2. {dynamicio-7.2.0 → dynamicio-7.2.1}/README.md +2 -2
  3. {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/__init__.py +6 -4
  4. {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/config/pydantic/__init__.py +1 -0
  5. {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/config/pydantic/io_resources.py +20 -2
  6. dynamicio-7.2.1/dynamicio/mixins/__init__.py +8 -0
  7. dynamicio-7.2.1/dynamicio/mixins/with_athena.py +55 -0
  8. {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/mixins/with_kafka.py +5 -0
  9. {dynamicio-7.2.0 → dynamicio-7.2.1}/pyproject.toml +5 -4
  10. dynamicio-7.2.0/dynamicio/mixins/__init__.py +0 -16
  11. {dynamicio-7.2.0 → dynamicio-7.2.1}/LICENSE +0 -0
  12. {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/__main__.py +0 -0
  13. {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/cli.py +0 -0
  14. {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/config/__init__.py +0 -0
  15. {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/config/io_config.py +0 -0
  16. {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/config/pydantic/config.py +0 -0
  17. {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/config/pydantic/table_schema.py +0 -0
  18. {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/core.py +0 -0
  19. {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/errors.py +0 -0
  20. {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/metrics.py +0 -0
  21. {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/mixins/utils.py +0 -0
  22. {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/mixins/with_local.py +0 -0
  23. {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/mixins/with_postgres.py +0 -0
  24. {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/mixins/with_s3.py +0 -0
  25. {dynamicio-7.2.0 → dynamicio-7.2.1}/dynamicio/py.typed +0 -0
  26. {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.0
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.3.0)
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
- [![CircleCI](https://dl.circleci.com/status-badge/img/gh/VorTECHsa/dynamicio/tree/master.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/gh/VorTECHsa/dynamicio/tree/master)
38
- [![Coverage Status](https://github.com/VorTECHsa/dynamicio/blob/master/docs/coverage_report/coverage-badge.svg?raw=True)]()
38
+ [![Build Status](https://github.com/VorTECHsa/dynamicio/actions/workflows/build_and_publish.yml/badge.svg?branch=master)](https://github.com/VorTECHsa/dynamicio/actions/workflows/build_and_publish.yml)
39
+ [![codecov](https://codecov.io/gh/VorTECHsa/dynamicio/graph/badge.svg?token=McZFwD2QPh)](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
- [![CircleCI](https://dl.circleci.com/status-badge/img/gh/VorTECHsa/dynamicio/tree/master.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/gh/VorTECHsa/dynamicio/tree/master)
2
- [![Coverage Status](https://github.com/VorTECHsa/dynamicio/blob/master/docs/coverage_report/coverage-badge.svg?raw=True)]()
1
+ [![Build Status](https://github.com/VorTECHsa/dynamicio/actions/workflows/build_and_publish.yml/badge.svg?branch=master)](https://github.com/VorTECHsa/dynamicio/actions/workflows/build_and_publish.yml)
2
+ [![codecov](https://codecov.io/gh/VorTECHsa/dynamicio/graph/badge.svg?token=McZFwD2QPh)](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 version, PackageNotFoundError
9
+ from importlib.metadata import PackageNotFoundError, version
9
10
  except ImportError:
10
- from importlib_metadata import version, PackageNotFoundError # type: ignore
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,5 +9,6 @@ from dynamicio.config.pydantic.io_resources import (
9
9
  PostgresDataEnvironment,
10
10
  S3DataEnvironment,
11
11
  S3PathPrefixEnvironment,
12
+ AthenaDataEnvironment
12
13
  )
13
14
  from dynamicio.config.pydantic.table_schema import DataframeSchema, SchemaColumn
@@ -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 (env_name, env_data) in info.items():
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 (key, value) in values.items():
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.0" # dummy, will be overridden by git tag
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.3.0"
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 = "==22.10.0"
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 = "==65.5.1"
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