diff --git a/python-smart-open.changes b/python-smart-open.changes index 24c6924..57dc732 100644 --- a/python-smart-open.changes +++ b/python-smart-open.changes @@ -1,3 +1,12 @@ +------------------------------------------------------------------- +Mon Dec 16 03:17:47 UTC 2024 - Steve Kowalik + +- Remove upper pin on urllib3. +- Drop moto_server set up and teardown, it is for one testcase. +- Drop patch skip-gzip-tests-python312.patch, not required. +- Add patch support-python-313.patch: + * Support Python 3.13 changes. + ------------------------------------------------------------------- Wed Oct 30 19:30:36 UTC 2024 - Dirk Müller diff --git a/python-smart-open.spec b/python-smart-open.spec index a7a69c6..7cf0c1c 100644 --- a/python-smart-open.spec +++ b/python-smart-open.spec @@ -23,7 +23,8 @@ Summary: Python utils for streaming large files License: MIT URL: https://github.com/piskvorky/smart_open Source: https://github.com/piskvorky/smart_open/archive/refs/tags/v%{version}.tar.gz#/smart_open-%{version}.tar.gz -Patch0: skip-gzip-tests-python312.patch +# PATCH-FIX-UPSTREAM gh#piskvorky/smart_open#847 +Patch0: support-python-313.patch BuildRequires: %{python_module pip} BuildRequires: %{python_module setuptools} BuildRequires: %{python_module wheel} @@ -39,9 +40,6 @@ Requires: python-wrapt Requires: python-zstandard Suggests: python-paramiko BuildArch: noarch -# see https://github.com/piskvorky/smart_open/issues/784 -BuildRequires: %{python_module urllib3 < 2} -Requires: python-urllib3 < 2 # SECTION test requirements BuildRequires: %{python_module azure-common} BuildRequires: %{python_module azure-core} @@ -74,12 +72,7 @@ Includes support for S3, HDFS, gzip, bz2, etc. %python_expand %fdupes %{buildroot}%{$python_sitelib} %check -moto_server -p5000 2>/dev/null & -server_pid=$! -export SO_ENABLE_MOTO_SERVER=1 -# Requires network -%pytest -rs -k 'not (test_http_gz or test_s3_gzip_compress_sanity)' smart_open/ -kill $server_pid +%pytest %files %{python_files} %doc README.rst diff --git a/skip-gzip-tests-python312.patch b/skip-gzip-tests-python312.patch deleted file mode 100644 index 530ed40..0000000 --- a/skip-gzip-tests-python312.patch +++ /dev/null @@ -1,48 +0,0 @@ -Index: smart_open-7.0.1/smart_open/tests/test_smart_open.py -=================================================================== ---- smart_open-7.0.1.orig/smart_open/tests/test_smart_open.py -+++ smart_open-7.0.1/smart_open/tests/test_smart_open.py -@@ -20,6 +20,7 @@ import tempfile - import unittest - from unittest import mock - import warnings -+import sys - - import boto3 - import pytest -@@ -1795,6 +1796,8 @@ def test_s3_gzip_compress_sanity(): - ) - def test_s3_read_explicit(url, _compression): - """Can we read using the explicitly specified compression?""" -+ if sys.version_info.minor == 12 and _compression == ".gz": -+ raise unittest.SkipTest - initialize_bucket() - with smart_open.open(url, 'rb', compression=_compression) as fin: - assert fin.read() == _DECOMPRESSED_DATA -@@ -1811,6 +1814,8 @@ def test_s3_read_explicit(url, _compress - ) - def test_s3_write_explicit(_compression, expected): - """Can we write using the explicitly specified compression?""" -+ if sys.version_info.minor == 12 and _compression == ".gz": -+ raise unittest.SkipTest - initialize_bucket() - - with smart_open.open("s3://bucket/key", "wb", compression=_compression) as fout: -@@ -1831,6 +1836,8 @@ def test_s3_write_explicit(_compression, - ) - def test_s3_write_implicit(url, _compression, expected): - """Can we determine the compression from the file extension?""" -+ if sys.version_info.minor == 12 and _compression == ".gz": -+ raise unittest.SkipTest - initialize_bucket() - - with smart_open.open(url, "wb", compression=INFER_FROM_EXTENSION) as fout: -@@ -1851,6 +1858,8 @@ def test_s3_write_implicit(url, _compres - ) - def test_s3_disable_compression(url, _compression, expected): - """Can we handle the compression parameter when reading/writing?""" -+ if sys.version_info.minor == 12 and _compression == ".gz": -+ raise unittest.SkipTest - initialize_bucket() - - with smart_open.open(url, "wb") as fout: diff --git a/support-python-313.patch b/support-python-313.patch new file mode 100644 index 0000000..9217d7e --- /dev/null +++ b/support-python-313.patch @@ -0,0 +1,322 @@ +From 1266ddb5d671d08ba1da45db963d0102de00cae4 Mon Sep 17 00:00:00 2001 +From: ddelange <14880945+ddelange@users.noreply.github.com> +Date: Sun, 15 Dec 2024 09:28:01 +0100 +Subject: [PATCH 1/2] Add cp313 to CI + +--- + .github/workflows/python-package.yml | 4 ++++ + setup.py | 2 ++ + 2 files changed, 6 insertions(+) + +diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml +index 58302d60..be6a7228 100644 +--- a/.github/workflows/python-package.yml ++++ b/.github/workflows/python-package.yml +@@ -31,6 +31,7 @@ jobs: + - {python-version: '3.10', os: ubuntu-20.04} + - {python-version: '3.11', os: ubuntu-20.04} + - {python-version: '3.12', os: ubuntu-20.04} ++ - {python-version: '3.13', os: ubuntu-20.04} + + - {python-version: '3.8', os: windows-2019} + - {python-version: '3.9', os: windows-2019} +@@ -67,6 +68,7 @@ jobs: + - {python-version: '3.10', os: ubuntu-20.04} + - {python-version: '3.11', os: ubuntu-20.04} + - {python-version: '3.12', os: ubuntu-20.04} ++ - {python-version: '3.13', os: ubuntu-20.04} + + # + # Some of the doctests don't pass on Windows because of Windows-specific +@@ -104,6 +106,7 @@ jobs: + - {python-version: '3.10', os: ubuntu-20.04} + - {python-version: '3.11', os: ubuntu-20.04} + - {python-version: '3.12', os: ubuntu-20.04} ++ - {python-version: '3.13', os: ubuntu-20.04} + + # Not sure why we exclude these, perhaps for historical reasons? + # +@@ -153,6 +156,7 @@ jobs: + - {python-version: '3.10', os: ubuntu-20.04} + - {python-version: '3.11', os: ubuntu-20.04} + - {python-version: '3.12', os: ubuntu-20.04} ++ - {python-version: '3.13', os: ubuntu-20.04} + + # - {python-version: '3.7', os: windows-2019} + # - {python-version: '3.8', os: windows-2019} +diff --git a/setup.py b/setup.py +index 9e738bea..d099c283 100644 +--- a/setup.py ++++ b/setup.py +@@ -102,6 +102,8 @@ def read(fname): + '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', + 'Topic :: System :: Distributed Computing', + 'Topic :: Database :: Front-Ends', + ], + +From c3105cc3d326cdfc50b6d0cb3d1544ca8522be29 Mon Sep 17 00:00:00 2001 +From: ddelange <14880945+ddelange@users.noreply.github.com> +Date: Sun, 15 Dec 2024 23:42:55 +0100 +Subject: [PATCH 2/2] Fix tests + +--- + .github/workflows/python-package.yml | 10 ++++++ + smart_open/azure.py | 52 +++++++++++++++++----------- + smart_open/hdfs.py | 22 +++++++++--- + smart_open/tests/test_azure.py | 10 ++++++ + 4 files changed, 69 insertions(+), 25 deletions(-) + +diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml +index be6a7228..abd387cf 100644 +--- a/.github/workflows/python-package.yml ++++ b/.github/workflows/python-package.yml +@@ -38,6 +38,7 @@ jobs: + - {python-version: '3.10', os: windows-2019} + - {python-version: '3.11', os: windows-2019} + - {python-version: '3.12', os: windows-2019} ++ - {python-version: '3.13', os: windows-2019} + steps: + - uses: actions/checkout@v2 + +@@ -78,6 +79,9 @@ jobs: + # - {python-version: '3.8', os: windows-2019} + # - {python-version: '3.9', os: windows-2019} + # - {python-version: '3.10', os: windows-2019} ++ # - {python-version: '3.11', os: windows-2019} ++ # - {python-version: '3.12', os: windows-2019} ++ # - {python-version: '3.13', os: windows-2019} + + steps: + - uses: actions/checkout@v2 +@@ -114,6 +118,9 @@ jobs: + # - {python-version: '3.8', os: windows-2019} + # - {python-version: '3.9', os: windows-2019} + # - {python-version: '3.10', os: windows-2019} ++ # - {python-version: '3.11', os: windows-2019} ++ # - {python-version: '3.12', os: windows-2019} ++ # - {python-version: '3.13', os: windows-2019} + + steps: + - uses: actions/checkout@v2 +@@ -162,6 +169,9 @@ jobs: + # - {python-version: '3.8', os: windows-2019} + # - {python-version: '3.9', os: windows-2019} + # - {python-version: '3.10', os: windows-2019} ++ # - {python-version: '3.11', os: windows-2019} ++ # - {python-version: '3.12', os: windows-2019} ++ # - {python-version: '3.13', os: windows-2019} + + steps: + - uses: actions/checkout@v2 +diff --git a/smart_open/azure.py b/smart_open/azure.py +index 1c991f05..f467992c 100644 +--- a/smart_open/azure.py ++++ b/smart_open/azure.py +@@ -195,8 +195,9 @@ class Reader(io.BufferedIOBase): + Implements the io.BufferedIOBase interface of the standard library. + + :raises azure.core.exceptions.ResourceNotFoundError: Raised when the blob to read from does not exist. +- + """ ++ _blob = None # always initialized so closed property is functional in case _get_blob_client fails ++ + def __init__( + self, + container, +@@ -207,9 +208,10 @@ def __init__( + max_concurrency=DEFAULT_MAX_CONCURRENCY, + ): + self._container_name = container ++ self._blob_name = blob + +- self._blob = _get_blob_client(client, container, blob) + # type: azure.storage.blob.BlobClient ++ self._blob = _get_blob_client(client, container, blob) + + if self._blob is None: + raise azure.core.exceptions.ResourceNotFoundError( +@@ -236,8 +238,13 @@ def __init__( + def close(self): + """Flush and close this stream.""" + logger.debug("close: called") +- self._blob = None +- self._raw_reader = None ++ if not self.closed: ++ self._blob = None ++ self._raw_reader = None ++ ++ @property ++ def closed(self): ++ return self._blob is None + + def readable(self): + """Return True if the stream can be read from.""" +@@ -369,20 +376,26 @@ def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + + def __str__(self): +- return "(%s, %r, %r)" % (self.__class__.__name__, +- self._container_name, +- self._blob.blob_name) ++ return "(%s, %r, %r)" % ( ++ self.__class__.__name__, ++ self._container_name, ++ self._blob_name ++ ) + + def __repr__(self): + return "%s(container=%r, blob=%r)" % ( +- self.__class__.__name__, self._container_name, self._blob.blob_name, ++ self.__class__.__name__, ++ self._container_name, ++ self._blob_name, + ) + + + class Writer(io.BufferedIOBase): + """Writes bytes to Azure Blob Storage. + +- Implements the io.BufferedIOBase interface of the standard library.""" ++ Implements the io.BufferedIOBase interface of the standard library. ++ """ ++ _blob = None # always initialized so closed property is functional in case _get_blob_client fails + + def __init__( + self, +@@ -392,21 +405,19 @@ def __init__( + blob_kwargs=None, + min_part_size=_DEFAULT_MIN_PART_SIZE, + ): +- self._is_closed = False + self._container_name = container +- +- self._blob = _get_blob_client(client, container, blob) ++ self._blob_name = blob + self._blob_kwargs = blob_kwargs or {} +- # type: azure.storage.blob.BlobClient +- + self._min_part_size = min_part_size +- + self._total_size = 0 + self._total_parts = 0 + self._bytes_uploaded = 0 + self._current_part = io.BytesIO() + self._block_list = [] + ++ # type: azure.storage.blob.BlobClient ++ self._blob = _get_blob_client(client, container, blob) ++ + # + # This member is part of the io.BufferedIOBase interface. + # +@@ -424,25 +435,26 @@ def terminate(self): + logger.debug('%s: terminating multipart upload', self) + if not self.closed: + self._block_list = [] +- self._is_closed = True ++ self._blob = None + logger.debug('%s: terminated multipart upload', self) + + # + # Override some methods from io.IOBase. + # + def close(self): ++ logger.debug("close: called") + if not self.closed: + logger.debug('%s: completing multipart upload', self) + if self._current_part.tell() > 0: + self._upload_part() + self._blob.commit_block_list(self._block_list, **self._blob_kwargs) + self._block_list = [] +- self._is_closed = True ++ self._blob = None + logger.debug('%s: completed multipart upload', self) + + @property + def closed(self): +- return self._is_closed ++ return self._blob is None + + def writable(self): + """Return True if the stream supports writing.""" +@@ -528,13 +540,13 @@ def __str__(self): + return "(%s, %r, %r)" % ( + self.__class__.__name__, + self._container_name, +- self._blob.blob_name ++ self._blob_name + ) + + def __repr__(self): + return "%s(container=%r, blob=%r, min_part_size=%r)" % ( + self.__class__.__name__, + self._container_name, +- self._blob.blob_name, ++ self._blob_name, + self._min_part_size + ) +diff --git a/smart_open/hdfs.py b/smart_open/hdfs.py +index a247d3e3..772d7591 100644 +--- a/smart_open/hdfs.py ++++ b/smart_open/hdfs.py +@@ -84,8 +84,13 @@ def __init__(self, uri): + def close(self): + """Flush and close this stream.""" + logger.debug("close: called") +- self._sub.terminate() +- self._sub = None ++ if not self.closed: ++ self._sub.terminate() ++ self._sub = None ++ ++ @property ++ def closed(self): ++ return self._sub is None + + def readable(self): + """Return True if the stream can be read from.""" +@@ -136,9 +141,16 @@ def __init__(self, uri): + self.raw = None + + def close(self): +- self.flush() +- self._sub.stdin.close() +- self._sub.wait() ++ logger.debug("close: called") ++ if not self.closed: ++ self.flush() ++ self._sub.stdin.close() ++ self._sub.wait() ++ self._sub = None ++ ++ @property ++ def closed(self): ++ return self._sub is None + + def flush(self): + self._sub.stdin.flush() +diff --git a/smart_open/tests/test_azure.py b/smart_open/tests/test_azure.py +index a82dbf17..2eb23a0e 100644 +--- a/smart_open/tests/test_azure.py ++++ b/smart_open/tests/test_azure.py +@@ -554,6 +554,16 @@ def test_read_blob_client(self): + + assert data == content + ++ def test_nonexisting_container(self): ++ with self.assertRaises(azure.core.exceptions.ResourceNotFoundError): ++ with smart_open.azure.open( ++ 'thiscontainerdoesntexist', ++ 'mykey', ++ 'rb', ++ CLIENT ++ ) as fin: ++ fin.read() ++ + + class WriterTest(unittest.TestCase): + """Test writing into Azure Blob files."""