aboutsummaryrefslogtreecommitdiff
path: root/catapult/common/py_vulcanize/py_vulcanize/module.py
diff options
context:
space:
mode:
Diffstat (limited to 'catapult/common/py_vulcanize/py_vulcanize/module.py')
-rw-r--r--catapult/common/py_vulcanize/py_vulcanize/module.py262
1 files changed, 262 insertions, 0 deletions
diff --git a/catapult/common/py_vulcanize/py_vulcanize/module.py b/catapult/common/py_vulcanize/py_vulcanize/module.py
new file mode 100644
index 00000000..bd6a68fa
--- /dev/null
+++ b/catapult/common/py_vulcanize/py_vulcanize/module.py
@@ -0,0 +1,262 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""This module contains the Module class and other classes for resources.
+
+The Module class represents a module in the trace viewer system. A module has
+a name, and may require a variety of other resources, such as stylesheets,
+template objects, raw JavaScript, or other modules.
+
+Other resources include HTML templates, raw JavaScript files, and stylesheets.
+"""
+
+import os
+import inspect
+import codecs
+
+from py_vulcanize import js_utils
+
+
+class DepsException(Exception):
+ """Exceptions related to module dependency resolution."""
+
+ def __init__(self, fmt, *args):
+ from py_vulcanize import style_sheet as style_sheet_module
+ context = []
+ frame = inspect.currentframe()
+ while frame:
+ frame_locals = frame.f_locals
+
+ module_name = None
+ if 'self' in frame_locals:
+ s = frame_locals['self']
+ if isinstance(s, Module):
+ module_name = s.name
+ if isinstance(s, style_sheet_module.StyleSheet):
+ module_name = s.name + '.css'
+ if not module_name:
+ if 'module' in frame_locals:
+ module = frame_locals['module']
+ if isinstance(s, Module):
+ module_name = module.name
+ elif 'm' in frame_locals:
+ module = frame_locals['m']
+ if isinstance(s, Module):
+ module_name = module.name
+
+ if module_name:
+ if len(context):
+ if context[-1] != module_name:
+ context.append(module_name)
+ else:
+ context.append(module_name)
+
+ frame = frame.f_back
+
+ context.reverse()
+ self.context = context
+ context_str = '\n'.join(' %s' % x for x in context)
+ Exception.__init__(
+ self, 'While loading:\n%s\nGot: %s' % (context_str, (fmt % args)))
+
+
+class ModuleDependencyMetadata(object):
+
+ def __init__(self):
+ self.dependent_module_names = []
+ self.dependent_raw_script_relative_paths = []
+ self.style_sheet_names = []
+
+ def AppendMetdata(self, other):
+ self.dependent_module_names += other.dependent_module_names
+ self.dependent_raw_script_relative_paths += \
+ other.dependent_raw_script_relative_paths
+ self.style_sheet_names += other.style_sheet_names
+
+
+_next_module_id = 1
+
+
+class Module(object):
+ """Represents a JavaScript module.
+
+ Interesting properties include:
+ name: Module name, may include a namespace, e.g. 'py_vulcanize.foo'.
+ filename: The filename of the actual module.
+ contents: The text contents of the module.
+ dependent_modules: Other modules that this module depends on.
+
+ In addition to these properties, a Module also contains lists of other
+ resources that it depends on.
+ """
+
+ def __init__(self, loader, name, resource, load_resource=True):
+ assert isinstance(name, basestring), 'Got %s instead' % repr(name)
+
+ global _next_module_id
+ self._id = _next_module_id
+ _next_module_id += 1
+
+ self.loader = loader
+ self.name = name
+ self.resource = resource
+
+ if load_resource:
+ f = codecs.open(self.filename, mode='r', encoding='utf-8')
+ self.contents = f.read()
+ f.close()
+ else:
+ self.contents = None
+
+ # Dependency metadata, set up during Parse().
+ self.dependency_metadata = None
+
+ # Actual dependencies, set up during load().
+ self.dependent_modules = []
+ self.dependent_raw_scripts = []
+ self.scripts = []
+ self.style_sheets = []
+
+ # Caches.
+ self._all_dependent_modules_recursive = None
+
+ def __repr__(self):
+ return '%s(%s)' % (self.__class__.__name__, self.name)
+
+ @property
+ def id(self):
+ return self._id
+
+ @property
+ def filename(self):
+ return self.resource.absolute_path
+
+ def IsThirdPartyComponent(self):
+ """Checks whether this module is a third-party Polymer component."""
+ if os.path.join('third_party', 'components') in self.filename:
+ return True
+ if os.path.join('third_party', 'polymer', 'components') in self.filename:
+ return True
+ return False
+
+ def Parse(self, excluded_scripts):
+ """Parses self.contents and fills in the module's dependency metadata."""
+ raise NotImplementedError()
+
+ def GetTVCMDepsModuleType(self):
+ """Returns the py_vulcanize.setModuleInfo type for this module"""
+ raise NotImplementedError()
+
+ def AppendJSContentsToFile(self,
+ f,
+ use_include_tags_for_scripts,
+ dir_for_include_tag_root):
+ """Appends the js for this module to the provided file."""
+ for script in self.scripts:
+ script.AppendJSContentsToFile(f, use_include_tags_for_scripts,
+ dir_for_include_tag_root)
+
+ def AppendHTMLContentsToFile(self, f, ctl, minify=False):
+ """Appends the HTML for this module [without links] to the provided file."""
+ pass
+
+ def Load(self, excluded_scripts=None):
+ """Loads the sub-resources that this module depends on from its dependency
+ metadata.
+
+ Raises:
+ DepsException: There was a problem finding one of the dependencies.
+ Exception: There was a problem parsing a module that this one depends on.
+ """
+ assert self.name, 'Module name must be set before dep resolution.'
+ assert self.filename, 'Module filename must be set before dep resolution.'
+ assert self.name in self.loader.loaded_modules, (
+ 'Module must be registered in resource loader before loading.')
+
+ metadata = self.dependency_metadata
+ for name in metadata.dependent_module_names:
+ module = self.loader.LoadModule(module_name=name,
+ excluded_scripts=excluded_scripts)
+ self.dependent_modules.append(module)
+
+ for name in metadata.style_sheet_names:
+ style_sheet = self.loader.LoadStyleSheet(name)
+ self.style_sheets.append(style_sheet)
+
+ @property
+ def all_dependent_modules_recursive(self):
+ if self._all_dependent_modules_recursive:
+ return self._all_dependent_modules_recursive
+
+ self._all_dependent_modules_recursive = set(self.dependent_modules)
+ for dependent_module in self.dependent_modules:
+ self._all_dependent_modules_recursive.update(
+ dependent_module.all_dependent_modules_recursive)
+ return self._all_dependent_modules_recursive
+
+ def ComputeLoadSequenceRecursive(self, load_sequence, already_loaded_set,
+ depth=0):
+ """Recursively builds up a load sequence list.
+
+ Args:
+ load_sequence: A list which will be incrementally built up.
+ already_loaded_set: A set of modules that has already been added to the
+ load sequence list.
+ depth: The depth of recursion. If it too deep, that indicates a loop.
+ """
+ if depth > 32:
+ raise Exception('Include loop detected on %s', self.name)
+ for dependent_module in self.dependent_modules:
+ if dependent_module.name in already_loaded_set:
+ continue
+ dependent_module.ComputeLoadSequenceRecursive(
+ load_sequence, already_loaded_set, depth + 1)
+ if self.name not in already_loaded_set:
+ already_loaded_set.add(self.name)
+ load_sequence.append(self)
+
+ def GetAllDependentFilenamesRecursive(self, include_raw_scripts=True):
+ dependent_filenames = []
+
+ visited_modules = set()
+
+ def Get(module):
+ module.AppendDirectlyDependentFilenamesTo(
+ dependent_filenames, include_raw_scripts)
+ visited_modules.add(module)
+ for m in module.dependent_modules:
+ if m in visited_modules:
+ continue
+ Get(m)
+
+ Get(self)
+ return dependent_filenames
+
+ def AppendDirectlyDependentFilenamesTo(
+ self, dependent_filenames, include_raw_scripts=True):
+ dependent_filenames.append(self.resource.absolute_path)
+ if include_raw_scripts:
+ for raw_script in self.dependent_raw_scripts:
+ dependent_filenames.append(raw_script.resource.absolute_path)
+ for style_sheet in self.style_sheets:
+ style_sheet.AppendDirectlyDependentFilenamesTo(dependent_filenames)
+
+
+class RawScript(object):
+ """Represents a raw script resource referenced by a module via the
+ py_vulcanize.requireRawScript(xxx) directive."""
+
+ def __init__(self, resource):
+ self.resource = resource
+
+ @property
+ def filename(self):
+ return self.resource.absolute_path
+
+ @property
+ def contents(self):
+ return self.resource.contents
+
+ def __repr__(self):
+ return 'RawScript(%s)' % self.filename