Skip to content

Commit

Permalink
refactor: Dropped support for Python 3.8 (#2751)
Browse files Browse the repository at this point in the history
* refactor: Dropped support for Python 3.8

* Update tests/core/test_connector_sql.py
  • Loading branch information
edgarrmondragon committed Nov 11, 2024
1 parent 0d2120d commit ea3ee96
Show file tree
Hide file tree
Showing 20 changed files with 478 additions and 658 deletions.
5 changes: 2 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,15 @@ jobs:
session: [tests]
os: ["ubuntu-latest", "macos-latest", "windows-latest"]
python-version:
- "3.8"
- "3.9"
- "3.10"
- "3.11"
- "3.12"
- "3.13"
include:
- { session: doctest, python-version: "3.13", os: "ubuntu-latest" }
- { session: mypy, python-version: "3.12", os: "ubuntu-latest" }
- { session: deps, python-version: "3.12", os: "ubuntu-latest" }
- { session: mypy, python-version: "3.13", os: "ubuntu-latest" }
- { session: deps, python-version: "3.13", os: "ubuntu-latest" }

steps:
- uses: actions/checkout@v4
Expand Down
3 changes: 1 addition & 2 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,8 @@
"3.11",
"3.10",
"3.9",
"3.8",
]
main_python_version = "3.12"
main_python_version = "3.13"
locations = "singer_sdk", "tests", "noxfile.py", "docs/conf.py"
nox.options.sessions = (
"mypy",
Expand Down
968 changes: 414 additions & 554 deletions poetry.lock

Large diffs are not rendered by default.

31 changes: 12 additions & 19 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ classifiers = [
"Intended Audience :: Developers",
"License :: OSI Approved :: Apache Software License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3.8",
"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",
"Programming Language :: Python :: Implementation :: CPython",
"Topic :: Software Development :: Libraries :: Application Frameworks",
"Typing :: Typed",
Expand All @@ -38,7 +38,7 @@ license = "Apache-2.0"
"Youtube" = "https://www.youtube.com/meltano"

[tool.poetry.dependencies]
python = ">=3.8"
python = ">=3.9"
backoff = { version = ">=2.0.0", python = "<4" }
backports-datetime-fromisoformat = { version = ">=2.0.1", python = "<3.11" }
click = "~=8.0"
Expand All @@ -58,23 +58,20 @@ requests = ">=2.25.1"
# TODO: remove this constraint once we get rid of the `fs` dependency
# newer setuptools versions are incompatible with some dependencies (fs)
setuptools = "<=70.3.0"
simpleeval = [
{ version = ">=0.9.13,<1", python = "<3.9" },
{ version = ">=0.9.13,!=1.0.1", python = ">=3.9" },
]
simpleeval = ">=0.9.13,!=1.0.1"
simplejson = ">=3.17.6"
sqlalchemy = ">=1.4,<3.0"
typing-extensions = ">=4.5.0"

# Sphinx dependencies installed as optional 'docs' extras
# https://github.com/readthedocs/readthedocs.org/issues/4912#issuecomment-664002569
furo = {version = ">=2024.5.6", python = ">=3.9", optional = true}
myst-parser = {version = ">=3", python = ">=3.9", optional = true}
sphinx = {version = ">=7", python = ">=3.9", optional = true}
sphinx-copybutton = {version = ">=0.5.2", python = ">=3.9", optional = true}
sphinx-inline-tabs = {version = ">=2023.4.21", python = ">=3.9", optional = true}
sphinx-notfound-page = {version = ">=1.0.0", python = ">=3.9", optional = true}
sphinx-reredirects = {version = ">=0.1.5", python = ">=3.9", optional = true}
furo = {version = ">=2024.5.6", optional = true}
myst-parser = {version = ">=3", optional = true}
sphinx = {version = ">=7", optional = true}
sphinx-copybutton = {version = ">=0.5.2", optional = true}
sphinx-inline-tabs = {version = ">=2023.4.21", optional = true}
sphinx-notfound-page = {version = ">=1.0.0", optional = true}
sphinx-reredirects = {version = ">=0.1.5", optional = true}

# File storage dependencies installed as optional 'filesystem' extras
fs-s3fs = {version = ">=1.1.1", optional = true}
Expand All @@ -86,14 +83,10 @@ s3fs = { version = ">=2024.9.0", optional = true }
# the version of Numpy that is compatible with the earliest Python version supported
# by this project, but that may not be compatible with the latest Python version.
numpy = [
{ version = ">=1.22,<1.25", python = "==3.8", optional = true },
{ version = ">=1.22,<2.1", python = "==3.9", optional = true },
{ version = ">=1.22", python = ">=3.10", optional = true },
]
pyarrow = [
{ version = ">=13,<18", python = "==3.8", optional = true },
{ version = ">=13", python = ">=3.9", optional = true },
]
pyarrow = { version = ">=13", optional = true }

# Testing dependencies installed as optional 'testing' extras
pytest = {version=">=7.2.1", optional = true}
Expand Down Expand Up @@ -318,7 +311,7 @@ extend-exclude = [
"cookiecutter/*",
]
line-length = 88
target-version = "py38"
target-version = "py39"

[tool.ruff.format]
docstring-code-format = true
Expand Down
9 changes: 2 additions & 7 deletions samples/aapl/aapl.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,12 @@

from __future__ import annotations

import importlib.resources
import json
import sys

from singer_sdk import Stream, Tap

if sys.version_info < (3, 9):
import importlib_resources
else:
import importlib.resources as importlib_resources

PROJECT_DIR = importlib_resources.files("samples.aapl")
PROJECT_DIR = importlib.resources.files("samples.aapl")


class AAPL(Stream):
Expand Down
10 changes: 2 additions & 8 deletions samples/sample_tap_countries/countries_streams.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,15 @@
from __future__ import annotations

import abc
import sys
import importlib.resources
import typing as t

from requests_cache.session import CachedSession

from singer_sdk import typing as th
from singer_sdk.streams.graphql import GraphQLStream

if sys.version_info < (3, 9):
import importlib_resources
else:
from importlib import resources as importlib_resources


SCHEMAS_DIR = importlib_resources.files(__package__) / "schemas"
SCHEMAS_DIR = importlib.resources.files(__package__) / "schemas"


class CountriesAPIStream(GraphQLStream, metaclass=abc.ABCMeta):
Expand Down
10 changes: 2 additions & 8 deletions samples/sample_tap_gitlab/gitlab_graphql_streams.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,13 @@

from __future__ import annotations

import sys
import importlib.resources

from singer_sdk.streams import GraphQLStream

if sys.version_info < (3, 9):
import importlib_resources
else:
from importlib import resources as importlib_resources


SITE_URL = "https://gitlab.com/graphql"

SCHEMAS_DIR = importlib_resources.files(__package__) / "schemas"
SCHEMAS_DIR = importlib.resources.files(__package__) / "schemas"


class GitlabGraphQLStream(GraphQLStream):
Expand Down
10 changes: 2 additions & 8 deletions samples/sample_tap_gitlab/gitlab_rest_streams.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from __future__ import annotations

import sys
import importlib.resources
import typing as t

from singer_sdk.authenticators import SimpleAuthenticator
Expand All @@ -19,13 +19,7 @@
StringType,
)

if sys.version_info < (3, 9):
import importlib_resources
else:
from importlib import resources as importlib_resources


SCHEMAS_DIR = importlib_resources.files(__package__) / "schemas"
SCHEMAS_DIR = importlib.resources.files(__package__) / "schemas"

DEFAULT_URL_BASE = "https://gitlab.com/api/v4"

Expand Down
8 changes: 4 additions & 4 deletions singer_sdk/_singerlib/catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
from typing_extensions import TypeAlias


Breadcrumb = t.Tuple[str, ...]
Breadcrumb = tuple[str, ...]

logger = logging.getLogger(__name__)


class SelectionMask(t.Dict[Breadcrumb, bool]):
class SelectionMask(dict[Breadcrumb, bool]):
"""Boolean mask for property selection in schemas and records."""

def __missing__(self, breadcrumb: Breadcrumb) -> bool:
Expand Down Expand Up @@ -95,7 +95,7 @@ class StreamMetadata(Metadata):
AnyMetadata: TypeAlias = t.Union[Metadata, StreamMetadata]


class MetadataMapping(t.Dict[Breadcrumb, AnyMetadata]):
class MetadataMapping(dict[Breadcrumb, AnyMetadata]):
"""Stream metadata mapping."""

@classmethod
Expand Down Expand Up @@ -352,7 +352,7 @@ def to_dict(self) -> dict[str, t.Any]: # noqa: C901
return result


class Catalog(t.Dict[str, CatalogEntry]):
class Catalog(dict[str, CatalogEntry]):
"""Singer catalog mapping of stream entries."""

@classmethod
Expand Down
2 changes: 1 addition & 1 deletion singer_sdk/about.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
]

