Skip to content
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

Fixed #29501 -- Allowed dbshell to pass options to underlying tool. #12508

Merged
merged 5 commits into from
Apr 14, 2020

Conversation

adamchainz
Copy link
Sponsor Member

@adamchainz adamchainz commented Feb 29, 2020

@adamchainz adamchainz changed the title Fixed #29501 -- Allowed dbshell to pass options to the underlying program. Fixed #29501 -- Allowed dbshell to pass options to underlying tool. Feb 29, 2020
@adamchainz adamchainz force-pushed the ticket_29501 branch 2 times, most recently from d83da58 to da8ccfc Compare February 29, 2020 14:56
@adamchainz
Copy link
Sponsor Member Author

Requested your review @claudep because you've reviewed a few dbshell PR's in the past :)

Copy link
Member

@claudep claudep left a comment

Choose a reason for hiding this comment

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

Looks great, added some comments.

django/db/backends/base/client.py Show resolved Hide resolved
docs/ref/django-admin.txt Show resolved Hide resolved
docs/ref/django-admin.txt Outdated Show resolved Hide resolved
@adamchainz adamchainz force-pushed the ticket_29501 branch 2 times, most recently from b89a213 to 2ec6ffc Compare March 1, 2020 10:03
Copy link
Member

@carltongibson carltongibson left a comment

Choose a reason for hiding this comment

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

Looks good. Couple of comments.

Aside: it seems sqlite3 has no way to run a command and exit: --cmd pops into the shell after running the supplied command. (That's not a problem with this PR.) Scrub that: it turns out not using --cmd at all works as expected. Doh.

tests/dbshell/test_sqlite.py Show resolved Hide resolved
docs/ref/django-admin.txt Show resolved Hide resolved
@adamchainz
Copy link
Sponsor Member Author

I think this is ready for merge?

@felixxm felixxm self-assigned this Mar 17, 2020
@felixxm
Copy link
Member

felixxm commented Mar 17, 2020

@adamchainz I will take a look in this week 👀 , please be patient.

@felixxm
Copy link
Member

felixxm commented Mar 17, 2020

@adamchainz Thanks for this patch 👍 I pushed edits to docs. What do you think about suppressing subprocess' exceptions?

@ngnpope
Copy link
Member

ngnpope commented Mar 17, 2020

I think suppressing the exception makes sense. Django is passing the arguments through and the error is raised by the external process which has already thrown messages to the console.

I was going to say reraising was not needed, but if scripted it could be handy to know which exact command failed.

@adamchainz
Copy link
Sponsor Member Author

Sorry about my impatience @felixxm 🙈

Agree with both that suppressing the exception makes sense. It already breaks some workflows before passing options through.

The reraise of CommandError would work, but I'm not sure it's necessary. I've instead changed the PR right now to use this pattern:

proc = subprocess.run(...)
sys.exit(proc.returncode)

This means Django is "transparent" around the command - it won't create any output but still exits with the same code. It's much more like its behaviour using os.exec* which was removed in bf5382c due to windows concerns. And then dbshell can (again) be a complete drop-in replacement in scripts.

@adamchainz adamchainz force-pushed the ticket_29501 branch 2 times, most recently from 8ab2100 to 019ebbb Compare March 17, 2020 22:23
@felixxm
Copy link
Member

felixxm commented Mar 18, 2020

This means Django is "transparent" around the command - it won't create any output but still exits with the same code. It's much more like its behaviour using os.exec* which was removed in bf5382c due to windows concerns. And then dbshell can (again) be a complete drop-in replacement in scripts.

This approach breaks usage with call_command(), I think we need to stay with suppression subprocess.CalledProcessError's (or re-raise them as CommandError's).

@adamchainz
Copy link
Sponsor Member Author

This approach breaks usage with call_command(), I think we need to stay with suppression subprocess.CalledProcessError's (or re-raise them as CommandError's).

I am not sure use of dbshell with call_command is common. It would not have worked Django< 1.6 at least due to use of os.exec* previously, since that replaces the process and stops Python executing entirely. And anyway like this, one can catch SystemExit instead of CommandError.

I think it's important to preserve the return code, especially when adding the ability to use custom flags. Which return code was used can carry extra meaning. I've made the sys.exit conditional on the return code not being 0 so at least testing with except SystemExit catches abnormal conditions only.

@adamchainz
Copy link
Sponsor Member Author

I've also changed to FileNotFoundError in dbshell.py, since Python 3 differentiates between different kinds of OSError, and that's the one relating to missing commands.

@carltongibson
Copy link
Member

Management commands are expected to raise CommandError:

raising CommandError exception (with a sensible description of the error) is the preferred way to indicate that something has gone wrong in the execution of a command.

I think we need to stick with that.

@adamchainz
Copy link
Sponsor Member Author

Okay done. To preserve the return code I added the returncode argument to CommandError, which also seems like it could be useful for other scripting sitautions.

Copy link
Member

@felixxm felixxm left a comment

Choose a reason for hiding this comment

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

@adamchainz Thanks 👍 Please reorganize commits, we shouldn't mix (multiple) cleanups with a new feature.

django/core/management/commands/dbshell.py Show resolved Hide resolved
django/core/management/base.py Show resolved Hide resolved
django/core/management/base.py Show resolved Hide resolved
@adamchainz
Copy link
Sponsor Member Author

Apologies for my unsplit commits. I think I've done it with good enough naming now.

@felixxm
Copy link
Member

felixxm commented Apr 14, 2020

@adamchainz Thanks 👍 I added tests for missing dbshell executable (in a separate commit), added test for CommandError.returncode, and pushed minor edits.

@adamchainz
Copy link
Sponsor Member Author

All LGTM

docs/releases/3.1.txt Outdated Show resolved Hide resolved
@felixxm felixxm merged commit 5b884d4 into django:master Apr 14, 2020
@adamchainz adamchainz deleted the ticket_29501 branch April 15, 2020 10:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants