Skip to content

Fixed #35515 -- Added auto-importing to shell command.#18158

Merged
nessita merged 1 commit intodjango:mainfrom
salvo-polizzi:auto-importing-shell
Jan 9, 2025
Merged

Fixed #35515 -- Added auto-importing to shell command.#18158
nessita merged 1 commit intodjango:mainfrom
salvo-polizzi:auto-importing-shell

Conversation

@salvo-polizzi
Copy link
Copy Markdown
Contributor

@salvo-polizzi salvo-polizzi commented May 11, 2024

Branch description

This would be an update of the existing Django shell that auto-imports models for you from your app/project. Also, the goal would be to allow the user to subclass this shell to customize its behavior and import extra things.

TODO

  • Enable the shell to auto-import all models from the current project/app.
  • Allow users to customize the shell by subclassing it, e.g., adding extra methods and/or classes to import.
  • Handle model name collisions by automatically importing each model's module.
  • With verbosity level 2 or above, print all functions and classes that are automatically imported.
  • Write unit tests to ensure models are correctly imported and in the proper order.
  • Write documentation to explain how users can customize the shell.

Checklist

  • This PR targets the main branch.
  • The commit message is written in past tense, mentions the ticket number, and ends with a period.
  • I have added or updated relevant tests.
  • I have added or updated relevant docs, including release notes if applicable.
  • I have attached screenshots in both light and dark modes for any UI changes.

Copy link
Copy Markdown
Member

@adamchainz adamchainz left a comment

Choose a reason for hiding this comment

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

Following up from my post on the forum :) You got this !

@salvo-polizzi salvo-polizzi force-pushed the auto-importing-shell branch from 4d9eb51 to abc0225 Compare May 15, 2024 13:45
@salvo-polizzi
Copy link
Copy Markdown
Contributor Author

Thank you so much, @adamchainz, for the feedback. Currently, I'm concentrating on writing tests for these features. Do you have any references or suggestions on where to start? I'm considering creating new functions within the shellCommandTestCase to test the auto-imports.

@adamchainz
Copy link
Copy Markdown
Member

Yes, the tests should be within ShellCommandTestCase.

You can unit-test get_namespace() by calling it (imports = Command().get_namespace()) and making assertions on its contents. Then you can write integration tests similar to the existing ones that use command and captured_stdin to test those pathways.

It seems the existing tests don't actually cover launching the various shells. I think it’s worth trying adding coverage, at least for the default python shell, though there’s a risk that calling the startup file, interactive hook, or readline are not a good idea during a test run.

@salvo-polizzi salvo-polizzi force-pushed the auto-importing-shell branch 2 times, most recently from b0a2562 to aa2080a Compare May 16, 2024 15:57
Copy link
Copy Markdown
Member

@adamchainz adamchainz left a comment

Choose a reason for hiding this comment

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

Great work getting some tests written! 💪

@salvo-polizzi salvo-polizzi force-pushed the auto-importing-shell branch 2 times, most recently from ca191f6 to c2a6351 Compare May 20, 2024 08:39
Copy link
Copy Markdown
Member

@adamchainz adamchainz left a comment

Choose a reason for hiding this comment

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

Thanks for your continued iteration. I hope you learn some things from my comments here.

@salvo-polizzi
Copy link
Copy Markdown
Contributor Author

Thanks @adamchainz again for your reviews. I left some questions in the comments

@salvo-polizzi salvo-polizzi force-pushed the auto-importing-shell branch from df3323c to a1582f0 Compare May 22, 2024 08:23
Copy link
Copy Markdown
Contributor

@DevilsAutumn DevilsAutumn left a comment

Choose a reason for hiding this comment

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

Hi @salvo-polizzi , Thankyou for your constants efforts on this! And special thanks to Adam for reviewing. This PR looks in a great shape already. 🥇
I finally have some time to push this further.

@salvo-polizzi salvo-polizzi force-pushed the auto-importing-shell branch 3 times, most recently from 07c514f to 10a17aa Compare May 23, 2024 08:59
@salvo-polizzi salvo-polizzi requested a review from adamchainz May 30, 2024 15:01
@salvo-polizzi salvo-polizzi marked this pull request as ready for review May 31, 2024 05:27
@salvo-polizzi salvo-polizzi force-pushed the auto-importing-shell branch from bcf6559 to 4de4e1e Compare June 6, 2024 16:28
Copy link
Copy Markdown
Contributor

@DevilsAutumn DevilsAutumn left a comment

