From 4af71de37efb2eb3e904450bc4ffc433535c556e Mon Sep 17 00:00:00 2001 From: jcardozo-eth Date: Fri, 27 Feb 2026 16:48:40 +0100 Subject: [PATCH 1/4] fix: replace deprecated datetime.utcnow() with timezone-aware alternative datetime.utcnow() has been deprecated since Python 3.12 and will be removed in a future version. Replace with datetime.now(timezone.utc), stripping tzinfo to maintain compatibility with the naive-datetime convention used throughout the codebase (enforced by the assertion in datetime_to_datestamp()). --- src/oaipmh/server.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/oaipmh/server.py b/src/oaipmh/server.py index ae843e8..ebeb66f 100644 --- a/src/oaipmh/server.py +++ b/src/oaipmh/server.py @@ -1,6 +1,6 @@ from lxml.etree import ElementTree, Element, SubElement from lxml import etree -from datetime import datetime +from datetime import datetime, timezone try: from urllib.parse import urlencode, quote, unquote except ImportError: @@ -174,7 +174,7 @@ def _outputBasicEnvelope(self, **kw): e_responseDate = SubElement(e_oaipmh, nsoai('responseDate')) # date should be first possible moment e_responseDate.text = datetime_to_datestamp( - datetime.utcnow().replace(microsecond=0)) + datetime.now(timezone.utc).replace(microsecond=0, tzinfo=None)) e_request = SubElement(e_oaipmh, nsoai('request')) for key, value in kw.items(): if key == 'from_': From d1e40677796efdca8a44bf345ea34ad1690c7a61 Mon Sep 17 00:00:00 2001 From: jcardozo-eth Date: Fri, 27 Feb 2026 16:51:46 +0100 Subject: [PATCH 2/4] chore: remove Python 2 compatibility imports from test files Remove dead try/except import blocks where the except branch can never execute on Python >= 3.10. Replace with direct imports of the Python 3 standard library modules. --- src/oaipmh/tests/fakeclient.py | 5 +---- src/oaipmh/tests/test_client.py | 13 +++---------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/src/oaipmh/tests/fakeclient.py b/src/oaipmh/tests/fakeclient.py index 6fba0fc..38047c0 100644 --- a/src/oaipmh/tests/fakeclient.py +++ b/src/oaipmh/tests/fakeclient.py @@ -1,10 +1,7 @@ from oaipmh import client, common import os.path from datetime import datetime -try: - from urllib.parse import urlencode -except ImportError: - from urllib import urlencode +from urllib.parse import urlencode class FakeClient(client.BaseClient): diff --git a/src/oaipmh/tests/test_client.py b/src/oaipmh/tests/test_client.py index 2b5a513..7b3851f 100644 --- a/src/oaipmh/tests/test_client.py +++ b/src/oaipmh/tests/test_client.py @@ -1,18 +1,11 @@ from unittest import TestCase -try: - from unittest import mock -except ImportError: # python < 3.3 - import mock +from unittest import mock from .fakeclient import FakeClient, GranularityFakeClient, TestError import os from datetime import datetime -try: - import urllib.request as urllib2 - URLOPEN_PATH = 'urllib.request.urlopen' -except ImportError: - import urllib2 - URLOPEN_PATH = 'urllib2.urlopen' +import urllib.request as urllib2 +URLOPEN_PATH = 'urllib.request.urlopen' from oaipmh import common, metadata, validation, client From 89d23b15897d2417577e4c1051ac1aaa9b6b57e1 Mon Sep 17 00:00:00 2001 From: jcardozo-eth Date: Fri, 27 Feb 2026 16:53:57 +0100 Subject: [PATCH 3/4] chore: remove non-functional Python 2 data generation scripts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These scripts use Python 2 print statements and hardcoded absolute paths, making them non-functional on Python 3. The fixture XML files they originally generated already exist in the fake1/–fake5/ test directories. --- src/oaipmh/tests/createdata.py | 68 ------------------- .../tests/createdata_deleted_records.py | 45 ------------ 2 files changed, 113 deletions(-) delete mode 100644 src/oaipmh/tests/createdata.py delete mode 100644 src/oaipmh/tests/createdata_deleted_records.py diff --git a/src/oaipmh/tests/createdata.py b/src/oaipmh/tests/createdata.py deleted file mode 100644 index e30a985..0000000 --- a/src/oaipmh/tests/createdata.py +++ /dev/null @@ -1,68 +0,0 @@ -from .fakeclient import FakeCreaterClient - -# tied to the server at EUR.. -client = FakeCreaterClient( - 'http://dspace.ubib.eur.nl/oai/', - '/home/faassen/py/oai/tests/fake2') - -print "GetRecord" -header, metadata, about = client.getRecord( - metadataPrefix='oai_dc', identifier='hdl:1765/315') -print "identifier:", header.identifier() -print "datestamp:", header.datestamp() -print "setSpec:", header.setSpec() -print "isDeleted:", header.isDeleted() -print - -print "Identify" -identify = client.identify() -print "repositoryName:", identify.repositoryName() -print "baseURL:", identify.baseURL() -print "protocolVerson:", identify.protocolVersion() -print "adminEmails:", identify.adminEmails() -print "earliestDatestamp:", identify.earliestDatestamp() -print "deletedRecords:", identify.deletedRecord() -print "granularity:", identify.granularity() -print "compression:", identify.compression() -print - -print "ListIdentifiers" -headers = client.listIdentifiers(from_=datetime(2003, 04, 10), - metadataPrefix='oai_dc') -for header in headers: - print "identifier:", header.identifier() - print "datestamp:", header.datestamp() - print "setSpec:", header.setSpec() - print "isDeleted:", header.isDeleted() -print - -print "ListMetadataFormats" -for prefix, schema, ns in client.listMetadataFormats(): - print "metadataPrefix:", prefix - print "schema:", schema - print "metadataNamespace:", ns -print - -print "ListRecords" -for header, metadata, about in client.listRecords( - from_=datetime(2003, 04, 10), metadataPrefix='oai_dc'): - print "header" - print "identifier:", header.identifier() - print "datestamp:", header.datestamp() - print "setSpec:", header.setSpec() - print "isDeleted:", header.isDeleted() - #print "metadata" - #for fieldname in fieldnames: - # print "%s:" % fieldname, metadata.getField(fieldname) - print "about" - print about -print - -print "ListSets" -for setSpec, setName, setDescription in client.listSets(): - print "setSpec:", setSpec - print "setName:", setName - print "setDescription:", setDescription -print - -client.save() diff --git a/src/oaipmh/tests/createdata_deleted_records.py b/src/oaipmh/tests/createdata_deleted_records.py deleted file mode 100644 index 2d6598b..0000000 --- a/src/oaipmh/tests/createdata_deleted_records.py +++ /dev/null @@ -1,45 +0,0 @@ -from .fakeserver import FakeCreaterServerProxy - -# tied to the server at EUR.. -server = FakeCreaterServerProxy( - 'http://dspace.ubib.eur.nl/oai/', - '/home/eric/CVS_checkouts/oai/tests/fake2') - -#deleted record -print "GetRecord" -header, metadata, about = server.getRecord( - metadataPrefix='oai_dc', identifier='hdl:1765/1160') -print "identifier:", header.identifier() -print "datestamp:", header.datestamp() -print "setSpec:", header.setSpec() -print "isDeleted:", header.isDeleted() -print - -#normal record -print "GetRecord" -header, metadata, about = server.getRecord( - metadataPrefix='oai_dc', identifier='hdl:1765/1162') -print "identifier:", header.identifier() -print "datestamp:", header.datestamp() -print "setSpec:", header.setSpec() -print "isDeleted:", header.isDeleted() -print - -print "ListRecords" -for header, metadata, about in server.listRecords( - from_=datetime(2004, 01, 01), until=datetime(2004, 02, 01), - metadataPrefix='oai_dc'): - print "header" - print "identifier:", header.identifier() - print "datestamp:", header.datestamp() - print "setSpec:", header.setSpec() - print "isDeleted:", header.isDeleted() - print "metadata" - if metadata is not None: - for fieldname in metadata.getMap().keys(): - print "%s:" % fieldname, metadata.getField(fieldname) - print "about" - print about -print - -server.save() From 85090354abe4841a29f7bb243d76ed035dfc03c8 Mon Sep 17 00:00:00 2001 From: jcardozo-eth Date: Fri, 27 Feb 2026 16:57:29 +0100 Subject: [PATCH 4/4] fix: rename TestError to FakeRequestError to fix PytestCollectionWarning Pytest attempts to collect TestError as a test class because its name starts with 'Test'. Rename to FakeRequestError to follow the existing Fake* naming convention in fakeclient.py and eliminate the warning. --- src/oaipmh/tests/fakeclient.py | 4 ++-- src/oaipmh/tests/test_client.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/oaipmh/tests/fakeclient.py b/src/oaipmh/tests/fakeclient.py index 38047c0..7842730 100644 --- a/src/oaipmh/tests/fakeclient.py +++ b/src/oaipmh/tests/fakeclient.py @@ -15,7 +15,7 @@ def makeRequest(self, **kw): # sort it to get stable behavior return self._mapping[getRequestKey(kw)] -class TestError(Exception): +class FakeRequestError(Exception): def __init__(self, kw): self.kw = kw @@ -28,7 +28,7 @@ def makeRequest(self, **kw): # even more fake, we'll simply raise an exception with the request # this can be caught by the test to see whether the request uses # day granularity.. - raise TestError(kw) + raise FakeRequestError(kw) def identify(self): return common.Identify( diff --git a/src/oaipmh/tests/test_client.py b/src/oaipmh/tests/test_client.py index 7b3851f..36603e6 100644 --- a/src/oaipmh/tests/test_client.py +++ b/src/oaipmh/tests/test_client.py @@ -1,7 +1,7 @@ from unittest import TestCase from unittest import mock -from .fakeclient import FakeClient, GranularityFakeClient, TestError +from .fakeclient import FakeClient, GranularityFakeClient, FakeRequestError import os from datetime import datetime import urllib.request as urllib2 @@ -164,7 +164,7 @@ def test_day_granularity(self): try: fakeclient.listRecords(from_=datetime(2003, 4, 10, 14, 0), metadataPrefix='oai_dc') - except TestError as e: + except FakeRequestError as e: self.assertEqual('2003-04-10T14:00:00Z', e.kw['from']) fakeclient = GranularityFakeClient(granularity='YYYY-MM-DD') fakeclient.updateGranularity() @@ -172,7 +172,7 @@ def test_day_granularity(self): fakeclient.listRecords(from_=datetime(2003, 4, 10, 14, 0), until=datetime(2004, 6, 17, 15, 30), metadataPrefix='oai_dc') - except TestError as e: + except FakeRequestError as e: self.assertEqual('2003-04-10', e.kw['from']) self.assertEqual('2004-06-17', e.kw['until'])