# Keep these in sync with the supported Python versions in pyproject.toml
_PY_MIN_VERSION = 8
_PY_MIN_VERSION = 9
_PY_MAX_VERSION = 13


Expand Down
2 changes: 1 addition & 1 deletion singer_sdk/connectors/sql.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ def boolean_to_jsonschema(self, column_type: sa.types.Boolean) -> dict: # noqa:


JSONtoSQLHandler: TypeAlias = t.Union[
t.Type[sa.types.TypeEngine],
type[sa.types.TypeEngine],
t.Callable[[dict], sa.types.TypeEngine],
]

Expand Down
8 changes: 4 additions & 4 deletions singer_sdk/contrib/batch_encoder_jsonl.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ def get_batches(
filename = f"{prefix}{sync_id}-{i}.json.gz"
with self.batch_config.storage.fs(create=True) as fs:
# TODO: Determine compression from config.
with fs.open(filename, "wb") as f, gzip.GzipFile(
fileobj=f,
mode="wb",
) as gz:
with (
fs.open(filename, "wb") as f,
gzip.GzipFile(fileobj=f, mode="wb") as gz,
):
gz.writelines(
(serialize_json(record) + "\n").encode() for record in chunk
)
Expand Down
10 changes: 2 additions & 8 deletions singer_sdk/helpers/_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,9 @@

import datetime
import sys
from importlib import resources as importlib_resources

if sys.version_info < (3, 9):
import importlib_resources
else:
from importlib import resources as importlib_resources

if sys.version_info < (3, 9):
from importlib_resources.abc import Traversable
elif sys.version_info < (3, 12):
if sys.version_info < (3, 12):
from importlib.abc import Traversable
else:
from importlib.resources.abc import Traversable
Expand Down
8 changes: 2 additions & 6 deletions singer_sdk/helpers/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,10 @@

import sys
import typing as t
from collections.abc import Mapping

import requests

if sys.version_info < (3, 9):
from typing import Mapping # noqa: ICN003
else:
from collections.abc import Mapping

if sys.version_info < (3, 10):
from typing_extensions import TypeAlias
else:
Expand All @@ -24,5 +20,5 @@
]

