code completion doesn't work for inhrtd class

2007-02-24
2013-03-15
  • Hi,

    I think PyDev Code Completion feature doesn't work for Inherited Classes.

    Let me give an example:

    class Log:
        def msg():
            ....
       
        def err():
            ....

    class Archiver:
        def compress():
            ....

        def uncompress():
            ....

    class Starter(Log, Archiver):
        pass

    StartInstance = Starter()

    Now if I do StartInstance.[CTRL + Space], shouldn't the methods from all the base classes be displayed ?
    Currently, nothing is displayed.

    Thanks,
    Ritesh

     
    • Hi,

      I confirmed the same in iPython.
      There it works perfectly. The instance is able to get the methods of the base class also.

      Thanks,
      Ritesh

       
      • Fabio Zadrozny
        Fabio Zadrozny
        2007-02-24

        The example below works well for me... which pydev version do you have?

        class Log:
           def msg(self):
               pass

           def err(self):
               pass

        class Archiver:
           def compress(self):
               pass

           def uncompress(self):
               pass

        class Starter(Log, Archiver):
           pass

        StartInstance = Starter()
        StartInstance.

         
        • I'm currently running 1.2.7. I was earlier running 1.2.6.
          On both I see the same problem.

          I check everything but still, are there any special settings to be set in pydev ?

          Thanks,
          Ritesh

           
    • I think my understanding of Python is still very low and there is more to learn.

      The issue happens if Starter is defined and instantiated in a function foo().
      If Starter is defined and instantiated in main(), there are no problems.

      Thanks,
      Ritesh

       
      • Fabio Zadrozny
        Fabio Zadrozny
        2007-02-24

        Can you pass me the exact code in which it is not working?

        Sometimes code-completion is hard to do in python, because types are not explicit in the language, so, you may be hitting a limitation of the type-inference engine (some analysis are sometimes not available because they are too time-consuming... or are simply not possible).

        Cheers,

        Fabio

         
        • Okay!!

          I have 3 classes defined in a file, foo.py

          class Log:
              '''A OOP implementation for logging.
              warnings is to tackle the warning option
              verbose is to tackle the verbose option
              debug is to tackle the debug option
             
              You should pass these options, taking it from optparse/getopt,
              during instantiation'''
             
              def __init__(self, warnings=None, verbose=None, debug=None):
                 
                  if warnings is True:
                      self.WARN = True
                  else: self.WARN = False
                 
                  if verbose is True:
                      self.VERBOSE = True
                  else: self.VERBOSE = False
                 
                  if debug is True:
                      self.DEBUG = True
                  else: self.DEBUG = False
                 
              def msg(self, msg):
                  sys.stdout.write(msg)
                  sys.stdout.flush()
                 
              def err(self, msg):
                  sys.stderr.write(msg)
                  sys.stderr.flush()
             
              # For the rest, we need to check the options also
              def warn(self, msg):
                  if self.WARN is True:
                  #if pypt_variables.options.warnings is True:
                      sys.stderr.write(msg)
                      sys.stderr.flush()

              def verbose(self, msg):
                  if self.VERBOSE is True:
                  #if pypt_variables.options.verbose is True:
                      sys.stdout.write(msg)
                      sys.stdout.flush()
                     
              def debug(self, msg):
                  if self.DEBUG is True:
                  #if pypt_variables.options.debug is True:
                      sys.stdout.write(msg)
                      sys.stdout.flush()

          class ProgressBar:
              def __init__(self, minValue = 0, maxValue = 0, width = None, fd = sys.stderr):
                  if sys.platform == "win32":
                      pass
                  else:
                      from fcntl import ioctl
                      import termios
                 
                  #width does NOT include the two places for [] markers
                  self.min = minValue
                  self.max = maxValue
                  print self.max
                  print self.min
                  self.span = float(self.max - self.min)
                  self.fd = fd
                  self.signal_set = False
                  if width is None:
                      try:
                          self.handle_resize(None, None)
                          signal.signal(signal.SIGWINCH, self.handle_resize)
                          self.signal_set = True
                      except:
                          self.width = 79 #The standard
                  else:
                      self.width = width
                  self.value = self.min
                  self.items = 0 #count of items being tracked
                  self.complete = 0
                 
              def handle_resize(self, signum, frame):
                  h,w=array('h', ioctl(self.fd,termios.TIOCGWINSZ,'\0'*8))[:2]
                  self.width = w
             
              def updateValue(self, newValue):
                  #require caller to supply a value! newValue is the increment from last call
                  self.value = max(self.min, min(self.max, self.value + newValue))
                  self.display()
                 
              def completed(self):
                  self.complete = self.complete + 1
                  if self.signal_set:
                      signal.signal(signal.SIGWINCH, signal.SIG_DFL)
                  self.display()
                 
              def addItem(self, maxValue):
                  self.max = self.max + maxValue
                  self.span = float(self.max - self.min)
                  self.items = self.items + 1
                  self.display()
                 
              def display(self):
                  print "\r%3s/%3s items: %s\r" % (self.complete, self.items, str(self)),
                 
              def __str__(self):
                  #compute display fraction
                  percentFilled = ((self.value - self.min) / self.span)
                  widthFilled = int(self.width * percentFilled + 0.5)
                  return ("[" + "#"*widthFilled + " "*(self.width - widthFilled) + "]" + " %5.1f%% of %d KB" % (percentFilled * 100.0, self.max/1024))

          class Archiver:
                 
              def TarGzipBZ2_Uncompress(self, SourceFileHandle, TargetFileHandle):
                  try:
                      TargetFileHandle.write(SourceFileHandle.read() )
                  except EOFError:
                      pass
                  return True
                 
              def compress_the_file(self, zip_file_name, files_to_compress):
                  '''Condenses all the files into one single file for easy transfer'''
                 
                  try:
                      filename = zipfile.ZipFile(zip_file_name, "a")
                  except IOError:
                      #INFO: By design zipfile throws an IOError exception when you open
                      # in "append" mode and the file is not present.
                      filename = zipfile.ZipFile(zip_file_name, "w")
                  except:
                      #TODO Handle the exception
                      return False
                             
                  filename.write(files_to_compress, files_to_compress, zipfile.ZIP_DEFLATED)                       
                  filename.close()
                  return True
                 
              def decompress_the_file(self, archive_file, path, target_file, archive_type):
                  '''Extracts all the files from a single condensed archive file'''
                 
                 
                  if archive_type is 1:
                      try:
                          read_from = bz2.BZ2File(archive_file, 'r')
                      except:
                          return False
                                     
                      try:
                          write_to = open (os.path.join(path, filename), 'wb')
                      except:
                          return False
                                     
                      if TarGzipBZ2_Uncomprerssed(read_from, write_to) != True:
                          raise ArchiveError
                     
                      write_to.close()
                      read_from.close()
                      return True
                     
                  elif archive_type is 2:
                      try:
                          read_from = gzip.GzipFile(file, 'r')
                      except:
                          return False
                                     
                      try:
                          write_to = open(os.path.join(path,filename), 'wb')
                      except:
                          return False           
                     
                      if TarGzipBZ2_Uncomprerssed(read_from, write_to) != True:
                          raise ArchiveError
                     
                      write_to.close()
                      read_from.close()
                      return True
                     
                  elif archive_type is 3:
                      # FIXME: This looks odd. Where are we writing to a file ???
                      try:
                          zip_file = zipfile.ZipFile(file, 'rb')
                      except:
                          return False
                         
                      for filename in zip_file.namelist():
                          data = zip_file.read()
                         
                      zip_file.close()
                      return True
                  else:
                      return False

          class ParseVariables:
              """Contains most of the variables that are required by the application to run.
              Also does command-line option parsing and variable validation."""

              version = "0.6.4"
              copyright = "(C) 2005 - 2007 Ritesh Raj Sarraf - RESEARCHUT (http://www.researchut.com/)"
                     
              errlist = []
              supported_platforms = ["Linux", "GNU/kFreeBSD", "GNU"]
              apt_update_target_path = '/var/lib/apt/lists/'
              apt_package_target_path = '/var/cache/apt/archives/'
              # Dummy paths while testing on Windows
              #apt_update_target_path = 'C:\\temp'
              #apt_package_target_path = 'C:\\temp'
                
              parser = optparse.OptionParser(usage="%prog [OPTION1, OPTION2, ...]", version="%prog " + version + "\n" + copyright)
                
              parser.add_option("-d","--download-dir", dest="download_dir", help="Root directory path to save the downloaded files", action="store", type="string", metavar="pypt-downloads")
              parser.add_option("-s","--cache-dir", dest="cache_dir", help="Root directory path where the pre-downloaded files will be searched. If not, give a period '.'",action="store", type="string", metavar=".")
              parser.add_option("--verbose", dest="verbose", help="Enable verbose messages", action="store_true")
              parser.add_option("--warnings", dest="warnings", help="Enable warnings", action="store_true")
              parser.add_option("--debug", dest="debug", help="Enable Debug mode", action="store_true")
              parser.add_option("-u","--uris", dest="uris_file", help="Full path of the uris file which contains the main database of files to be downloaded",action="store", type="string")
              parser.add_option("","--disable-md5check", dest="disable_md5check", help="Disable md5checksum validation on downloaded files", action="store_true")
              parser.add_option("", "--threads", dest="num_of_threads", help="Number of threads to spawn", action="store", type="int", metavar="1", default=1)
                
              #INFO: Option zip is not enabled by default but is highly encouraged.
              parser.add_option("-z","--zip", dest="zip_it", help="Zip the downloaded files to a single zip file", action="store_true")
              parser.add_option("--zip-update-file", dest="zip_update_file", help="Default zip file for downloaded (update) data", action="store", type="string", metavar="pypt-offline-update.zip", default="pypt-offline-update.zip")
              parser.add_option("--zip-upgrade-file", dest="zip_upgrade_file", help="Default zip file for downloaded (upgrade) data", action="store", type="string", metavar="pypt-offline-upgrade.zip", default="pypt-offline-upgrade.zip")
                
              #INFO: At the moment nargs cannot be set to something like * so that optparse could manipulate n number of args. This is a limitation in optparse at the moment. The author might add this feature in the future.
              # When fixed by the author, we'd be in a better shape to use the above mentioned line instead of relying on this improper way.
              # With action="store_true", we are able to store all the arguments into the args variable from where it can be fetched later.
              #parser.add_option("", "--set-install-packages", dest="set_install_packages", help="Extract the list of uris which need to be fetched for installation of the given package and its dependencies", action="store", type="string", nargs=10, metavar="package_name")
              parser.add_option("", "--set-install", dest="set_install", help="Extract the list of uris which need to be fetched for installation of the given package and its dependencies", action="store", metavar="pypt-offline-install.dat")
              parser.add_option("", "--set-install-packages", dest="set_install_packages", help="Name of the packages which need to be fetched", action="store_true", metavar="package_names")
                
              parser.add_option("", "--set-update", dest="set_update", help="Extract the list of uris which need to be fetched for updation", action="store", type="string", metavar="pypt-offline-update.dat")
              parser.add_option("", "--fetch-update", dest="fetch_update", help="Fetch the list of uris which are needed for apt's databases _updation_. This command must be executed on the WITHNET machine", action="store", type="string", metavar="pypt-offline-update.dat")
              parser.add_option("", "--install-update", dest="install_update", help="Install the fetched database files to the  NONET machine and _update_ the apt database on the NONET machine. This command must be executed on the NONET machine", action="store", type="string", metavar="pypt-offline-update.zip")
              parser.add_option("", "--set-upgrade", dest="set_upgrade", help="Extract the list of uris which need to be fetched for _upgradation_", action="store", type="string", metavar="pypt-offline-upgrade.dat")
              parser.add_option("", "--upgrade-type", dest="upgrade_type", help="Type of upgrade to do. Use one of upgrade, dist-upgrade, dselect-ugprade", action="store", type="string", metavar="upgrade")
              parser.add_option("", "--fetch-upgrade", dest="fetch_upgrade", help="Fetch the list of uris which are needed for apt's databases _upgradation_. This command must be executed on the WITHNET machine", action="store", type="string", metavar="pypt-offline-upgrade.dat")
              parser.add_option("", "--install-upgrade", dest="install_upgrade", help="Install the fetched packages to the  NONET machine and _upgrade_ the packages on the NONET machine. This command must be executed on the NONET machine", action="store", type="string", metavar="pypt-offline-upgrade.zip")
              (options, args) = parser.parse_args()

          def fetcher(variables, url_file, download_dir, cache_dir, zip_bool, zip_type_file, arg_type = 0):
              '''
              uri - The uri data whill will contain the information
              path - The path (if any) where the download needs to be done
              cache - The cache (if any) where we should check before downloading from the net
              arg_type - arg_type is basically used to identify wether it's a update download or upgrade download
              '''
             
              class Fetcher(DataGetter, ProgressBar, Archiver, DataIntegrity, Log):
                  def __init__(self, width):
                      self.width = width
              FetcherInstance = Fetcher(width=30)
              FetcherInstance.err("Ritesh.\n")
              # Hi Fabioz, At this moment, code completion for FetcherInstance doesn't work.
              # If I declare this class outside the fetcher(), then, there, the code completion feature works

          def main():
              '''Here we basically do the sanity checks, some validations
              and then accordingly call the corresponding functions.'''
             
              variables = ParseVariables()

              try:
                  # Hi Fabioz, Even though I make this global. When I try to use the log instance inside some functions, code completion doesn't work.
                  global log
                  log = Log(variables.options.warnings, variables.options.verbose, variables.options.debug)
                 
                  log.msg("pypt-offline %s\n" % (variables.version))
                  log.msg("Copyright %s\n" % (variables.copyright))

                  if variables.options.fetch_update:
                  # The code completion also doesn't work here when I do variables.options.[CTRL + Space]
                      if os.access(variables.options.fetch_update, os.F_OK):
                          log.msg("\nFetching uris which update apt's package database\n\n")
                          variables.options.disable_md5check = True
                          # Since we're in fetch_update, the download_type will be non-deb/rpm data
                          # 1 is for update packages
                          # 2 is for upgrade packages
                          fetcher(variables, 1)
                      else:
                          log.err("\n%s file not present. Check path.\n" % (variables.options.fetch_update) )
                          sys.exit(1)

           
          • Hi Fabioz,

            Do you think we should file a bug for this so that it can be addressed sometime later (if not now) ?

            Thanks,
            Ritesh

             
            • Fabio Zadrozny
              Fabio Zadrozny
              2007-02-24

              Ok, so, your problem seems much more related to the global statement, not to having a subclass... mostly, the code-completion engine will not consider globals in your namespace as globals (unless you did an assign in the global module)... so, yes, you can add a bug for that.

              class Log:
                  def method1(self):
                      pass
                 
              def main():
                  global logger
                  logger = Log()
                  logger.|<-- works here
                 
              def otherFunction():
                  logger. |<-- won't work here

              Cheers,

              Fabio

               
              • No,

                The global statement related bug was just one of it.

                Even if you define a class by inheriting from other base classes, the methods of the base classes aren't available in code completion _if_ the class is being defined in a function. I'm not sure if this is logically allowed in Python or not. But all I can say is that iPython (with the same instantiation method mentioned above) is able to access all the methods of all the base classes.

                One thing more of note is that if I import a custom module of my own and try to use its methods, code-completions doesn't work there too..

                Thanks,
                Ritesh

                 
                • Fabio Zadrozny
                  Fabio Zadrozny
                  2007-02-24

                  The methods in the class or all the methods? If it's all, that means that it is not finding the class definition... ipython is based on a shell, so it has runtime information that pydev does not have (in that level it works solely on the source-code -- it does have a shell, but it's only used for builtins).

                  Cheers,

                  Fabio

                   
                  • The methods of the base class. Like if Archiver is a base class and compress is a method of class Archiver:

                    class Archiver:
                        def compress():
                           ....

                    class Foo:
                        def bar():
                            ...

                    def some_function():
                        class Starter(Archiver, Foo):
                            pass

                        Start = Starter()
                        Start.[CTRL + Space]

                    Now,
                    Start.[CTRL + Space] doesn't give me _any_ results. Nothing is listed there.

                    But if I write the code correctly without code completion, the code works fine. That proves that the code is correct.
                    That brings me down to:
                    a) Either Python is only able to reference at Runtime (which pydev doesn't do).
                    b) Or pydev currently has some bug in code completion because of which it is not able display all methods.

                    I hope I've been able to describe the problem.

                    Thanks,
                    Ritesh

                     
                    • Fabio Zadrozny
                      Fabio Zadrozny
                      2007-02-24

                      Yeap, seems a bug in pydev... I think that it is better as described with:

                      def some_function():
                         class Starter:
                             def m1(self):
                                 pass

                         Starter.|<-- ctrl+space is not working here (should bring m1)

                      because it's related to declaring a class in a nested scope, and not with the superclasses...

                      Cheers,

                      Fabio