From fec88deda479578f3ea29377704e7e0ecca254ab Mon Sep 17 00:00:00 2001 From: d-w-moore Date: Tue, 3 Mar 2026 09:46:38 -0500 Subject: [PATCH 01/12] timestamps for Ticket --- irods/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/irods/models.py b/irods/models.py index d779bf86b..ec79324f0 100644 --- a/irods/models.py +++ b/irods/models.py @@ -244,8 +244,8 @@ class Ticket(Model): write_byte_limit = Column(Integer, "TICKET_WRITE_BYTE_LIMIT", 2214) ## For now, use of these columns raises CAT_SQL_ERR in both PRC and iquest: (irods/irods#5929) - # create_time = Column(String, 'TICKET_CREATE_TIME', 2209) - # modify_time = Column(String, 'TICKET_MODIFY_TIME', 2210) + create_time = Column(DateTime, 'TICKET_CREATE_TIME', 2209) + modify_time = Column(DateTime, 'TICKET_MODIFY_TIME', 2210) class DataObject(Model): """For queries of R_DATA_MAIN when joining to R_TICKET_MAIN. From b5c20333e3d7f029c5e3e3a69f7ffd0407b02cb9 Mon Sep 17 00:00:00 2001 From: d-w-moore Date: Tue, 3 Mar 2026 10:09:31 -0500 Subject: [PATCH 02/12] [_801] copy result attributes into Ticket object --- irods/ticket.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/irods/ticket.py b/irods/ticket.py index 5a213dbcb..551080c3f 100644 --- a/irods/ticket.py +++ b/irods/ticket.py @@ -32,12 +32,30 @@ class Ticket: def __init__(self, session, ticket="", result=None, allow_punctuation=False): self._session = session try: - if result is not None: + if hasattr(result,'__getitem__') and result.get(TicketQuery.Ticket.string,''): + pass + elif ticket and hasattr(result,'__iter__'): + result = [_ for _ in result if _[TicketQuery.Ticket.string] == ticket][0] + if result: ticket = result[TicketQuery.Ticket.string] + for attr, value in TicketQuery.Ticket.__dict__.items(): + if value is TicketQuery.Ticket.string: continue + #print ('copying',attr) + if not attr.startswith("_"): + try: + setattr(self, attr, result[value]) + except KeyError: + # backward compatibility with older schema versions + pass except TypeError: + raise RuntimeError( "If specified, 'result' parameter must be a TicketQuery.Ticket search result" ) + except IndexError: + raise RuntimeError( + "If both result and string are specified, at least one 'result' must match the ticket string" + ) self._ticket = ( ticket if ticket else self._generate(allow_punctuation=allow_punctuation) ) From 5b0873a27c6dc9533cb0ff168e1f2bda172245e9 Mon Sep 17 00:00:00 2001 From: d-w-moore Date: Tue, 3 Mar 2026 14:42:19 -0500 Subject: [PATCH 03/12] [_120] function list_tickets --- irods/ticket.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/irods/ticket.py b/irods/ticket.py index 551080c3f..cd5c1ff72 100644 --- a/irods/ticket.py +++ b/irods/ticket.py @@ -28,6 +28,26 @@ def get_epoch_seconds(utc_timestamp): raise # final try at conversion, so a failure is an error +def list_tickets(session, all=True): + """ + Enumerates (via GenQuery1) all tickets visible by, or owned by, the current user. + + Args: + session: An iRODSSession object for use in the query. + all: True if a comprehensive list is desired; otherwise only those + tickets owned by the calling user. + + Returns: + An iterator over a range of ticket objects. + """ + query = session.query(TicketQuery.Ticket) + if not all: + query = query.filter( + TicketQuery.Ticket.user_id == session.users.get(session.username).id + ) + yield from query + + class Ticket: def __init__(self, session, ticket="", result=None, allow_punctuation=False): self._session = session From 3323b9dd3f2ed4fb5e8a2bff9ab308c5cfc61cfa Mon Sep 17 00:00:00 2001 From: d-w-moore Date: Wed, 4 Mar 2026 06:16:29 -0500 Subject: [PATCH 04/12] [_120,_801] misc changes --- irods/models.py | 6 ++-- irods/test/ticket_test.py | 58 +++++++++++++++++++++++++++++++++++++-- irods/ticket.py | 38 +++++++++++++++++++------ 3 files changed, 86 insertions(+), 16 deletions(-) diff --git a/irods/models.py b/irods/models.py index ec79324f0..e1ebf3dfe 100644 --- a/irods/models.py +++ b/irods/models.py @@ -242,10 +242,8 @@ class Ticket(Model): write_file_limit = Column(Integer, "TICKET_WRITE_FILE_LIMIT", 2212) write_byte_count = Column(Integer, "TICKET_WRITE_BYTE_COUNT", 2213) write_byte_limit = Column(Integer, "TICKET_WRITE_BYTE_LIMIT", 2214) - - ## For now, use of these columns raises CAT_SQL_ERR in both PRC and iquest: (irods/irods#5929) - create_time = Column(DateTime, 'TICKET_CREATE_TIME', 2209) - modify_time = Column(DateTime, 'TICKET_MODIFY_TIME', 2210) + create_time = Column(DateTime, 'TICKET_CREATE_TIME', 2209, min_version=(4, 3, 0)) + modify_time = Column(DateTime, 'TICKET_MODIFY_TIME', 2210, min_version=(4, 3, 0)) class DataObject(Model): """For queries of R_DATA_MAIN when joining to R_TICKET_MAIN. diff --git a/irods/test/ticket_test.py b/irods/test/ticket_test.py index 56e7c3a10..854d237f6 100644 --- a/irods/test/ticket_test.py +++ b/irods/test/ticket_test.py @@ -1,20 +1,25 @@ #! /usr/bin/env python +import calendar +import datetime +import logging import os import sys -import unittest import time -import calendar +import unittest import irods.test.helpers as helpers import tempfile from irods.session import iRODSSession import irods.exception as ex import irods.keywords as kw -from irods.ticket import Ticket +from irods.ticket import list_tickets, Ticket from irods.models import TicketQuery, DataObject, Collection +logger = logging.getLogger(__name__) + + # As with most of the modules in this test suite, session objects created via # make_session() are implicitly agents of a rodsadmin unless otherwise indicated. # Counterexamples within this module shall be obvious as they are instantiated by @@ -29,6 +34,17 @@ def gmtime_to_timestamp(gmt_struct): ) +def delete_tickets(session, dry_run = False): + for res in session.query(TicketQuery.Ticket): + t = Ticket(session, result=res) + if dry_run in (False, None): + t.delete(**{kw.ADMIN_KW: ""}) + elif isinstance(dry_run, list): + dry_run.append(t) + else: + logger.info('Found ticket: %s',t.string) + + def delete_my_tickets(session): my_userid = session.users.get(session.username).id my_tickets = session.query(TicketQuery.Ticket).filter( @@ -358,6 +374,27 @@ def test_coll_read_ticket_between_rodsusers(self): os.unlink(file_.name) alice.cleanup() + def test_new_attributes_in_tickets__issue_801(self): + + if (admin:=helpers.make_session()).server_version < (4, 3, 0): + self.skipTest('"create_time" and "modify_time" not supported for Ticket') + + try: + with self.login(self.bob) as bob: + bobs_ticket = Ticket(bob) + bobs_ticket.issue('write', helpers.home_collection(bob)) + time.sleep(2) + bobs_ticket.modify('add', 'user', admin.username) + bobs_ticket = Ticket(bob, result=[], ticket=bobs_ticket.string) + self.assertGreaterEqual( + bobs_ticket.modify_time, + bobs_ticket.create_time + datetime.timedelta(seconds=1) + ) + + admin_ticket_for_bob = Ticket(admin, result=[], ticket=bobs_ticket.string) + self.assertEqual(admin_ticket_for_bob.id, bobs_ticket.id) + finally: + admin_ticket_for_bob.delete(**{kw.ADMIN_KW:''}) class TestTicketOps(unittest.TestCase): @@ -456,6 +493,21 @@ def test_coll_ticket_write(self): self._ticket_write_helper(obj_type="coll") + def test_list_tickets__issue_120(self): + + ses = self.sess + + # t first assigned as a "utility" Ticket object + t = Ticket(ses).issue('read', helpers.home_collection(ses)) + self.assertGreater(len(t.string) , 5) + + # This time, t receives attributes from the GenQuery row result + t = Ticket(ses, result=[], ticket=t.string) + + # Check an id attribute is present and listed in the results from list_tickets + self.assertIn(t.id, (_.id for _ in list_tickets(ses))) + + if __name__ == "__main__": # let the tests find the parent irods lib sys.path.insert(0, os.path.abspath("../..")) diff --git a/irods/ticket.py b/irods/ticket.py index cd5c1ff72..e066b6da2 100644 --- a/irods/ticket.py +++ b/irods/ticket.py @@ -1,6 +1,8 @@ from irods.api_number import api_number from irods.message import iRODSMessage, TicketAdminRequest from irods.models import TicketQuery +from irods.column import Like +from collections.abc import Mapping, Sequence import random import string @@ -28,12 +30,13 @@ def get_epoch_seconds(utc_timestamp): raise # final try at conversion, so a failure is an error -def list_tickets(session, all=True): +def list_tickets(session, *, raw=False, all=True): """ Enumerates (via GenQuery1) all tickets visible by, or owned by, the current user. Args: session: An iRODSSession object for use in the query. + raw: True if only the queried rows are to be returned; False to construct Ticket objects for each row. all: True if a comprehensive list is desired; otherwise only those tickets owned by the calling user. @@ -45,22 +48,40 @@ def list_tickets(session, all=True): query = query.filter( TicketQuery.Ticket.user_id == session.users.get(session.username).id ) - yield from query + if raw: + yield from query + else: + yield from (Ticket(session, result=_) for _ in query) class Ticket: def __init__(self, session, ticket="", result=None, allow_punctuation=False): self._session = session try: - if hasattr(result,'__getitem__') and result.get(TicketQuery.Ticket.string,''): - pass - elif ticket and hasattr(result,'__iter__'): - result = [_ for _ in result if _[TicketQuery.Ticket.string] == ticket][0] + if isinstance(result, Mapping): + if (single_string:=result.get(TicketQuery.Ticket.string, '')): + if ticket and (ticket != single_string): + raise RuntimeError( + f"The specified result contained a ticket string mismatched to the provided identifier ({ticket = })" + ) + + # Allow limited query for the purpose of populating id and other attributes + if result == [] and ticket: + result[:] = list(session.query(TicketQuery.Ticket).filter(TicketQuery.Ticket.string == ticket)) + + if isinstance(result, Sequence): + if ticket: + result = [_ for _ in result if _[TicketQuery.Ticket.string] == ticket][:1] + + if not result: + result = None + else: + result = result[0] + if result: ticket = result[TicketQuery.Ticket.string] for attr, value in TicketQuery.Ticket.__dict__.items(): if value is TicketQuery.Ticket.string: continue - #print ('copying',attr) if not attr.startswith("_"): try: setattr(self, attr, result[value]) @@ -68,9 +89,8 @@ def __init__(self, session, ticket="", result=None, allow_punctuation=False): # backward compatibility with older schema versions pass except TypeError: - raise RuntimeError( - "If specified, 'result' parameter must be a TicketQuery.Ticket search result" + "If specified, 'result' parameter must be a TicketQuery.Ticket search result or iterable of same" ) except IndexError: raise RuntimeError( From 5626233fd2922d5ba7c121a05f86c5481234ce75 Mon Sep 17 00:00:00 2001 From: d-w-moore Date: Wed, 4 Mar 2026 18:19:57 -0500 Subject: [PATCH 05/12] ws --- irods/test/ticket_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/irods/test/ticket_test.py b/irods/test/ticket_test.py index 854d237f6..12d86af28 100644 --- a/irods/test/ticket_test.py +++ b/irods/test/ticket_test.py @@ -387,7 +387,7 @@ def test_new_attributes_in_tickets__issue_801(self): bobs_ticket.modify('add', 'user', admin.username) bobs_ticket = Ticket(bob, result=[], ticket=bobs_ticket.string) self.assertGreaterEqual( - bobs_ticket.modify_time, + bobs_ticket.modify_time, bobs_ticket.create_time + datetime.timedelta(seconds=1) ) From b1334593d862c0fe0dbc0682251277e36e8b15f2 Mon Sep 17 00:00:00 2001 From: d-w-moore Date: Thu, 5 Mar 2026 09:49:15 -0500 Subject: [PATCH 06/12] review suggestions --- irods/test/ticket_test.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/irods/test/ticket_test.py b/irods/test/ticket_test.py index 12d86af28..b44f55cd0 100644 --- a/irods/test/ticket_test.py +++ b/irods/test/ticket_test.py @@ -376,6 +376,8 @@ def test_coll_read_ticket_between_rodsusers(self): def test_new_attributes_in_tickets__issue_801(self): + admin_ticket_for_bob = None + if (admin:=helpers.make_session()).server_version < (4, 3, 0): self.skipTest('"create_time" and "modify_time" not supported for Ticket') @@ -394,7 +396,8 @@ def test_new_attributes_in_tickets__issue_801(self): admin_ticket_for_bob = Ticket(admin, result=[], ticket=bobs_ticket.string) self.assertEqual(admin_ticket_for_bob.id, bobs_ticket.id) finally: - admin_ticket_for_bob.delete(**{kw.ADMIN_KW:''}) + if admin_ticket_for_bob: + admin_ticket_for_bob.delete(**{kw.ADMIN_KW:''}) class TestTicketOps(unittest.TestCase): @@ -499,9 +502,8 @@ def test_list_tickets__issue_120(self): # t first assigned as a "utility" Ticket object t = Ticket(ses).issue('read', helpers.home_collection(ses)) - self.assertGreater(len(t.string) , 5) - # This time, t receives attributes from the GenQuery row result + # This time, t receives attributes from an internal GenQuery result. t = Ticket(ses, result=[], ticket=t.string) # Check an id attribute is present and listed in the results from list_tickets From 2d23c0f66eaf613d793559e0ab4d3fad16bebd6f Mon Sep 17 00:00:00 2001 From: d-w-moore Date: Fri, 6 Mar 2026 06:49:29 -0500 Subject: [PATCH 07/12] [_120,_801] better separation of concerns in ticket enumeration vs construction. This is hopefully a less awkward implementation (as well as less confusing - always a bonus). --- irods/test/ticket_test.py | 16 ++++++++------ irods/ticket.py | 44 ++++++++++++++++++++++----------------- 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/irods/test/ticket_test.py b/irods/test/ticket_test.py index b44f55cd0..7113404ac 100644 --- a/irods/test/ticket_test.py +++ b/irods/test/ticket_test.py @@ -13,7 +13,7 @@ from irods.session import iRODSSession import irods.exception as ex import irods.keywords as kw -from irods.ticket import list_tickets, Ticket +from irods.ticket import enumerate_tickets, Ticket from irods.models import TicketQuery, DataObject, Collection @@ -387,13 +387,13 @@ def test_new_attributes_in_tickets__issue_801(self): bobs_ticket.issue('write', helpers.home_collection(bob)) time.sleep(2) bobs_ticket.modify('add', 'user', admin.username) - bobs_ticket = Ticket(bob, result=[], ticket=bobs_ticket.string) + bobs_ticket = Ticket(bob, result=enumerate_tickets(bob, raw=True), ticket=bobs_ticket.string) self.assertGreaterEqual( bobs_ticket.modify_time, bobs_ticket.create_time + datetime.timedelta(seconds=1) ) - admin_ticket_for_bob = Ticket(admin, result=[], ticket=bobs_ticket.string) + admin_ticket_for_bob = Ticket(admin, result=enumerate_tickets(admin, raw=True), ticket=bobs_ticket.string) self.assertEqual(admin_ticket_for_bob.id, bobs_ticket.id) finally: if admin_ticket_for_bob: @@ -496,7 +496,7 @@ def test_coll_ticket_write(self): self._ticket_write_helper(obj_type="coll") - def test_list_tickets__issue_120(self): + def test_enumerate_tickets__issue_120(self): ses = self.sess @@ -504,10 +504,14 @@ def test_list_tickets__issue_120(self): t = Ticket(ses).issue('read', helpers.home_collection(ses)) # This time, t receives attributes from an internal GenQuery result. - t = Ticket(ses, result=[], ticket=t.string) + t = Ticket( + ses, + result=enumerate_tickets(ses, raw=True), + ticket=t.string + ) # Check an id attribute is present and listed in the results from list_tickets - self.assertIn(t.id, (_.id for _ in list_tickets(ses))) + self.assertIn(t.id, (_.id for _ in enumerate_tickets(ses))) if __name__ == "__main__": diff --git a/irods/ticket.py b/irods/ticket.py index e066b6da2..e99f116e8 100644 --- a/irods/ticket.py +++ b/irods/ticket.py @@ -30,33 +30,39 @@ def get_epoch_seconds(utc_timestamp): raise # final try at conversion, so a failure is an error -def list_tickets(session, *, raw=False, all=True): +class _default_ticket_query_factory: + + callable = staticmethod(lambda session: session.query(TicketQuery.Ticket)) + + def __call__(self, session): + return self.callable(session) + +def enumerate_tickets(session, *, query_factory = _default_ticket_query_factory(), raw=False): """ Enumerates (via GenQuery1) all tickets visible by, or owned by, the current user. Args: session: An iRODSSession object for use in the query. - raw: True if only the queried rows are to be returned; False to construct Ticket objects for each row. - all: True if a comprehensive list is desired; otherwise only those - tickets owned by the calling user. + query_factory: A callable which returns a generic query or other iterable + over TicketQuery.Ticket row results. + raw: If false, transform each row returned into a Ticket object; else return + the result rows unaltered. Returns: An iterator over a range of ticket objects. """ - query = session.query(TicketQuery.Ticket) - if not all: - query = query.filter( - TicketQuery.Ticket.user_id == session.users.get(session.username).id - ) + query = query_factory(session) + if raw: yield from query else: - yield from (Ticket(session, result=_) for _ in query) + yield from (Ticket(session, result=row) for row in query) class Ticket: def __init__(self, session, ticket="", result=None, allow_punctuation=False): self._session = session + try: if isinstance(result, Mapping): if (single_string:=result.get(TicketQuery.Ticket.string, '')): @@ -65,18 +71,14 @@ def __init__(self, session, ticket="", result=None, allow_punctuation=False): f"The specified result contained a ticket string mismatched to the provided identifier ({ticket = })" ) - # Allow limited query for the purpose of populating id and other attributes - if result == [] and ticket: - result[:] = list(session.query(TicketQuery.Ticket).filter(TicketQuery.Ticket.string == ticket)) - - if isinstance(result, Sequence): + elif hasattr(result, '__iter__'): if ticket: result = [_ for _ in result if _[TicketQuery.Ticket.string] == ticket][:1] if not result: result = None else: - result = result[0] + result = next(iter(result)) # result[0] if result: ticket = result[TicketQuery.Ticket.string] @@ -88,17 +90,21 @@ def __init__(self, session, ticket="", result=None, allow_punctuation=False): except KeyError: # backward compatibility with older schema versions pass + + self._ticket = ticket + except TypeError: raise RuntimeError( "If specified, 'result' parameter must be a TicketQuery.Ticket search result or iterable of same" ) + except IndexError: raise RuntimeError( "If both result and string are specified, at least one 'result' must match the ticket string" ) - self._ticket = ( - ticket if ticket else self._generate(allow_punctuation=allow_punctuation) - ) + + if not self._ticket: + self._ticket = self._generate(allow_punctuation=allow_punctuation) @property def session(self): From e456d010bd3b46de9f57af6f8662300d15baf460 Mon Sep 17 00:00:00 2001 From: d-w-moore Date: Fri, 6 Mar 2026 06:50:47 -0500 Subject: [PATCH 08/12] ws2 --- irods/ticket.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/irods/ticket.py b/irods/ticket.py index e99f116e8..03f050af5 100644 --- a/irods/ticket.py +++ b/irods/ticket.py @@ -31,7 +31,7 @@ def get_epoch_seconds(utc_timestamp): class _default_ticket_query_factory: - + callable = staticmethod(lambda session: session.query(TicketQuery.Ticket)) def __call__(self, session): From adea12d8b5acd8f9e58fe769821b74ddf12ffbba Mon Sep 17 00:00:00 2001 From: d-w-moore Date: Fri, 6 Mar 2026 14:09:58 -0500 Subject: [PATCH 09/12] make factory syntax simpler, and demonstrate subclassing example --- irods/ticket.py | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/irods/ticket.py b/irods/ticket.py index 03f050af5..ca8d155e7 100644 --- a/irods/ticket.py +++ b/irods/ticket.py @@ -1,8 +1,10 @@ from irods.api_number import api_number from irods.message import iRODSMessage, TicketAdminRequest from irods.models import TicketQuery -from irods.column import Like +from irods.column import Like, Column + from collections.abc import Mapping, Sequence +from typing import Any, Iterable, Optional, Type, Union import random import string @@ -29,21 +31,27 @@ def get_epoch_seconds(utc_timestamp): except ValueError: raise # final try at conversion, so a failure is an error +class default_ticket_query_factory: + _callable = staticmethod(lambda session: session.query(TicketQuery.Ticket)) + def __call__(self, session): + return self._callable(session) -class _default_ticket_query_factory: - - callable = staticmethod(lambda session: session.query(TicketQuery.Ticket)) +class myf(default_ticket_query_factory): + def __init__(self, initargs): + self._callable = lambda ses: default_ticket_query_factory._callable(ses).filter(*initargs) - def __call__(self, session): - return self.callable(session) +def etk(s): + import functools + myfc = functools.partial(myf, initargs=[TicketQuery.Ticket.id > 11000]) + return enumerate_tickets(s, query_factory = myfc) -def enumerate_tickets(session, *, query_factory = _default_ticket_query_factory(), raw=False): +def enumerate_tickets(session, *, query_factory = default_ticket_query_factory, raw=False): """ Enumerates (via GenQuery1) all tickets visible by, or owned by, the current user. Args: session: An iRODSSession object for use in the query. - query_factory: A callable which returns a generic query or other iterable + query_factory: A class capable of generating a generic query or other iterable over TicketQuery.Ticket row results. raw: If false, transform each row returned into a Ticket object; else return the result rows unaltered. @@ -51,16 +59,23 @@ def enumerate_tickets(session, *, query_factory = _default_ticket_query_factory( Returns: An iterator over a range of ticket objects. """ - query = query_factory(session) + query = query_factory()(session) if raw: yield from query else: yield from (Ticket(session, result=row) for row in query) +_column_lookup = dict[Type[Column], Any] class Ticket: - def __init__(self, session, ticket="", result=None, allow_punctuation=False): + + def __init__(self, + session, + ticket="", + result: Optional[Union[_column_lookup, Iterable[_column_lookup]]] =None, # Optional (vs. '|') is Python 3.9 syntax + allow_punctuation=False): + self._session = session try: From 66f50c467b8e0d3afc60549cfee57187c193f5b8 Mon Sep 17 00:00:00 2001 From: d-w-moore Date: Fri, 6 Mar 2026 14:45:13 -0500 Subject: [PATCH 10/12] revoke subclass example (will do a better one for README in a bit.) --- irods/ticket.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/irods/ticket.py b/irods/ticket.py index ca8d155e7..5dd22cc3a 100644 --- a/irods/ticket.py +++ b/irods/ticket.py @@ -36,15 +36,6 @@ class default_ticket_query_factory: def __call__(self, session): return self._callable(session) -class myf(default_ticket_query_factory): - def __init__(self, initargs): - self._callable = lambda ses: default_ticket_query_factory._callable(ses).filter(*initargs) - -def etk(s): - import functools - myfc = functools.partial(myf, initargs=[TicketQuery.Ticket.id > 11000]) - return enumerate_tickets(s, query_factory = myfc) - def enumerate_tickets(session, *, query_factory = default_ticket_query_factory, raw=False): """ Enumerates (via GenQuery1) all tickets visible by, or owned by, the current user. From 9d728cbee4eb225974d931c1dd6249e485bc4f69 Mon Sep 17 00:00:00 2001 From: d-w-moore Date: Fri, 6 Mar 2026 15:07:11 -0500 Subject: [PATCH 11/12] review chgs --- irods/test/ticket_test.py | 3 ++- irods/ticket.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/irods/test/ticket_test.py b/irods/test/ticket_test.py index 7113404ac..ef7d62df1 100644 --- a/irods/test/ticket_test.py +++ b/irods/test/ticket_test.py @@ -375,7 +375,8 @@ def test_coll_read_ticket_between_rodsusers(self): alice.cleanup() def test_new_attributes_in_tickets__issue_801(self): - + # Specifically we are testing that 'modify_time' and 'create_time' attributes function as expected, + # and that other attributes such as 'id' are also present. admin_ticket_for_bob = None if (admin:=helpers.make_session()).server_version < (4, 3, 0): diff --git a/irods/ticket.py b/irods/ticket.py index 5dd22cc3a..3f1b93e34 100644 --- a/irods/ticket.py +++ b/irods/ticket.py @@ -79,7 +79,7 @@ def __init__(self, elif hasattr(result, '__iter__'): if ticket: - result = [_ for _ in result if _[TicketQuery.Ticket.string] == ticket][:1] + result = [row for row in result if row[TicketQuery.Ticket.string] == ticket][:1] if not result: result = None From 93c5bcbc4a681ad40ac0eb98dcad068403b63b07 Mon Sep 17 00:00:00 2001 From: d-w-moore Date: Fri, 6 Mar 2026 15:23:00 -0500 Subject: [PATCH 12/12] no spc around = --- irods/ticket.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/irods/ticket.py b/irods/ticket.py index 3f1b93e34..034b22949 100644 --- a/irods/ticket.py +++ b/irods/ticket.py @@ -36,7 +36,7 @@ class default_ticket_query_factory: def __call__(self, session): return self._callable(session) -def enumerate_tickets(session, *, query_factory = default_ticket_query_factory, raw=False): +def enumerate_tickets(session, *, query_factory=default_ticket_query_factory, raw=False): """ Enumerates (via GenQuery1) all tickets visible by, or owned by, the current user.