Context: TypeAlias = Mapping[str, t.Any]
Record: TypeAlias = t.Dict[str, t.Any]
Record: TypeAlias = dict[str, t.Any]
Auth: TypeAlias = t.Callable[[requests.PreparedRequest], requests.PreparedRequest]
2 changes: 1 addition & 1 deletion singer_sdk/mapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def md5(string: str) -> str:
return hashlib.md5(string.encode("utf-8")).hexdigest() # noqa: S324


StreamMapsDict: TypeAlias = t.Dict[str, t.Union[str, dict, None]]
StreamMapsDict: TypeAlias = dict[str, t.Union[str, dict, None]]


class StreamMap(metaclass=abc.ABCMeta):
Expand Down
16 changes: 8 additions & 8 deletions singer_sdk/sinks/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -739,10 +739,10 @@ def process_batch_files(
storage = StorageTarget.from_url(head)

if encoding.format == BatchFileFormat.JSONL:
with storage.fs(create=False) as batch_fs, batch_fs.open(
tail,
mode="rb",
) as file:
with (
storage.fs(create=False) as batch_fs,
batch_fs.open(tail, mode="rb") as file,
):
if encoding.compression == "gzip":
with gzip_open(file) as context_file:
context = {
Expand All @@ -759,10 +759,10 @@ def process_batch_files(
):
import pyarrow.parquet as pq # noqa: PLC0415

with storage.fs(create=False) as batch_fs, batch_fs.open(
tail,
mode="rb",
) as file:
with (
storage.fs(create=False) as batch_fs,
batch_fs.open(tail, mode="rb") as file,
):
table = pq.read_table(file)
context = {"records": table.to_pylist()}
self.process_batch(context)
Expand Down
2 changes: 1 addition & 1 deletion singer_sdk/testing/tap_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def test(self) -> None:
catalog = tap1.catalog_dict
# Reset and re-initialize with discovered catalog
kwargs = {k: v for k, v in self.runner.default_kwargs.items() if k != "catalog"}
tap2: Tap = t.cast(t.Type[Tap], self.runner.singer_class)(
tap2: Tap = t.cast(type[Tap], self.runner.singer_class)(
config=self.runner.config,
catalog=catalog,
**kwargs,
Expand Down
5 changes: 3 additions & 2 deletions tests/core/configuration/test_dict_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,9 @@ def test_get_env_var_config(
assert not set.intersection(missing_props, env_config)

m.setenv("PLUGIN_TEST_PROP3", "val1,val2")
with subtests.test(msg="Legacy array parsing"), caplog.at_level(
logging.WARNING,
with (
subtests.test(msg="Legacy array parsing"),
caplog.at_level(logging.WARNING),
):
parsed = parse_environment_config(CONFIG_JSONSCHEMA, "PLUGIN_TEST_")
assert parsed["prop3"] == ["val1", "val2"]
Expand Down
Loading

0 comments on commit ea3ee96

Please # to comment.