python / cpython

The Python programming language
https://www.python.org
Other
63.12k stars 30.22k forks source link

Implemenation of ImportNotFoundError PEP #34908

Closed 1d332e54-3b86-44a9-8fc6-e0c6e60d2a36 closed 23 years ago

1d332e54-3b86-44a9-8fc6-e0c6e60d2a36 commented 23 years ago
BPO 448488
Nosy @gvanrossum, @loewis
Files
  • 2patch1.cdiff: sample implementation (Warning: also contains 3 functions of patch 448305)
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields: ```python assignee = None closed_at = created_at = labels = ['interpreter-core'] title = 'Implemenation of ImportNotFoundError PEP' updated_at = user = 'https://bugs.python.org/giacometti' ``` bugs.python.org fields: ```python activity = actor = 'loewis' assignee = 'none' closed = True closed_date = None closer = None components = ['Interpreter Core'] creation = creator = 'giacometti' dependencies = [] files = ['3497'] hgrepos = [] issue_num = 448488 keywords = ['patch'] message_count = 9.0 messages = ['37200', '37201', '37202', '37203', '37204', '37205', '37206', '37207', '37208'] nosy_count = 3.0 nosy_names = ['gvanrossum', 'loewis', 'giacometti'] pr_nums = [] priority = 'normal' resolution = 'rejected' stage = None status = 'closed' superseder = None type = None url = 'https://bugs.python.org/issue448488' versions = [] ```

    1d332e54-3b86-44a9-8fc6-e0c6e60d2a36 commented 23 years ago

    This is a reference implementation for the PEP proposal. Here, the patch has existed before the PEP, therefore the PEP refers to the patch... I need to create the patch before I submit the PEP :)))

    Frederic Giacometti

    -----------------------

    PEP XXX: ImportNotFoundError Exception

    fred@arakne.com (Frederic Giacometti)

    Abstract

    The object of this proposal is the definition of a specific ImportNotFoundError error, along with its association to a new import exception trapping mechanism that will enable the extension of the import mechanism to generic global/static objects.

    Part 1: The ImportNotFoundError exception.

    In the present code, the ImportError string exception is used for all import failures; this does not permit us to catch efficiently import failures caused specifically by a 'component not found' situation.

    To fix this, we introduce a new exception, that we call ImportNotFoundError. This exception is designed for use in the very specific situation where an import string cannot be found by the import mechanism (as opposed to 'found' but 'failed to open', failed to load', 'failed to initialize'...).

    ImportNotFoundError instance raised within execution of an import statement will have two attributes as follows:

    A formal Python definition of ImportErrorNotFound could be:

            class ImportNotFoundError( ImportError):
                def __init__( self, parent, name):
               assert( hasattr( parent, '__name__'))
               assert( sys.modules.has_key( parent.__name__))
               assert( sys.modules[ parent.__name__] is parent)
               assert( not hasattr( parent, name))
               ImportError.__init__( self, parent, name)
               self.parent = parent
               self.name = name

    2) Alternative import customization hook in the module global scope (altimp)

    2.a: Why in the module global scope? (or: Why not overwrite builtins.__import__?)

    Use of builtins.__import is not a preferred integration practice: Only one __import value can be defined; and if you're integrating two components with each its own __import__ version, you'll be stuck...

    Furthermore, if one insists on overwriting __import, one can still do it. Meanwhile, if an application requires overwriting __import, and one does not want to, one is stuck again...

    Since we want to keep options open, we're proposing to enhance the import procedure defining a trapping mechanism based on the presence of a globals()[ '__altimp__'] object, which is called if present upon trigger of an ImportNotFoundError within an import statement.

    In doing so, those who don't want (rightfully) to be bothered won't be, unless they define __altimp in their module global scope, and a module's usage of a certain implementation of __altimp won't bother other modules, at all.

    2.b Definition

    If present, globals()[ '__altimp__'] will refer to a callable object specified as follows:

    def importextended( failedparent, failedname, name,
                        globs=None, locs=None, fromlist=None):
       failedparent: 'parent' attribute from the ImportnotFoundError exception
       failedname:   'name' attribute from the ImportnotFoundError exception
       (name, globs, locs, fromlist): same arguments as in __import__()

    return: same as in __import__() [object for insertion in sys.modules...]

    Effect on existing code

    There should be no effect on existing code, other than when code like exc.__class__ == ImportError has been meant for isinstance( exc, ImportError)

    (hopeless anyway? :(()

    Applications:

    Given that importext.py contains:

      __all__ = ('importextended')
    
      import sys
    
      def importextended( failedparent, failedname, name,
                          globs=None, locs=None, fromlist=None):
          curmod = failedparent
          remainder = name[ len( curmod.__name__) + 1:].split( '.')
          while remainder:
              curname = remainder.pop( 0)
              curmod = getattr( curmod, curname)
              sys.modules[ curmod.__name__] = curmod
          return curmod

    A JPE file may contain:

    # import Java's javax.swing package in global scope

      from importext import importextended as __altimport__
      from java import Jstring
      from java.system.javax import swing
    
      frame = swing.JFrame( Jstring( 'HelloWorldSwing'))
      label = swing.JLabel( Jstring( 'Hello World'))
      frame.getContentPane().add(label)
      frame.setDefaultCloseOperation( swing.JFrame.EXIT_ON_CLOSE)
      frame.pack()
      frame.setVisible( 1)

    A Metadynamic Python file can contain:

      # User is a Metaphase persistant class
      from importext import importextended as __altimport__
      from metadyn.mclass import User
    
      users = mclass.User.QueryDbItem( some_query)
      ...

    Based on this feature, applications can easily be programmed on the client side for any client/server situation, including pulling Python modules from remote repository...

    Reference Implementation:

    A sample implementation of ImportErrorNotFoundError in the current import mechanism, with the __altimp__ hook is provided in patch href:attached_file.

    61337411-43fc-4a9c-b8d5-4060aede66d0 commented 23 years ago

    Logged In: YES user_id=21627

    There is in general no need to put the PEP into the SF comment. Just send it to the PEP editor, who should assign a PEP number and commit it into CVS in a timely manner.

    I have a number of comments on this PEP, but I'll send them directly to you once the PEP is on python.sf.net/peps.

    gvanrossum commented 23 years ago

    Logged In: YES user_id=6380

    Rejected. The "PEP" doesn't provide a motivation for the ImportNotFound error, and I don't see a reason why this particular condition should be excepted.

    The __altimp__ idea is a nice one, but the elaboration here is incomprehensible. I suggest that you submit this idea as a separate patch.

    1d332e54-3b86-44a9-8fc6-e0c6e60d2a36 commented 23 years ago

    Logged In: YES user_id=93657

    The 'motivation' is at the beginning of the first section of the PEP. I'm pasting it below:

    Part 1: The ImportNotFoundError exception.

                     In the present code, the ImportError

    string exception is used for all import failures; this does not permit us to catch efficiently import failures caused specifically by a 'component not found' situation.

                     To fix this, we introduce a new

    exception, that we call ImportNotFoundError. This exception is designed for use in the very specific situation where an import string cannot be found by the import mechanism (as opposed to 'found' but 'failed to open', failed to load', 'failed to initialize'...).

    gvanrossum commented 23 years ago

    Logged In: YES user_id=6380

    What's missing is why an application would need to catch this kind of import failures. I have a limited imagination, and I can't come up with a scenario that would require this. That's what I meant by "motivation".

    1d332e54-3b86-44a9-8fc6-e0c6e60d2a36 commented 23 years ago

    Logged In: YES user_id=93657

    There are two application scenarios for __altimp, and the __altimp implementation requires ImportNotFoundError.

    The introduction of ImportNotFoundError is the only way I could find to implement efficiently __altimp__ .

    I'm giving below an similar implementation in Python that overrides __import__ (with the inconvenient that this affects all import statements). A possible importextended function is defined in the [sample] 'Application' section of the PEP :

    ------------

    pyimport = __import__
    def altimport(  name, globs=None, locs=None, fromlist=None):
        try:
            return pyimport( globs, locs, fromlist)
        except ImportNotFoundError, iexc:
            return importextended( iexc.parent, iexc.name, name,
    globs, locs, fromlist)
    
    sys.__buildins__.__import__ = altimport

    Let's make clear that in this case I only want to catch the ImportError's corresponding to something whose import fails because it is not on the filesystem (not on PYTHONPATH....), and that I do not want to catch any other case of ImportError that could be raised. Thus, a subclass of ImportError is needed: ImportNotFoundError.

    FG

    gvanrossum commented 23 years ago

    Logged In: YES user_id=6380

    The existing import hooks have ways to do this without catching the exception.

    1d332e54-3b86-44a9-8fc6-e0c6e60d2a36 commented 23 years ago

    Logged In: YES user_id=93657

    How? You could at least give a pointer, a word, something!

    FG

    61337411-43fc-4a9c-b8d5-4060aede66d0 commented 23 years ago

    Logged In: YES user_id=21627

    One option is to subclass ihooks.ModuleLoader and redefine find_module and find_module_in_dir, always calling the base class method and taking action only when those return None.

    It seems that ImportNotFound is not precisely specified. Suppose I have

    #foo.py
    import bar
    print "Done"

    and suppose bar is not available. Now, the application performs

    import foo

    Should it get an ImportNotFoundError? Importing bar probably raised one, but foo.py is present, so importing foo should raise a plain ImportError.