Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Publish to PyPI

on:
push:
tags:
- '[0-9]+.[0-9]+.[0-9]+*'

jobs:
publish:
name: Build and publish to PyPI
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Install uv
uses: astral-sh/setup-uv@v7
with:
enable-cache: true

- name: Set up Python
run: uv python install 3.12

- name: Build package
run: uv build

- name: Smoke test wheel
run: |
uv run --isolated --no-project --with dist/*.whl python -c "import sqlalchemy_redshift; print(sqlalchemy_redshift.__version__)"

- name: Publish to PyPI
run: uv publish
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,7 @@ target/

# IDE
.idea/

# uv
.venv
.python-version
1 change: 0 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ The package is available on PyPI::

* psycopg2 - standard distribution of psycopg2, requires compilation so few system dependencies are required for it
* psycopg2-binary - already compiled distribution (no system dependencies are required)
* psycopg2cffi - pypy compatible version

See `Psycopg2's binary install docs <http://initd.org/psycopg/docs/install.html#binary-install-from-pypi>`_
for more context on choosing a distribution.
Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def _warn_node(self, msg, node, **kwargs):
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
language = 'en'

# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
Expand Down
64 changes: 64 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
[build-system]
requires = ["hatchling", "uv-dynamic-versioning"]
build-backend = "hatchling.build"

[project]
name = "deepnote-sqlalchemy-redshift"
dynamic = ["version"]
description = "Amazon Redshift Dialect for sqlalchemy"
readme = "README.rst"
requires-python = ">=3.9,<3.14"
license = "MIT"
authors = [
{name = "Matt George", email = "mgeorge@gmail.com"},
]
maintainers = [
{name = "Deepnote", email = "product-engineers@deepnote.com"},
]
classifiers = [
"Development Status :: 4 - Beta",
"Environment :: Console",
"Intended Audience :: Developers",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
]
dependencies = [
"SQLAlchemy>=1.4.15,<3.0.0",
"packaging",
]

[project.urls]
Homepage = "https://github.com/deepnote/sqlalchemy-redshift"

[project.entry-points."sqlalchemy.dialects"]
redshift = "sqlalchemy_redshift.dialect:RedshiftDialect_psycopg2"
"redshift.psycopg2" = "sqlalchemy_redshift.dialect:RedshiftDialect_psycopg2"
"redshift.redshift_connector" = "sqlalchemy_redshift.dialect:RedshiftDialect_redshift_connector"

[tool.hatch.version]
source = "uv-dynamic-versioning"

[tool.uv-dynamic-versioning]
fallback-version = "0.0.1"
vcs = "git"
style = "pep440"

[tool.hatch.build.targets.wheel]
packages = ["sqlalchemy_redshift"]

[tool.hatch.build.targets.wheel.force-include]
"sqlalchemy_redshift/redshift-ca-bundle.crt" = "sqlalchemy_redshift/redshift-ca-bundle.crt"

[tool.hatch.build.targets.sdist]
include = [
"sqlalchemy_redshift/",
"README.rst",
"CHANGES.rst",
"LICENSE",
]
22 changes: 0 additions & 22 deletions redshift_sqlalchemy/__init__.py

This file was deleted.

9 changes: 5 additions & 4 deletions requirements-docs.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
-e .
sphinx==1.6.3
numpydoc==0.6.0
psycopg2-binary==2.9.1
jinja2<3.1.0
sphinx>=7.0.0,<8.0.0
numpydoc>=1.5.0,<2.0.0
psycopg2-binary>=2.9.9,<3.0.0
jinja2>=3.1.0,<4.0.0
setuptools>=65.0.0,<76.0.0
2 changes: 0 additions & 2 deletions setup.cfg

This file was deleted.

53 changes: 0 additions & 53 deletions setup.py

This file was deleted.

17 changes: 8 additions & 9 deletions sqlalchemy_redshift/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
from pkg_resources import DistributionNotFound, get_distribution, parse_version
from importlib.metadata import version, PackageNotFoundError
from packaging.version import parse as parse_version

for package in ['psycopg2', 'psycopg2-binary', 'psycopg2cffi']:
MIN_PSYCOPG2_VERSION = parse_version('2.5')

for package in ['psycopg2', 'psycopg2-binary']:
try:
if get_distribution(package).parsed_version < parse_version('2.5'):
if parse_version(version(package)) < MIN_PSYCOPG2_VERSION:
raise ImportError('Minimum required version for psycopg2 is 2.5')
break
except DistributionNotFound:
except PackageNotFoundError:
pass

__version__ = get_distribution('sqlalchemy-redshift').version
__version__ = version('deepnote-sqlalchemy-redshift')

from sqlalchemy.dialects import registry # noqa

Expand All @@ -20,10 +23,6 @@
"redshift.psycopg2", "sqlalchemy_redshift.dialect",
"RedshiftDialect_psycopg2"
)
registry.register(
'redshift+psycopg2cffi', 'sqlalchemy_redshift.dialect',
'RedshiftDialect_psycopg2cffi',
)

registry.register(
"redshift+redshift_connector", "sqlalchemy_redshift.dialect",
Expand Down
10 changes: 5 additions & 5 deletions sqlalchemy_redshift/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@
TOKEN_RE = re.compile('[A-Za-z0-9/+=]+')
AWS_PARTITIONS = frozenset({'aws', 'aws-cn', 'aws-us-gov'})
AWS_ACCOUNT_ID_RE = re.compile('[0-9]{12}')
IAM_ROLE_NAME_RE = re.compile('[A-Za-z0-9+=,.@\-_]{1,64}') # noqa
IAM_ROLE_ARN_RE = re.compile('arn:(aws|aws-cn|aws-us-gov):iam::'
'[0-9]{12}:role/[A-Za-z0-9+=,.@\-_]{1,64}') # noqa
IAM_ROLE_NAME_RE = re.compile(r'[A-Za-z0-9+=,.@\-_]{1,64}')
IAM_ROLE_ARN_RE = re.compile(r'arn:(aws|aws-cn|aws-us-gov):iam::'
r'[0-9]{12}:role/[A-Za-z0-9+=,.@\-_]{1,64}')


def _process_aws_credentials(access_key_id=None, secret_access_key=None,
Expand Down Expand Up @@ -501,7 +501,7 @@ class CopyCommand(_ExecutableClause):
aws_account_id: str, optional
AWS account ID for role-based credentials. Required unless you supply
key based credentials (``access_key_id`` and ``secret_access_key``)
or role arns (``iam_role_arns``) directly.
or role arns (``iam_role_arns``) directly.
iam_role_name: str, optional
IAM role name for role-based credentials. Required unless you supply
key based credentials (``access_key_id`` and ``secret_access_key``)
Expand Down Expand Up @@ -930,7 +930,7 @@ class CreateLibraryCommand(_ExecutableClause):
aws_account_id: str, optional
AWS account ID for role-based credentials. Required unless you supply
key based credentials (``access_key_id`` and ``secret_access_key``)
or role arns (``iam_role_arns``) directly.
or role arns (``iam_role_arns``) directly.
iam_role_name: str, optional
IAM role name for role-based credentials. Required unless you supply
key based credentials (``access_key_id`` and ``secret_access_key``)
Expand Down
8 changes: 1 addition & 7 deletions sqlalchemy_redshift/ddl.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,21 +116,18 @@ class CreateMaterializedView(DDLElement):
... sa.Column('id', sa.Integer, primary_key=True),
... sa.Column('name', sa.String)
... )
>>> selectable = sa.select([user.c.id, user.c.name], from_obj=user)
>>> selectable = sa.select(user.c.id, user.c.name)
>>> view = CreateMaterializedView(
... 'materialized_view_of_users',
... selectable,
... distkey='id',
... sortkey='name'
... )
>>> print(view.compile(engine))
<BLANKLINE>
CREATE MATERIALIZED VIEW materialized_view_of_users
DISTKEY (id) SORTKEY (name)
AS SELECT "user".id, "user".name
FROM "user"
<BLANKLINE>
<BLANKLINE>

The materialized view can take full advantage of Redshift's distributed
architecture via distribution styles and sort keys.
Expand Down Expand Up @@ -224,10 +221,7 @@ class DropMaterializedView(DDLElement):
... if_exists=True
... )
>>> print(drop.compile(engine))
<BLANKLINE>
DROP MATERIALIZED VIEW IF EXISTS materialized_view_of_users
<BLANKLINE>
<BLANKLINE>

This can be included in any execute() statement.
"""
Expand Down
Loading