Skip to content

Instantly share code, notes, and snippets.

@salvo-polizzi
Last active May 2, 2024 07:09
Show Gist options
  • Save salvo-polizzi/304b8cd001e7ccef95e7f1d1b57bdea4 to your computer and use it in GitHub Desktop.
Save salvo-polizzi/304b8cd001e7ccef95e7f1d1b57bdea4 to your computer and use it in GitHub Desktop.
Shell Plus - GSOC 2024

SHELL PLUS

Table Of Content

  1. Abstract
    • 1.1 Overview
    • 1.2 Goals
    • 1.3 Benefits
  2. The Solution
    • 2.1 Create get_namespace method
    • 2.2 Create import_items method
    • 2.3 Document how to subclass shell
  3. Schedule

1.Abstract

1.1 Overview

One of the most used django-extension is shell-plus: a shell that auto-imports models for you. For instance, imagine a django project with several models, and you need to try some operation using the shell: in that case it would be very useful a tool that auto-import model for you. Furthermore, maybe you'd like to import extra-things (modules from other libraries, function that you wrote, ecc.): in that case it may be cool if you can customize things you need to import, overriding only a single method.

1.2 Goals

The proposal is about to modify existing shell.py in order to auto-import models of a project and let the developer customize his imports.

  1. The first milestone is to create a method get_namespace that maps every model to its name/module and use this for importing the models.
  2. The second milestone is to make this method overridable for changing the imports or adding new ones. I'll create an import_items method that the developer can use for importing extra-items.
  3. The third milestone will be to document how an user can extend get_namespace in order to import extra-things.

1.3 Benefits

  • If you want to try operations for each model of your app, you don't have to manually import the models
  • The developer can create his customized shell with customized imports

2.The Solution

2.1 Create get_namespace method

First of all, we can add a method, such get_apps_and_models that return a dictionary associating for every module found, a list of associated models. The core function get_namespace will import all objects. At the end we return imported_objects. The signature of the method will be something like:

def get_namespace(self, options, style)
...
  1. We need to import apps from the existing project in order to associate the model_module to the model using app_config:
    def get_apps_and_models(self):
        for app_conf in apps.get_app_configs():
            if app_conf.models_module:
                yield app_conf.models_module, app_conf.get_models()
    ...
  1. For each app, we try to import each model:
...
for model in app_models:
    if model.__module__:
        try:
            imported_objects[model.__name__] = import_string(
                "%s.%s" % (model.__module__, model.__name__)
            )
        except ImportError as e:
            if options.get("traceback"):
                traceback.print_exc()
            else:
                print(
                    style.ERROR(
                        "Failed to import %s from %s, reason %s"
                        % (model.__name__, model.__module__, e)
                    )
                )
...

At this point we imported all objects in INSTALLED_APPS order, so in the case of two or more models with the same name we pick the first in the order mentioned before.

  1. Finally we return imported_objects

2.2 Create import_items method

  1. At this time we want something that can help the user to import extra-items. The idea of import_items is to get a dictionary that maps for each module all the objects to import like this:
IMPORTS = {'os': [], 'an_app': ['submodule1', 'submodule2']}

If the list is empty, we intend like the user wants to import everything from that module, so we use the importlib.import_module function adding to imported_objects all objects imported for that module, otherwise we import only the objects requested for that module with importlib.__import__.

  1. The signature of the method could be like:
def import_items(import_dict, imported_objects, style)
  1. Finally we import all objects:
...

for module, objects in import_dict.items():
    try:
        if len(objects) > 0:
            imported_objects[module] = importlib.__import__(module, {}, {}, objects) 
        else:
            imported_objects[module] = importlib.import_module(module)
    except as ImportError:
        print(style.ERROR(f"Unable to import {module}"))
...

2.3 Document how to subclass the shell

The most important goal to achieve in this project is to make shell customizable by the user: in order to do that we have to explain how the user can override the get_namespace method: my idea is to add a section to docs/howto/custom-management-commands.txt and explain what to do:

  1. Explain how to create your custom dictionary of imports
  2. Explain how to override get_namespace and show some example like:
class Command(shell.Command):
    def get_namespace(self, options, style):
        import_dict = {}
        imported_objects = super().get_namespace(options, style)
        import_items(import_dict, imported_objects)

3.Schedule

My exams will start in June and will finish in the end of july. So that I think I would be able to devote 15-20 hours a week throughout GSOC period. I would like to devote 50% of my time to learning and coding, and 50% of my time to test the changes and write documentation for new stuff. I also will keep in touch with the community and mentors using django-forum and discord.

3.1 Community Bonding (May 1 - May 26)

  • Discuss implementation with mentors
  • Figure out any better approach for the solution

3.2 Create get_namespace method -- first milestone

(From May 27 - June 30)The goal in this phase is to implement the core function for auto-importing models.

3.2.1 Updating shell command and creating get_namespace method (1 week)

  • Modify imported_objects in ipython, b_python and python
  • Write unit tests and verify that fails for new imported_objects

3.2.2 Create a method that associate every module to his app (1 week)

  • Create the method
  • Write unit tests
  • Documentation (if required)

3.2.3 Write the core of get_namespace method (2-3 week)

  • Import all models
  • Write unit tests
  • Documentation (if required)

3.3 Create import_items method -- second milestone

(From July 1 - July 16)The goal in this phase is to implement a function that helps user to customize his auto-importing shell.

3.3.1 Defining the method behavior (1-2 weeks)

  • Implement a prototype
  • Write unit tests
  • Documentation

3.4 Document how user can subclass shell

(From July 16 - August 10)The goal in this phase is to create an exaustive documentation about the new functionality and how user can subclass the shell

3.4.1 Writing docs

4.About Me

My name is Salvo Polizzi and I am a Pre-final year Btech. student from University Of Catania (Italy). I started coding when join the university with C++ then used python for some projects. I started learnig Django because interested in web framework and how they are built.

Past Contributions in django

I started contributing in django in December 2023 and I found a lot of help from all the community.

Pull Requests

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