Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-aioftp for openSUSE:Factory checked in at 2025-06-26 11:39:02 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-aioftp (Old) and /work/SRC/openSUSE:Factory/.python-aioftp.new.7067 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-aioftp" Thu Jun 26 11:39:02 2025 rev:13 rq:1288510 version:0.25.2 Changes: -------- --- /work/SRC/openSUSE:Factory/python-aioftp/python-aioftp.changes 2025-06-03 19:10:46.041988060 +0200 +++ /work/SRC/openSUSE:Factory/.python-aioftp.new.7067/python-aioftp.changes 2025-06-26 11:40:16.216140564 +0200 @@ -1,0 +2,7 @@ +Wed Jun 25 09:35:11 UTC 2025 - John Paul Adrian Glaubitz <adrian.glaub...@suse.com> + +- Update to version 0.25.2 + * adjust codebase for python 3.14 (#190) + * client: resolve tls issues (fixes #186) (#187) + +------------------------------------------------------------------- Old: ---- aioftp-0.25.1.tar.gz New: ---- aioftp-0.25.2.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-aioftp.spec ++++++ --- /var/tmp/diff_new_pack.PPrYqm/_old 2025-06-26 11:40:16.984172425 +0200 +++ /var/tmp/diff_new_pack.PPrYqm/_new 2025-06-26 11:40:16.984172425 +0200 @@ -18,7 +18,7 @@ %{?sle15_python_module_pythons} Name: python-aioftp -Version: 0.25.1 +Version: 0.25.2 Release: 0 Summary: FTP client/server for asyncio License: Apache-2.0 ++++++ aioftp-0.25.1.tar.gz -> aioftp-0.25.2.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioftp-0.25.1/PKG-INFO new/aioftp-0.25.2/PKG-INFO --- old/aioftp-0.25.1/PKG-INFO 2025-04-11 20:53:50.560357600 +0200 +++ new/aioftp-0.25.2/PKG-INFO 2025-06-13 16:44:02.607434700 +0200 @@ -1,8 +1,8 @@ Metadata-Version: 2.4 Name: aioftp -Version: 0.25.1 +Version: 0.25.2 Summary: ftp client/server for asyncio -Author: yieyu, rsichnyi, jw4js, oleksandr-kuzmenko, ndhansen, modelmat, greut, PonyPC, jacobtomlinson, bachya, CrafterKolyan, jkr78 +Author: yieyu, rsichnyi, jw4js, oleksandr-kuzmenko, ndhansen, modelmat, greut, PonyPC, jacobtomlinson, bachya, CrafterKolyan, jkr78, sakosha Author-email: pohmelie <multisosnoo...@gmail.com>, asvetlov <andrew.svet...@gmail.com>, decaz <deca...@gmail.com>, janneronkko <janne.ron...@iki.fi>, thirtyseven <t...@shlashdot.org>, ported-pw <cont...@ported.pw>, Olegt0rr <t...@mail.ru>, michalc <mic...@charemza.name>, ch3pjw <p...@concertdaw.co.uk>, puddly <pudd...@gmail.com>, AMDmi3 <amd...@amdmi3.ru>, webknjaz <webknjaz+github/prof...@redhat.com>, rcfox <r...@rcfox.ca>, bellini666 <thi...@bellini.dev> License: Apache License Version 2.0, January 2004 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioftp-0.25.1/history.rst new/aioftp-0.25.2/history.rst --- old/aioftp-0.25.1/history.rst 2025-04-11 20:53:44.000000000 +0200 +++ new/aioftp-0.25.2/history.rst 2025-06-13 16:43:58.000000000 +0200 @@ -1,5 +1,12 @@ x.x.x (xxxx-xx-xx) +0.25.2 (2025-06-13) +------------------- +- adjust codebase for python 3.14 (#190) +- client: resolve tls issues (fixes #186) (#187) +Thanks to `sakosha <https://github.com/sakosha>`_ + + 0.25.1 (2025-04-11) ------------------- - client: do not start explicit tls if implicit mode used (#184) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioftp-0.25.1/pyproject.toml new/aioftp-0.25.2/pyproject.toml --- old/aioftp-0.25.1/pyproject.toml 2025-04-11 20:53:44.000000000 +0200 +++ new/aioftp-0.25.2/pyproject.toml 2025-06-13 16:43:58.000000000 +0200 @@ -1,6 +1,6 @@ [project] name = "aioftp" -version = "0.25.1" +version = "0.25.2" description = "ftp client/server for asyncio" readme = "README.rst" requires-python = ">= 3.9" @@ -32,6 +32,7 @@ {name = "webknjaz", email="webknjaz+github/prof...@redhat.com"}, {name = "rcfox", email="r...@rcfox.ca"}, {name = "bellini666", email="thi...@bellini.dev"}, + {name = "sakosha"}, ] classifiers = [ "Programming Language :: Python", @@ -76,6 +77,8 @@ target-version = "py39" lint.select = ["E", "W", "F", "Q", "UP", "I", "ASYNC"] src = ["src"] +exclude = ["ftpbench.py"] + [tool.coverage] run.source = ["./src/aioftp"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioftp-0.25.1/src/aioftp/client.py new/aioftp-0.25.2/src/aioftp/client.py --- old/aioftp-0.25.1/src/aioftp/client.py 2025-04-11 20:53:44.000000000 +0200 +++ new/aioftp-0.25.2/src/aioftp/client.py 2025-06-13 16:43:58.000000000 +0200 @@ -7,7 +7,6 @@ import pathlib import re import ssl -from functools import partial from . import errors, pathio from .common import ( @@ -139,9 +138,20 @@ self.parse_list_line_custom = parse_list_line_custom self.parse_list_line_custom_first = parse_list_line_custom_first self._passive_commands = passive_commands - self._open_connection = partial(open_connection, ssl=self.ssl, **siosocks_asyncio_kwargs) - self._upgraded_to_tls = False - self._logged_in = False + self._siosocks_asyncio_kwargs = siosocks_asyncio_kwargs + + async def _open_connection(self, host, port): + ssl_resolved = self.ssl + if self.stream is not None: + ssl_object = self.stream.writer.transport.get_extra_info("ssl_object") + if ssl_object is not None: + ssl_resolved = SSLSessionBoundContext( + ssl.PROTOCOL_TLS_CLIENT, + context=ssl_object.context, + session=ssl_object.session, + ) + connection = await open_connection(host, port, ssl=ssl_resolved, **self._siosocks_asyncio_kwargs) + return connection async def connect(self, host, port=DEFAULT_PORT): self.server_host = host @@ -423,8 +433,7 @@ if diff > TWO_YEARS_IN_SECONDS: d = d.replace(year=prev_leap_year + 4) else: - d = datetime.datetime.strptime(s, "%b %d %H:%M") - d = d.replace(year=now.year) + d = datetime.datetime.strptime(f"{now.year} {s}", "%Y %b %d %H:%M") diff = (now - d).total_seconds() if diff > HALF_OF_YEAR_IN_SECONDS: d = d.replace(year=now.year + 1) @@ -663,22 +672,13 @@ :type sslcontext: :py:class:`ssl.SSLContext` """ if self.ssl: - raise RuntimeError("SSL context is already set in implicit mode, can't use explicit mode") - - if self._upgraded_to_tls: - return + raise RuntimeError("ssl context is already set and used implicitly") await self.command("AUTH TLS", "234") - - if sslcontext: - self.ssl = sslcontext - elif not isinstance(self.ssl, ssl.SSLContext): - self.ssl = ssl.create_default_context() - - await self.stream.start_tls(sslcontext=self.ssl, server_hostname=self.server_host) - - if self._logged_in: - await self._send_tls_protection_commands() + if sslcontext is None: + sslcontext = ssl.create_default_context() + await self.stream.start_tls(sslcontext=sslcontext, server_hostname=self.server_host) + await self._send_tls_protection_commands() async def login( self, @@ -718,11 +718,6 @@ censor_after=censor_after, ) - self._logged_in = True - - if self._upgraded_to_tls: - await self._send_tls_protection_commands() - async def get_current_directory(self): """ :py:func:`asyncio.coroutine` @@ -1239,18 +1234,6 @@ throttles={"_": self.throttle}, timeout=self.socket_timeout, ) - - ssl_object = self.stream.writer.transport.get_extra_info("ssl_object") - if ssl_object and not self.ssl: - await writer.start_tls( - sslcontext=SSLSessionBoundContext( - ssl.PROTOCOL_TLS_CLIENT, - context=ssl_object.context, - session=ssl_object.session, - ), - server_hostname=self.server_host, - ) - return stream async def abort(self, *, wait=True): @@ -1276,6 +1259,7 @@ user=DEFAULT_USER, password=DEFAULT_PASSWORD, account=DEFAULT_ACCOUNT, + upgrade_to_tls=False, **kwargs, ): """ @@ -1310,6 +1294,8 @@ client = cls(**kwargs) try: await client.connect(host, port) + if upgrade_to_tls: + await client.upgrade_to_tls() await client.login(user, password, account) except Exception: client.close() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioftp-0.25.1/src/aioftp.egg-info/PKG-INFO new/aioftp-0.25.2/src/aioftp.egg-info/PKG-INFO --- old/aioftp-0.25.1/src/aioftp.egg-info/PKG-INFO 2025-04-11 20:53:50.000000000 +0200 +++ new/aioftp-0.25.2/src/aioftp.egg-info/PKG-INFO 2025-06-13 16:44:02.000000000 +0200 @@ -1,8 +1,8 @@ Metadata-Version: 2.4 Name: aioftp -Version: 0.25.1 +Version: 0.25.2 Summary: ftp client/server for asyncio -Author: yieyu, rsichnyi, jw4js, oleksandr-kuzmenko, ndhansen, modelmat, greut, PonyPC, jacobtomlinson, bachya, CrafterKolyan, jkr78 +Author: yieyu, rsichnyi, jw4js, oleksandr-kuzmenko, ndhansen, modelmat, greut, PonyPC, jacobtomlinson, bachya, CrafterKolyan, jkr78, sakosha Author-email: pohmelie <multisosnoo...@gmail.com>, asvetlov <andrew.svet...@gmail.com>, decaz <deca...@gmail.com>, janneronkko <janne.ron...@iki.fi>, thirtyseven <t...@shlashdot.org>, ported-pw <cont...@ported.pw>, Olegt0rr <t...@mail.ru>, michalc <mic...@charemza.name>, ch3pjw <p...@concertdaw.co.uk>, puddly <pudd...@gmail.com>, AMDmi3 <amd...@amdmi3.ru>, webknjaz <webknjaz+github/prof...@redhat.com>, rcfox <r...@rcfox.ca>, bellini666 <thi...@bellini.dev> License: Apache License Version 2.0, January 2004 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioftp-0.25.1/tests/test_file.py new/aioftp-0.25.2/tests/test_file.py --- old/aioftp-0.25.1/tests/test_file.py 2025-04-11 20:53:44.000000000 +0200 +++ new/aioftp-0.25.2/tests/test_file.py 2025-06-13 16:43:58.000000000 +0200 @@ -241,7 +241,7 @@ @pytest.mark.asyncio async def test_stat_mlst(pair_factory): async with pair_factory() as pair: - now = dt.datetime.utcnow() + now = dt.datetime.now(tz=dt.timezone.utc).replace(tzinfo=None) await pair.make_server_files("foo") info = await pair.client.stat("foo") assert info["type"] == "file" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioftp-0.25.1/tests/test_simple_functions.py new/aioftp-0.25.2/tests/test_simple_functions.py --- old/aioftp-0.25.1/tests/test_simple_functions.py 2025-04-11 20:53:44.000000000 +0200 +++ new/aioftp-0.25.2/tests/test_simple_functions.py 2025-06-13 16:43:58.000000000 +0200 @@ -14,14 +14,16 @@ assert parsed == pathlib.PurePosixPath('baz " test nop') -def test_connection_del_future(): +@pytest.mark.asyncio +async def test_connection_del_future(): loop = asyncio.new_event_loop() c = aioftp.Connection(loop=loop) c.foo = "bar" del c.future.foo -def test_connection_not_in_storage(): +@pytest.mark.asyncio +async def test_connection_not_in_storage(): loop = asyncio.new_event_loop() c = aioftp.Connection(loop=loop) with pytest.raises(AttributeError): @@ -244,7 +246,8 @@ assert b(past, now) == "Jan 1 2001" -def test_get_paths_windows_traverse(): +@pytest.mark.asyncio +async def test_get_paths_windows_traverse(): base_path = pathlib.PureWindowsPath("C:\\ftp") user = aioftp.User() user.base_path = base_path diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioftp-0.25.1/tests/test_tls.py new/aioftp-0.25.2/tests/test_tls.py --- old/aioftp-0.25.1/tests/test_tls.py 2025-04-11 20:53:44.000000000 +0200 +++ new/aioftp-0.25.2/tests/test_tls.py 2025-06-13 16:43:58.000000000 +0200 @@ -6,14 +6,18 @@ pytest.skip(reason="required python 3.11+", allow_module_level=True) -async def _auth_response(connection, rest): - connection.response("234", ":P") - return True +def simple_response(code, message): + async def command(connection, rest): + connection.response(code, message) + return True + + return command -async def _ok_response(connection, rest): - connection.response("200", ":P") - return True +AUTH_RESPONSE = simple_response("234", ":P") +PBSZ_RESPONSE = simple_response("200", ":P") +PROT_RESPONSE = simple_response("200", ":P") +OK_RESPONSE = simple_response("200", ":P") @pytest.mark.asyncio @@ -22,7 +26,9 @@ create_default_context = mocker.patch("aioftp.client.ssl.create_default_context", return_value=ssl_context) async with pair_factory(logged=False, do_quit=False) as pair: - pair.server.commands_mapping["auth"] = _auth_response + pair.server.commands_mapping["auth"] = AUTH_RESPONSE + pair.server.commands_mapping["pbsz"] = PBSZ_RESPONSE + pair.server.commands_mapping["prot"] = PROT_RESPONSE start_tls = mocker.patch.object(pair.client.stream, "start_tls") command_spy = mocker.spy(pair.client, "command") @@ -31,7 +37,14 @@ create_default_context.assert_called_once_with() start_tls.assert_called_once_with(sslcontext=ssl_context, server_hostname=pair.client.server_host) - command_spy.assert_called_once_with("AUTH TLS", "234") + assert command_spy.call_count == 3 + command_spy.assert_has_calls( + [ + mocker.call("AUTH TLS", "234"), + mocker.call("PBSZ 0", "200"), + mocker.call("PROT P", "200"), + ], + ) @pytest.mark.asyncio @@ -40,7 +53,9 @@ create_default_context = mocker.patch("aioftp.client.ssl.create_default_context") async with pair_factory(logged=False, do_quit=False) as pair: - pair.server.commands_mapping["auth"] = _auth_response + pair.server.commands_mapping["auth"] = AUTH_RESPONSE + pair.server.commands_mapping["pbsz"] = PBSZ_RESPONSE + pair.server.commands_mapping["prot"] = PROT_RESPONSE start_tls = mocker.patch.object(pair.client.stream, "start_tls") command_spy = mocker.spy(pair.client, "command") @@ -49,40 +64,7 @@ create_default_context.assert_not_called() start_tls.assert_called_once_with(sslcontext=ssl_context, server_hostname=pair.client.server_host) - command_spy.assert_called_once_with("AUTH TLS", "234") - - -@pytest.mark.asyncio -async def test_upgrade_to_tls_does_nothing_when_already_updated(mocker, pair_factory): - mocker.patch("aioftp.client.ssl.create_default_context") - - async with pair_factory(logged=False, do_quit=False) as pair: - pair.client._upgraded_to_tls = True - - start_tls = mocker.patch.object(pair.client.stream, "start_tls") - command_spy = mocker.spy(pair.client, "command") - - await pair.client.upgrade_to_tls() - - start_tls.assert_not_called() - command_spy.assert_not_called() - - -@pytest.mark.asyncio -async def test_upgrade_to_tls_when_logged_in(mocker, pair_factory): - mocker.patch("aioftp.client.ssl.create_default_context") - - async with pair_factory(logged=False, do_quit=False) as pair: - pair.server.commands_mapping["auth"] = _auth_response - pair.server.commands_mapping["pbsz"] = _ok_response - pair.server.commands_mapping["prot"] = _ok_response - pair.client._logged_in = True - - mocker.patch.object(pair.client.stream, "start_tls") - command_spy = mocker.spy(pair.client, "command") - - await pair.client.upgrade_to_tls() - + assert command_spy.call_count == 3 command_spy.assert_has_calls( [ mocker.call("AUTH TLS", "234"), @@ -93,20 +75,22 @@ @pytest.mark.asyncio -async def test_login_should_send_tls_protection_when_upgraded(mocker, pair_factory): +async def test_upgrade_to_tls_when_logged_in(mocker, pair_factory): mocker.patch("aioftp.client.ssl.create_default_context") async with pair_factory(logged=False, do_quit=False) as pair: - pair.server.commands_mapping["pbsz"] = _ok_response - pair.server.commands_mapping["prot"] = _ok_response - pair.client._upgraded_to_tls = True + pair.server.commands_mapping["auth"] = AUTH_RESPONSE + pair.server.commands_mapping["pbsz"] = OK_RESPONSE + pair.server.commands_mapping["prot"] = OK_RESPONSE + mocker.patch.object(pair.client.stream, "start_tls") command_spy = mocker.spy(pair.client, "command") - await pair.client.login("foo", "bar") + await pair.client.upgrade_to_tls() command_spy.assert_has_calls( [ + mocker.call("AUTH TLS", "234"), mocker.call("PBSZ 0", "200"), mocker.call("PROT P", "200"), ],