Skip to content

require FORCE_FLAG_KW uniformly for overwrites by put and create #721

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

d-w-moore
Copy link
Collaborator

No description provided.

@d-w-moore
Copy link
Collaborator Author

tests are upcoming.

if force:
options[kw.FORCE_FLAG_KW]=''

if self.exists(path) and kw.FORCE_FLAG_KW not in options:
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's reverse the conditions as is being done in put:

Suggested change
if self.exists(path) and kw.FORCE_FLAG_KW not in options:
if kw.FORCE_FLAG_KW not in options and self.exists(path):

Comment on lines 466 to 467
# TODO: We could deprecate and then later remove the force parameter in favor of FORCE_FLAG_KW.
# That seems in order, since FORCE_FLAG_KW is the predominant pattern for asserting overwrites.
Copy link
Contributor

Choose a reason for hiding this comment

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

Go ahead and make an issue for this to attach to the TODO.

@@ -319,6 +319,9 @@ def put(
)
else:
obj = irods_path
if kw.FORCE_FLAG_KW not in options and self.exists(obj):
raise ex.OVERWRITE_WITHOUT_FORCE_FLAG
options.pop(kw.FORCE_FLAG_KW, None)
Copy link
Contributor

Choose a reason for hiding this comment

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

I would say we don't need to pop this, but if it seems better to do so, perhaps a "why" comment would be appropriate.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Popping is a matter of form, I think. Don't allow options to be included for an API call that doesn't respect them

@trel
Copy link
Member

trel commented May 1, 2025

thought experiment question.... about blast radius...

how many existing python applications will start to fail on their puts and need to be updated to support a force flag?

second question...
given this is what we want to do... is it better/best to wait until 3.2.0 rather than a patch release? if we're considering the current behavior a bug... then 3.1.1 is fine.

@korydraughn
Copy link
Contributor

That's a good question. If we're introducing a breaking change, then that means a major version bump.

@alanking
Copy link
Contributor

alanking commented May 1, 2025

That's a good question. If we're introducing a breaking change, then that means a major version bump.

#132 is a bug so, although this would be a behavioral change, I would expect this in a minor release if not a patch release since the current behavior is incorrect. Thoughts?

@korydraughn
Copy link
Contributor

If it's a bug, then yes. I agree it's fine to include it in a minor/patch release.

@trel
Copy link
Member

trel commented May 1, 2025

i'm leaning to 'number go up' and people can tweak their force flag usage. we're not going to accidentally overwrite MORE things... it will be fewer things.

@d-w-moore
Copy link
Collaborator Author

d-w-moore commented May 1, 2025

thought experiment question.... about blast radius...

how many existing python applications will start to fail on their puts and need to be updated to support a force flag?

second question... given this is what we want to do... is it better/best to wait until 3.2.0 rather than a patch release? if we're considering the current behavior a bug... then 3.1.1 is fine.

No source installations will fail, because the default behavior of force = False will be "absorbed" into **opts, as well as presence of the keyword if/when provided.
So consider if we do:

def force_is_not_true( force=False , **opts): 
  return True==(force or opts.get('force'))

We now have a situation in which we can leverage that force either has a boolean equivalent that's True, or (if given as None/False/bool-false-equivalents, or not given at all ) then it will not be True. That's our present dichotomy.

then:

force_is_not_true(force = False) # => False
force_is_not_true( ) => False
force_is_not_true( force = True) => True

at which point we can really just delete the force keyword, even without deprecating first.

@trel
Copy link
Member

trel commented May 1, 2025

I'm having trouble even reading that comment. force and negative and false...

@d-w-moore
Copy link
Collaborator Author

I'm having trouble even reading that comment. force and negative and false...

Sorry. It was written in a hurry and without clarity of thought. I'm going to redo it.

@d-w-moore
Copy link
Collaborator Author

d-w-moore commented May 2, 2025

So in a nutshell, we can replace

    def create(self, path, resource=None, force=False, **options):
        # ... function body

with

    def create(self, path, resource=None, **options):
      force = options.get('force',False)
      ##.... rest of function body

and in fact no applications should break. That's because despite removing the force=False in the function signature,Python will make sure any force=<value> keyword argument passed into the function call is folded into the **options dict.

Note - I wouldn't be so very keen to get rid of an option like force=False thing except that leaving it in at the same time we're beginning to officially respect FORCE_FLAG_KW in options would be confusing, to many, I fear.

There are other uses of a force parameter in the PRC presently, but they don't alias to use of FORCE_FLAG_KW.

@trel
Copy link
Member

trel commented May 2, 2025

that makes sense. thank you

@d-w-moore
Copy link
Collaborator Author

d-w-moore commented May 2, 2025

thought experiment question.... about blast radius...

how many existing python applications will start to fail on their puts and need to be updated to support a force flag?

second question... given this is what we want to do... is it better/best to wait until 3.2.0 rather than a patch release? if we're considering the current behavior a bug... then 3.1.1 is fine.

