Skip to content

Commit

Permalink
ci: Streamline CI tools and workflows (#65)
Browse files Browse the repository at this point in the history
* ci: Streamline CI tools

* style: Fix code formatting
  • Loading branch information
danielptv authored Feb 17, 2024
1 parent ece7244 commit 749d8b6
Show file tree
Hide file tree
Showing 16 changed files with 335 additions and 227 deletions.
4 changes: 2 additions & 2 deletions .flake8
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[flake8]
ignore = D105,D203,D213,W503
docstring-convention = google
max-line-length = 120
max-complexity = 10
max-line-length = 88
max-complexity = 15
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@ jobs:
- name: Deploy to PyPI
run: |
poetry publish -r testpypi -u "__token__" -p "${{ secrets.TEST_PYPI_TOKEN }}"
poetry publish -u "__token__" -p "${{ secrets.PYPI_TOKEN }}"
poetry publish -u "__token__" -p "${{ secrets.PYPI_TOKEN }}"
3 changes: 3 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,6 @@ jobs:
- name: Test with pytest
run: |
poetry run pytest
- name: Lint with tox
run: |
poetry run tox -e lint
34 changes: 34 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
ci:
autofix_prs: false
autoupdate_schedule: weekly
autoupdate_commit_msg: 'chore: pre-commit autoupdate'

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: check-json
- id: check-toml
exclude: |
(?x)^(
copier_template/.*/pyproject.toml
)$
- id: end-of-file-fixer
exclude: (copier_template/.*|docs/.*|samples/.*\.json)
- id: trailing-whitespace

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.2.1
hooks:
- id: ruff
args: [--fix]
- id: ruff-format

- repo: https://github.com/pre-commit/mirrors-mypy
rev: 'v1.8.0'
hooks:
- id: mypy
exclude: tests
additional_dependencies:
- types-sqlalchemy
- types-jsonschema
2 changes: 0 additions & 2 deletions .pylintrc

This file was deleted.

2 changes: 1 addition & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
"ms-python.flake8",
"ms-python.black-formatter"
]
}
}
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"editor.defaultFormatter": "ms-python.black-formatter"
}
}
205 changes: 99 additions & 106 deletions poetry.lock

Large diffs are not rendered by default.

