Lichen

Changeset

418:70d826ac7510
2016-12-15 Paul Boddie raw files shortlog changelog graph Added a method to test whether an inter-module dependency applies. Expanded and formatted the dependency report when modules cannot be ordered. Reorganised testing for callable references.
deducer.py (file) importer.py (file)
     1.1 --- a/deducer.py	Thu Dec 15 23:50:05 2016 +0100
     1.2 +++ b/deducer.py	Thu Dec 15 23:52:11 2016 +0100
     1.3 @@ -671,12 +671,12 @@
     1.4                  else:
     1.5                      raise DeduceError("Cannot find module for path %s." % path)
     1.6  
     1.7 -            # Identify usage of callables employing dynamic defaults.
     1.8 +            # Identify references providing dependencies.
     1.9  
    1.10              for attrtype, objtype, attr in referenced_attrs:
    1.11 -                if self.importer.uses_dynamic_callable(attr):
    1.12 +                if not attr.unresolved():
    1.13                      provider = self.importer.get_module_provider(attr)
    1.14 -                    self.importer.add_provider(path, provider)
    1.15 +                    self.importer.test_dependency(attr, path, provider)
    1.16  
    1.17      def get_referenced_attrs(self, location):
    1.18  
     2.1 --- a/importer.py	Thu Dec 15 23:50:05 2016 +0100
     2.2 +++ b/importer.py	Thu Dec 15 23:52:11 2016 +0100
     2.3 @@ -445,18 +445,19 @@
     2.4                          if module.name not in self.required:
     2.5                              init_item(self.waiting, module.name, set)
     2.6                              self.waiting[module.name].add(provider)
     2.7 +                            if self.verbose:
     2.8 +                                print >>sys.stderr, "Noting", provider, "for", ref, "from", module.name
     2.9  
    2.10                          # Make this module required in the accessing module.
    2.11  
    2.12                          elif provider not in self.required:
    2.13                              self.required.add(provider)
    2.14                              if self.verbose:
    2.15 -                                print >>sys.stderr, "Requiring", provider, "for", ref
    2.16 +                                print >>sys.stderr, "Requiring", provider, "for", ref, "from", module.name
    2.17  
    2.18                          # Record a module ordering dependency.
    2.19  
    2.20 -                        if not found.static() or self.uses_dynamic_callable(found):
    2.21 -                            self.add_provider(module.name, provider)
    2.22 +                        self.test_dependency(found, module.name, provider)
    2.23  
    2.24              module.deferred = original_deferred
    2.25  
    2.26 @@ -466,6 +467,16 @@
    2.27          for module_name in self.waiting.keys():
    2.28              self.require_providers(module_name)
    2.29  
    2.30 +    def test_dependency(self, ref, module_name, provider):
    2.31 +
    2.32 +        """
    2.33 +        Test 'ref' for establishing a dependency from 'module_name' to
    2.34 +        'provider'.
    2.35 +        """
    2.36 +
    2.37 +        if not ref.static() or self.uses_dynamic_callable(ref):
    2.38 +            self.add_provider(module_name, provider)
    2.39 +
    2.40      def add_provider(self, module_name, provider):
    2.41  
    2.42          "Add a dependency for 'module_name' of 'provider'."
    2.43 @@ -488,6 +499,19 @@
    2.44                          print >>sys.stderr, "Requiring", provider
    2.45                      self.require_providers(provider)
    2.46  
    2.47 +    def uses_callable(self, ref):
    2.48 +
    2.49 +        "Return whether 'ref' refers to a callable."
    2.50 +
    2.51 +        # Find the function or method associated with the reference.
    2.52 +
    2.53 +        if ref.has_kind("<function>"):
    2.54 +            return ref.get_origin()
    2.55 +        elif ref.has_kind("<class>"):
    2.56 +            return "%s.__init__" % ref.get_origin()
    2.57 +        else:
    2.58 +            return False
    2.59 +
    2.60      def uses_dynamic_callable(self, ref):
    2.61  
    2.62          """
    2.63 @@ -495,14 +519,12 @@
    2.64          need initialising before the callable can be used.
    2.65          """
    2.66  
    2.67 -        # Find the function or method associated with the reference.
    2.68 +        origin = self.uses_callable(ref)
    2.69 +        if not origin:
    2.70 +            return False
    2.71  
    2.72 -        if ref.has_kind("<function>"):
    2.73 -            origin = ref.get_origin()
    2.74 -        elif ref.has_kind("<class>"):
    2.75 -            origin = "%s.__init__" % ref.get_origin()
    2.76 -        else:
    2.77 -            return False
    2.78 +        if ref.has_kind("<class>"):
    2.79 +            return True
    2.80  
    2.81          # Find any defaults for the function or method.
    2.82  
    2.83 @@ -566,10 +588,35 @@
    2.84  
    2.85          "Check the ordering dependencies."
    2.86  
    2.87 +        mutual = set()
    2.88 +
    2.89 +        # Get dependency relationships for each module.
    2.90 +
    2.91          for module_name, modules in self.depends.items():
    2.92 +
    2.93 +            # Find the reverse relationship.
    2.94 +
    2.95              for provider in modules:
    2.96                  if self.depends.has_key(provider) and module_name in self.depends[provider]:
    2.97 -                    raise ProgramError, "Modules %s and %s may not depend on each other for non-static objects." % (module_name, provider)
    2.98 +
    2.99 +                    # Record the module names in order.
   2.100 +
   2.101 +                    mutual.add((module_name < provider and module_name or provider,
   2.102 +                                module_name > provider and module_name or provider))
   2.103 +
   2.104 +        if not mutual:
   2.105 +            return
   2.106 +
   2.107 +        # Format the dependencies.
   2.108 +
   2.109 +        mutual = list(mutual)
   2.110 +        mutual.sort()
   2.111 +        l = []
   2.112 +
   2.113 +        for module_name, provider in mutual:
   2.114 +            l.append("(%s <-> %s)" % (module_name, provider))
   2.115 +
   2.116 +        raise ProgramError, "Modules may not depend on each other for non-static objects:\n%s" % "\n".join(l)
   2.117  
   2.118      def find_dependency(self, ref):
   2.119