Choose a reason for hiding this comment

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

Thanks @salvo-polizzi , I left some comments on the docs. Also, it will be better to add one more docs section under how to override the shell to show how to override the shell to use it for a custom python shell runner.

@salvo-polizzi
Copy link
Copy Markdown
Contributor Author

Hi @DevilsAutumn, thanks for your review. I've started writing the new section on how to override the shell for a customized shell runner. I'll push it in the next few days.

Copy link
Copy Markdown
Member

@adamchainz adamchainz left a comment

Choose a reason for hiding this comment

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

Hello! I just left DjangoCon Europe this morning. I have been discussing this project with other attendees and I only heard positive reactions.

It’s great to see further progress on this PR.

I’ve added some specific comments, but I think now is a good time to also address a couple of pieces of groundwork.

First, we don’t have a Trac ticket, and we should do for any notable change. Salvo, can you create one on Trac, mention it in the PR description (update to match the PR template), and retitle the PR/commit something like “Fixed # -- Added auto-importing to shell command.”.

The ticket description doesn’t need to be long and can link to the wiki and your proposal.

Second, let’s add some missing test coverage in a separate PR. Specifically, I noted that we don’t have any test coverage of the methods that run shells: ipython(), bpython(), and python().

Let’s add those tests in a separate PR with a title like “Refs # -- Improved test coverage of shell command.”. The tests can use this technique to mock the imports of the IPython and bpython, so they don’t actually launch the shell, and don’t need the corresponding packages installed. Similar mocking can be done for the code module’s code.interact() in python().

The tests in this PR can then build on that, checking that the right arguments are passed. They won’t be perfect, since we won’t actually be testing the integration, but they’ll be better than nothing.

@salvo-polizzi salvo-polizzi changed the title [GSOC] feat: auto-importing django shell Fixed #35515 -- Added auto-importing to shell command. Jun 9, 2024
@salvo-polizzi
Copy link
Copy Markdown
Contributor Author

Hi @adamchainz,

I've just changed the PR title and created a Trac ticket for this new feature. It's great to hear that this functionality is very well appreciated by other Django users and contributors, and I'm glad to contribute to this project. In the next few days, I'll work on writing tests to improve test coverage for methods that run the shell. I'm looking forward to seeing how this will turn out.

@salvo-polizzi salvo-polizzi force-pushed the auto-importing-shell branch from 4fa9414 to 31d51df Compare June 10, 2024 07:56
@sarahboyce sarahboyce force-pushed the auto-importing-shell branch 2 times, most recently from d1afe8b to 77727da Compare October 8, 2024 15:06
@sarahboyce sarahboyce force-pushed the auto-importing-shell branch from 77727da to b1d62f7 Compare October 15, 2024 14:35
@nessita
Copy link
Copy Markdown
Contributor

nessita commented Nov 14, 2024

Thank you @salvo-polizzi for this work, I'm performing an in-depth review with the goal to land in the next 1-2 weeks. Sarah and I would love to have this feature merged before the 5.2 feature freeze. I'm also doing some trivial fixes and pushes, such as resolving conflicts.

There is one issue from the printed models (with -v 2) that REALLY bothers me which is that the shown imports does not follow the usual import isort guidelines that we use. (cc/ @adamchainz)