78 changes: 41 additions & 37 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@
name = "tap-ibm-db2"
version = "0.0.0"
description = "`Tap-DB2` is a Singer tap for IBM DB2 data sources."
readme = "README.md"
authors = ["Daniel Purtov"]
license = "MIT"
readme = "README.md"
homepage = "https://meltano.com"
repository = "https://github.com/danielptv/tap-db2"
keywords = [
"ELT",
"DB2",
"IBM DB2",
"DB2"
"ELT",
"Meltano",
"Meltano SDK"
]
classifiers = [
"Intended Audience :: Developers",
Expand All @@ -19,62 +24,61 @@ classifiers = [
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: Implementation :: CPython",
]
license = "MIT"
packages = [
{ include = "tap_db2" },
]

[tool.poetry.dependencies]
python = ">=3.8,<3.12"
singer-sdk = { version=">=0.31.1,<0.36.0" }
singer-sdk = "0.35.1"
ibm-db-sa = "0.4.0"
sqlalchemy = "2.0.27"

[tool.poetry.group.dev.dependencies]
pytest = "8.0.0"
singer-sdk = { version=">=0.31.1,<0.36.0", extras = ["testing"] }
black = "24.2.0"
deptry = "0.12.0"
faker = "23.2.1"
isort = "5.13.2"
tox = "4.12.1"
mypy = "1.8.0"
ruff = "0.2.1"
types-sqlalchemy = "1.4.53.38"
types-jsonschema = "^4.21.0.20240118"

[tool.mypy]
python_version = "3.9"
warn_unused_configs = true
plugins = "sqlmypy"

[tool.black]
line-length = 120

[tool.ruff]
ignore = [
"ANN101", # missing-type-self
"ANN102", # missing-type-cls
]
select = ["ALL"]
src = ["tap_db2"]
target-version = "py37"


[tool.ruff.flake8-annotations]
allow-star-arg-any = true

[tool.ruff.isort]
known-first-party = ["tap_db2"]

[tool.ruff.pydocstyle]
convention = "google"
exclude = "tests"

[build-system]
requires = ["poetry-core==1.8.1", "poetry-dynamic-versioning==1.2.0"]
build-backend = "poetry_dynamic_versioning.backend"

[tool.poetry.scripts]
# CLI declaration
tap-db2 = 'tap_db2.tap:TapDB2.cli'

[tool.poetry-dynamic-versioning]
enable = true
vcs = "git"
style = "semver"

[tool.poetry.scripts]
# CLI declaration
tap-db2 = 'tap_db2.tap:TapDB2.cli'
[tool.ruff]
target-version = "py38"

[tool.ruff.lint]
select = [
"F", # Pyflakes
"W", # pycodestyle warnings
"E", # pycodestyle errors
"I", # isort
"N", # pep8-naming
"D", # pydocsyle
"ICN", # flake8-import-conventions
"RUF", # ruff
]

[tool.ruff.lint.flake8-import-conventions]
banned-from = ["sqlalchemy"]

[tool.ruff.lint.flake8-import-conventions.extend-aliases]
sqlalchemy = "sa"

[tool.ruff.lint.pydocstyle]
convention = "google"
54 changes: 33 additions & 21 deletions tap_db2/connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
import re
import typing as t

import sqlalchemy # noqa: TCH002
import sqlalchemy as sa
from singer_sdk import SQLConnector
from singer_sdk import typing as th
from sqlalchemy.engine import Engine
from sqlalchemy.types import TypeEngine

SUPPLIED_USER_TABLES_PATTERN = r"^(DSN_|PLAN_TABLE)"

Expand All @@ -28,37 +29,36 @@ def get_sqlalchemy_url(self, config: dict) -> str:
connection_url = (
f"ibm_db_sa://{config['user']}:"
f"{config['password']}@{config['host']}"
f":{str(config['port'])}/"
f":{config['port']!s}/"
f"{config['database']};"
)
if "encryption" in config:
connection_url += "SECURITY=SSL;"
if "ssl_server_certificate" in config["encryption"]:
connection_url += f"SSLServerCertificate={config['encryption']['ssl_server_certificate']};"
connection_url += f"SSLServerCertificate={config['encryption']['ssl_server_certificate']};" # noqa: E501
if "ssl_client_key_store_db" in config["encryption"]:
connection_url += f"SSLClientKeyStoreDB={config['encryption']['ssl_client_key_store_db']['database']};"
connection_url += f"SSLClientKeyStoreDB={config['encryption']['ssl_client_key_store_db']['database']};" # noqa: E501
if "password" in config["encryption"]["ssl_client_key_store_db"]:
connection_url += (
f"SSLClientKeyStoreDBPassword={config['encryption']['ssl_client_key_store_db']['password']};"
)
connection_url += f"SSLClientKeyStoreDBPassword={config['encryption']['ssl_client_key_store_db']['password']};" # noqa: E501
if "key_stash" in config["encryption"]["ssl_client_key_store_db"]:
connection_url += (
f"SSLClientKeyStash={config['encryption']['ssl_client_key_store_db']['key_stash']};"
)
connection_url += f"SSLClientKeyStash={config['encryption']['ssl_client_key_store_db']['key_stash']};" # noqa: E501
if "connection_parameters" in config:
for key, value in config["connection_parameters"].items():
connection_url += f"{key}={value};"
return connection_url

def create_engine(self) -> Engine:
"""Creates and returns a new engine."""
if "sqlalchemy_execution_options" in self.config:
sqlalchemy_connection_kwargs = {"execution_options": self.config["sqlalchemy_execution_options"]}
return sqlalchemy.create_engine(self.sqlalchemy_url, **sqlalchemy_connection_kwargs)
return sqlalchemy.create_engine(self.sqlalchemy_url)
sqlalchemy_connection_kwargs = {
"execution_options": self.config["sqlalchemy_execution_options"]
}
return sa.create_engine(self.sqlalchemy_url, **sqlalchemy_connection_kwargs)
return sa.create_engine(self.sqlalchemy_url)

@staticmethod
def to_jsonschema_type(
sql_type: str | sqlalchemy.types.TypeEngine | type[sqlalchemy.types.TypeEngine] | t.Any, # noqa: ANN401
sql_type: str | TypeEngine | type[TypeEngine] | t.Any,
) -> dict:
"""Return a JSON Schema representation of the provided type.
Expand All @@ -69,7 +69,7 @@ def to_jsonschema_type(
The JSON Schema representation of the provided type.
"""
# Map DATE to date-time in JSON Schema
if isinstance(sql_type, sqlalchemy.DATE):
if isinstance(sql_type, sa.DATE):
return th.DateTimeType.type_dict

return SQLConnector.to_jsonschema_type(sql_type)
Expand All @@ -82,24 +82,36 @@ def discover_catalog_entries(self) -> list[dict]:
"""
result: list[dict] = []
engine = self._engine
inspected = sqlalchemy.inspect(engine)
inspected = sa.inspect(engine)
for schema_name in self.get_schema_names(engine, inspected):
# Iterate through each table and view
for table_name, is_view in self.get_object_names(
engine,
inspected,
schema_name,
):
if is_view and "ignore_views" in self.config and self.config["ignore_views"]:
if (
is_view
and "ignore_views" in self.config
and self.config["ignore_views"]
):
continue
if not re.match(SUPPLIED_USER_TABLES_PATTERN, table_name, re.IGNORECASE) or (
"ignore_supplied_tables" in self.config and not self.config["ignore_supplied_tables"]
if not re.match(
SUPPLIED_USER_TABLES_PATTERN, table_name, re.IGNORECASE
) or (
"ignore_supplied_tables" in self.config
and not self.config["ignore_supplied_tables"]
):
# Filter by schema
# Connection parameter 'CURRENTSCHEMA=mySchema;' doesn't work
# https://www.ibm.com/support/pages/525-error-nullidsysstat-package-when-trying-set-current-schema-against-db2-zos-database
target_schema = self.config["schema"] if "schema" in self.config else None
if target_schema is None or target_schema.strip().lower() == schema_name.strip().lower():
target_schema = (
self.config["schema"] if "schema" in self.config else None
)
if (
target_schema is None
or target_schema.strip().lower() == schema_name.strip().lower()
):
catalog_entry = self.discover_catalog_entry(
engine,
inspected,
Expand Down
Loading

0 comments on commit 749d8b6

Please sign in to comment.