To the more direct question: Yes, definitely a valid concern, as applications will break due to the PRC now honoring the semantics of FORCE_FLAG_KW unless we change the default value of force to True. I'd have no qualms in doing that, especially if we agree to the absorption of the force keyword into options and document all of this.

Yes, I need to do a little more work on the docstring...

@d-w-moore
Copy link
Collaborator Author

d-w-moore commented May 2, 2025

you know what? Nevermind. I'm wrong. @trel is correct.

What a complete mess.

The traditional behavior of allowing an overwrite despite the advertised default of force=False is, well, a blatant lie to the application developer. Unfortunately, to correct that, we have to break applications.

We should wait for a major release.

@d-w-moore
Copy link
Collaborator Author

d-w-moore commented May 2, 2025

The alternative, if we want something sensible in v3.1.1 to somewhat solve this mess but not break others' source code, is this:

  • changing the default value of force to True, i.e. correcting the lie.
  • allow forced overwrites to happen via either of:
    • force = True being passed , whether explicitly or by defaulting; or,
    • passing FORCE_FLAG_KW in options

In this way, even in the upcoming v3.1.1, applications will behave as they have behaved, except for those where FORCE_FLAG_KW was included in options accidentally (without effect, up to now).

The disadvantage: honoring the FORCE_FLAG_KW semantics will require that application programmers always explicitly specify force = False. That is the "ick" part, the fact that there are two independent but oppositely-defaulting ways to achieve the same thing.

That alone is really enough to make me opt to wait for a major release.

@d-w-moore d-w-moore marked this pull request as draft May 2, 2025 12:42
@trel
Copy link
Member

trel commented May 2, 2025

If I understand the main confusion/difference... the PRC .put is really an open/write/close.

Not sure that helps resolve our decision here, but it's definitely part of the conversation.

@korydraughn
Copy link
Contributor

Adding this as a discush item.

@d-w-moore
Copy link
Collaborator Author

d-w-moore commented May 2, 2025

Ok, better idea ... since the CREATE/OPEN api doesn't respect FORCE_FLAG_KW (or absence of it) anyway, we shouldn't pretend the create( ) call needs it either. So in this next release of 3.1.1, we could just correct the real bug and make it internally consistent.

That is, we won't change the force = False, or remove the force keyword either. We'll simply make it throw a custom exception, OverwriteWithoutForceSelected , when the logical path pre-exists, or something by a better name if someone can think of it. The reason I don't want to throw OVERWRITE_WITHOUT_FORCE_FLAG is because the force flag it mentions refers to the actual FORCE_FLAG_KW, used by and for the server

As we've discussed, the deviation of this call from its advertised behavior (truncating / overwriting even with force=False) is a bug in itself, which most likely very few, or no-one, may be relying on.

@korydraughn
Copy link
Contributor

The reason I don't want to throw OVERWRITE_WITHOUT_FORCE_FLAG is because the force flag it mentions refers to the actual FORCE_FLAG_KW, used by and for the server

I agree that that is a much better approach. Making it clear that this is specific to the PRC is good.

Alternative names:

  • DataObjectExistsAtLogicalPath
  • LogicalPathAlreadyExists
  • OverwriteRequiresForceFlag
  • PreexistingDataObjectAtLogicalPath
  • LogicalPathNameCollision

@korydraughn
Copy link
Contributor

More names:

  • LogicalPathInUse
  • LogicalPathAlreadyInUse
  • OverwriteNotAllowedWithoutForceFlag
  • OverwriteNotAllowed

@trel
Copy link
Member

trel commented May 2, 2025

LogicalPathAlreadyExists catches my eye

@d-w-moore
Copy link
Collaborator Author

d-w-moore commented May 2, 2025

LogicalPathAlreadyExists catches my eye

Say no more. Now a part of this PR...! That was my first choice too, among Kory's suggestions.

…write

PUT will respect FORCE_FLAG_KW, to mimic iput; whereas create will now respect
the force=True option when requested.
@d-w-moore d-w-moore self-assigned this May 9, 2025
@d-w-moore
Copy link
Collaborator Author

thought experiment question.... about blast radius...

how many existing python applications will start to fail on their puts and need to be updated to support a force flag?

second question... given this is what we want to do... is it better/best to wait until 3.2.0 rather than a patch release? if we're considering the current behavior a bug... then 3.1.1 is fine.

My thoughts

  • It's likely some major applications will require some major updates in whatever window / milestone we choose for this
  • On consideration, it can probably wait for v3.2.0. It's been this way for a while. in the meanwhile:
  • We could introduce {put|create}_without_force( ) for those who want correct behavior in v3.1.1 - whatsay team???
  • If we agree it's a bug , that CREATE and PUT can cause data loss the way they are ... then shall we label it a bug?

@trel
Copy link
Member

trel commented May 9, 2025

data loss from CREATE feels very wrong

data loss (aka overwrite) from PUT does not feel as incorrect.. just a bit, perhaps, pointy/dangerous/surprising for a new user.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

4 participants