(I'm adding this comment in isolation to start this conversation, other comments may come later as comments or new revnos pushed.)

Specifically, for a simple project with a single custom Django app (testapp), this is what I see:

(djangodev-3.13) nessita@picasso:~/fellowship/projectfromrepo$ python -Wall manage.py shell -v 2
12 objects imported automatically
from testapp.models import BasicModel
from django.contrib.sessions.models import Session
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.models import User, Group, Permission
from django.contrib.admin.models import LogEntry
import django.contrib.admin.models as admin_models
import django.contrib.auth.models as auth_models
import django.contrib.contenttypes.models as contenttypes_models
import django.contrib.sessions.models as sessions_models
import testapp.models as testapp_models
Python 3.13.0 (main, Oct  8 2024, 08:51:27) [GCC 13.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> 

(MY EYES MY EYES) I would rather we indent one level in for the list, and we sort with isort if possible?

(djangodev-3.13) nessita@picasso:~/fellowship/projectfromrepo$ python -Wall manage.py shell -v 2

Automatic imports: 12 objects imported automatically.

    import django.contrib.admin.models as admin_models
    import django.contrib.auth.models as auth_models
    import django.contrib.contenttypes.models as contenttypes_models
    import django.contrib.sessions.models as sessions_models
    from django.contrib.sessions.models import Session
    from django.contrib.contenttypes.models import ContentType
    from django.contrib.auth.models import User, Group, Permission
    from django.contrib.admin.models import LogEntry

    import testapp.models as testapp_models
    from testapp.models import BasicModel

Python 3.13.0 (main, Oct  8 2024, 08:51:27) [GCC 13.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> 

Also, I think I have all the comments in the PR history and I did see the mention of including extra imports, but I think I don't see those in this PR? Are the ones that we agreed on https://forum.djangoproject.com/t/default-automatic-imports-in-the-shell/33708 being added in a follow up PR?

Thanks everyone!!!

@nessita nessita force-pushed the auto-importing-shell branch from b1d62f7 to af76daa Compare November 14, 2024 20:40
@salvo-polizzi
Copy link
Copy Markdown
Contributor Author

Hi @nessita,
Thank you for your valuable review. I apologize for not providing feedback over the past few weeks; I've been quite busy.

Regarding the sorting of imports, I’d like to hear Adam’s thoughts, but personally, I think using isort could be a good solution.

The PR related to default imports is #18560. I haven’t worked on it yet because I was waiting for this PR to be merged first, so we can build the new changes upon it.

@nessita
Copy link
Copy Markdown
Contributor

nessita commented Nov 18, 2024

Hi @nessita, Thank you for your valuable review. I apologize for not providing feedback over the past few weeks; I've been quite busy.

No worries!

Regarding the sorting of imports, I’d like to hear Adam’s thoughts, but personally, I think using isort could be a good solution.

So I've been thinking about this, and I think that we should only sort if the env already has isort available, because at this stage we depend on the env created for the project and not in Django's. Does this make sense?

I also think we should indent the listing, I can push a proposal soon if you don't object.

The PR related to default imports is #18560. I haven’t worked on it yet because I was waiting for this PR to be merged first, so we can build the new changes upon it.

Thank you! Makes sense to not update that until this one is completed and merged.

@sarahboyce
Copy link
Copy Markdown
Contributor

Note that the current order printed is not the same as the order imported and we should probably update this (as duplicate model names overwrite eachother)

@nessita
Copy link
Copy Markdown
Contributor

nessita commented Nov 19, 2024

Note that the current order printed is not the same as the order imported and we should probably update this (as duplicate model names overwrite eachother)

I was thinking whether we could only show the final list of "really" imported paths, that would mean removing from the list the shadowed imports.

@salvo-polizzi Before changing anything, do you think that is doable and reasonably simple? Or does it add too much complexity?

@salvo-polizzi
Copy link
Copy Markdown
Contributor Author

I was thinking whether we could only show the final list of "really" imported paths, that would mean removing from the list the shadowed imports.

Do you mean to only show the models that aren't overwritten?

So I've been thinking about this, and I think that we should only sort if the env already has isort available, because at this stage we depend on the env created for the project and not in Django's. Does this make sense?

I'm sorry if this is straightforward but how can we check if the env has isort available and then use it?

@jacobtylerwalls
Copy link
Copy Markdown
Member

I'm sorry if this is straightforward but how can we check if the env has isort available and then use it?

You could probably take some inspiration from run_formatters(), and possibly #18597.

@salvo-polizzi
Copy link
Copy Markdown
Contributor Author

You could probably take some inspiration from run_formatters(), and possibly #18597.

Thank you for this valuable advice! I do have some concerns about how we should run isort in the shell prompt. Am I correct in understanding that isort can only be run on Python files?

One potential solution could be to import isort and use the isort.code() function. However, I don't think this approach is viable, as it would shift away from relying on the user's environment and instead introduce additional complexity to the Django environment.

@jacobtylerwalls
Copy link
Copy Markdown
Member

Oh, good point that we are unlikely to want to launch an executable like we're doing with black. Sorry, I hadn't thought that far ahead. I bet just try / except'ing import isort would do?

Could I ask you to flesh out a bit what you mean when you say the difference between django & user environments? Are you thinking about having to adjust the development dependencies for django itself to run the tests for this feature?

@salvo-polizzi
Copy link
Copy Markdown
Contributor Author

Could I ask you to flesh out a bit what you mean when you say the difference between django & user environments? Are you thinking about having to adjust the development dependencies for django itself to run the tests for this feature?

Yes, that was what i meant - I didn't expressed myself clearly.

@salvo-polizzi
Copy link
Copy Markdown
Contributor Author

@nessita should I proceed with sorting the imports, or should we wait for more feedback?

@nessita
Copy link
Copy Markdown
Contributor

nessita commented Nov 27, 2024

@nessita should I proceed with sorting the imports, or should we wait for more feedback?

I'm very much in favor of this, could you please confirm if this is doable in a reasonable timeframe? Also, do you have the availability? And lastly, do you think this would be a desirable improvement?

Thanks!

@salvo-polizzi
Copy link
Copy Markdown
Contributor Author

salvo-polizzi commented Nov 28, 2024

I'm very much in favor of this, could you please confirm if this is doable in a reasonable timeframe? Also, do you have the availability? And lastly, do you think this would be a desirable improvement?

Thanks!

Yes, I think it is a good improvement showing the imports in the right order and I think it is a pretty straightforward change to implement (i'm just about to complete the implementation 😊) . Let me know if you have any additional thoughts or requirements!

@nessita
Copy link
Copy Markdown
Contributor

nessita commented Nov 29, 2024

I'm very much in favor of this, could you please confirm if this is doable in a reasonable timeframe? Also, do you have the availability? And lastly, do you think this would be a desirable improvement?
Thanks!

Yes, I think it is a good improvement showing the imports in the right order and I think it is a pretty straightforward change to implement (i'm just about to complete the implementation 😊) . Let me know if you have any additional thoughts or requirements!

Thank you @salvo-polizzi! I ran out of time this week but this PR will be my priority next week. I may push some fixes and let you know. Until then, would you please consider fixing the conflict in the release notes?

@salvo-polizzi
Copy link
Copy Markdown
Contributor Author

@nessita Following our discussion on the forum, I plan to complete all the requested changes by the end of this week.

@nessita
Copy link
Copy Markdown
Contributor

nessita commented Dec 13, 2024

@nessita Following our discussion on the forum, I plan to complete all the requested changes by the end of this week.

Thank you so much, I really appreciate this!

Copy link
Copy Markdown
Contributor

@nessita nessita left a comment

Choose a reason for hiding this comment

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

Thank you @salvo-polizzi for your latest changes. I have pushed a set of tweaks, including a minor refactor of the tests. I think this is ready for merge! 🌟

Independently of that, I have one question that arises from the tests. Specifically, for test_message_with_stdout_listing_objects_with_isort_not_installed, the info message says ... 5 objects imported... but the rest, for -v 2, shows 3 objects:

  from django.contrib.contenttypes.models import ContentType
  from shell.models import Phone, Marker

I understand that the other two are the customized MyClass and constant, but somehow these are not showing in the "full list" and it feels unexpected. Have you given this any consideration?

@salvo-polizzi
Copy link
Copy Markdown
Contributor Author

Thank you, @nessita, for your work on this PR—I really appreciate it.

I understand that the other two are the customized MyClass and constant, but somehow these are not showing in the "full list" and it feels unexpected. Have you given this any consideration?

Regarding MyClass and constant not appearing in the "full list," I believe this is due to our decision not to show nested classes or variables in the list. Here's the link to the discussion for reference. Let me know if I misinterpreted your question.

@nessita
Copy link
Copy Markdown
Contributor

nessita commented Jan 9, 2025

Regarding MyClass and constant not appearing in the "full list," I believe this is due to our decision not to show nested classes or variables in the list. Here's the link to the discussion for reference. Let me know if I misinterpreted your question.

@salvo-polizzi Thank you, this answers my question. I guess then that my proposal of appending "Full list" should be revisited since the list that we show is not a complete list. Perhaps we should say "Including" or similar, like Sarah suggested? Do you have a preference/recommendation?

@salvo-polizzi
Copy link
Copy Markdown
Contributor Author

Perhaps we should say "Including" or similar, like Sarah suggested? Do you have a preference/recommendation?

I think that something like "including" should be good.

…mand.

Thanks to Bhuvnesh Sharma and Adam Johnson for mentoring this Google
Summer of Code 2024 project. Thanks to Sarah Boyce, David Smith, Jacob
Walls and Natalia Bidart for reviews.
Copy link
Copy Markdown
Member

@pauloxnet pauloxnet left a comment

Choose a reason for hiding this comment

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

Great job. 👏
Sorry I was late with the review, I had added a couple of comments that were absolutely not blocking for the PR. 😅

@salvo-polizzi
Copy link
Copy Markdown
Contributor Author

Thanks @pauloxnet for your valuable feedback 👍

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.

8 participants