Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions irods/manager/metadata_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,14 @@
def __init__(self, *_):
self._opts = _MetadataManager_opts_initializer.copy()
super().__init__(*_)
# For the iRODS-api keywords only (currently ADMIN_KW is the sole one used):
self.__kw = {}

@property
def use_timestamps(self):
return self._opts['timestamps']

__kw: Dict[str, Any] = {} # default (empty) keywords
__default_kw: Dict[str, Any] = {} # default (empty) keywords

Check failure on line 46 in irods/manager/metadata_manager.py

View workflow job for this annotation

GitHub Actions / ruff-lint / ruff-check

Ruff mutable-class-default

mutable-class-default: Mutable default value for class attribute [check:mutable-class-default]

Check failure on line 46 in irods/manager/metadata_manager.py

View workflow job for this annotation

GitHub Actions / ruff-lint / ruff-check

Ruff non-pep585-annotation

non-pep585-annotation: Use `dict` instead of `Dict` for type annotation [check:non-pep585-annotation]

def _updated_keywords(self, opts):
kw_ = self.__kw.copy()
Expand All @@ -52,18 +54,20 @@
return self.__kw.copy()

def __call__(self, **flags):
# Make a new shallow copy of the manager object, but update options from parameter list.
# Make a new shallow copy of the manager object, but duplicate options from parameter list as well as iRODS API
# flags (stored in the instance's private __kw member) to be applied in each call.
new_self = copy.copy(self)
new_self._opts = copy.copy(self._opts)
new_self.__kw = copy.copy(self.__kw)

Check failure on line 61 in irods/manager/metadata_manager.py

View workflow job for this annotation

GitHub Actions / ruff-lint / ruff-check

Ruff private-member-access

private-member-access: Private member accessed: `__kw` [check:private-member-access]

# Update the flags that do bookkeeping in the returned(new) manager object.
new_self._opts.update((key, val) for key, val in flags.items() if val is not None)

# Update the ADMIN_KW flag in the returned(new) object.
# For the new object, make ADMIN_KW flag or absence thereof reflect the admin option in _opts.
if new_self._opts.get('admin'):
self.__kw[kw.ADMIN_KW] = ""
new_self.__kw[kw.ADMIN_KW] = ""

Check failure on line 68 in irods/manager/metadata_manager.py

View workflow job for this annotation

GitHub Actions / ruff-lint / ruff-check

Ruff private-member-access

private-member-access: Private member accessed: `__kw` [check:private-member-access]
else:
self.__kw.pop(kw.ADMIN_KW, None)
new_self.__kw.pop(kw.ADMIN_KW, None)

Check failure on line 70 in irods/manager/metadata_manager.py

View workflow job for this annotation

GitHub Actions / ruff-lint / ruff-check

Ruff private-member-access

private-member-access: Private member accessed: `__kw` [check:private-member-access]

return new_self

Expand Down
46 changes: 46 additions & 0 deletions irods/test/meta_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,52 @@
# data.metadata(admin = True) generates a cloned object but for the one change to "admin".
data.metadata.admin = True

def test_destickifying_of_admin_option__issue_833(self):
# Create a rodsuser, and a session for that roduser.
adm = self.sess
user = d = None
try:
user = adm.users.create("bobby", "rodsuser")
user.modify("password", "bpass")
sessions = []
for _ in range(2):
with iRODSSession(
port=adm.port,
zone=adm.zone,
host=adm.host,
user=user.name,
password="bpass",
) as ses:
# Get a reference to a data object owned by the rodsuser, creating it if not already there.
d = ses.data_objects.create("/{adm.zone}/home/{user.name}/testfile".format(**locals()))

# d.metadata is a different MetadataManager instance (and d, a different session instance) on the
# second iteration of the loop as compared to the first. Thus, admin flags should not carry over.
if not sessions:
d.metadata(admin=True)
else:
# Should not be applying ADMIN_KW because INSUFFICIENT_PRIVILEGE_LEVEL exception would result
d.metadata.set('a','b')
sessions.append(ses)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is ses still active beyond the with-block?
Wouldn't the cleanup logic run at the end of the with-block?

Comment on lines +808 to +827

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we simplify this by dropping the for-loop?


# Admin option should be false after the second loop iteration.
self.assertFalse(d.metadata.admin)

get_call_keywords = lambda metacoll: metacoll._manager._updated_keywords((),)

Check failure on line 832 in irods/test/meta_test.py

View workflow job for this annotation

GitHub Actions / ruff-lint / ruff-check

Ruff private-member-access

private-member-access: Private member accessed: `_updated_keywords` [check:private-member-access]

Check failure on line 832 in irods/test/meta_test.py

View workflow job for this annotation

GitHub Actions / ruff-lint / ruff-check

Ruff private-member-access

private-member-access: Private member accessed: `_manager` [check:private-member-access]

Check failure on line 832 in irods/test/meta_test.py

View workflow job for this annotation

GitHub Actions / ruff-lint / ruff-check

Ruff lambda-assignment

lambda-assignment: Do not assign a `lambda` expression, use a `def` [check:lambda-assignment]

# Applying admin=True should result in API flags containing ADMIN_KW among the lookup keys.
self.assertIn(kw.ADMIN_KW, get_call_keywords(md_modified:=d.metadata(admin=True)))

# Admin option should be on in the object options bookkeeping.
self.assertTrue(md_modified.admin)

# However, the unmodified source object should not reflect use of an ADMIN_KW.
self.assertNotIn(kw.ADMIN_KW, get_call_keywords(d.metadata)) # keyword updates not reflected in copied obj.

Check failure on line 841 in irods/test/meta_test.py

View workflow job for this annotation

GitHub Actions / ruff-lint / ruff-format

Ruff format

Improper formatting
finally:
if d:
d.unlink(force=True)
if user:
user.remove()

if __name__ == "__main__":
# let the tests find the parent irods lib
Expand Down
Loading