class YUILoader: def __init__(self): self._module_info = Components(MODULE_INFO) self._components = set() self._rolled_up_components = {} self._rollup_counters = {} self.set_version('min') def set_version(self, version): self._version = VERSIONS[version] def add_component(self, new_component_name): if not self._has_component(new_component_name): self._add_requirements(new_component_name) self._count_in_rollups(new_component_name) rollup_name = self._get_satisfied_rollup(new_component_name) if rollup_name: self.add_component(rollup_name) else: self._components.add(new_component_name) self._roll_up_superseded(new_component_name) def add_module(self, module_def): module_data = {} lexer = shlex(module_def, posix=True) def expect(*patterns): token = lexer.get_token() if token not in patterns: raise ValueError, '%s expected instead of %s' % \ (' or '.join(repr(s) for s in patterns), token and repr(token) or 'end of data') return token str_attrs = 'name', 'type', 'path', 'fullpath', 'varName' list_attrs = 'requires', 'optional', 'after' state = 'ATTR' expect('{') while state != 'STOP': if state == 'ATTR': token = expect(*str_attrs+list_attrs) expect(':') if token in str_attrs: module_data[token] = lexer.get_token() if module_data[token] is None: raise ValueError, \ 'string expected instead of end of data' state = 'DELIM' elif token in list_attrs: expect('[') lst = module_data[token] = [] state = 'LIST' elif state == 'LIST': lst.append(lexer.get_token()) if re.search(r'\W', lst[-1]): raise ValueError, 'invalid component name %r' % token if expect(',', ']') == ']': state = 'DELIM' elif state == 'DELIM': if expect(',', '}') == '}': expect(None) state = 'STOP' else: state = 'ATTR' if 'type' not in module_data: raise ValueError, 'type missing in %r' % module_def self._module_info.add(module_data['name'], module_data) return module_data def render(self): return '\n'.join(self._render_component(component) for component in self._sort_components()) def _has_component(self, component_name): return component_name in self._components \ or component_name in self._rolled_up_components def _get_satisfied_rollup(self, component_name): if self._version == '-min': for rollup_name in self._module_info.get_rollups(component_name): rollup_status = self._rollup_counters.get(rollup_name, set()) if len(rollup_status) >= self._module_info[rollup_name].rollup: return rollup_name def _count_in_rollups(self, component_name): for rollup_name in self._module_info.get_rollups(component_name): rolled_up = self._rollup_counters.setdefault(rollup_name, set()) rolled_up.add(component_name) for superseded in self._module_info[component_name].supersedes: self._count_in_rollups(superseded) def _roll_up_superseded(self, component_name): for superseded in self._module_info[component_name].supersedes: self._rolled_up_components[superseded] = component_name if superseded in self._components: self._components.remove(superseded) def _add_requirements(self, component_name): component = self._module_info[component_name] for requirement in component.requires: self.add_component(requirement) if component.skinnable: self.add_component(SKIN['defaultSkin']) def _render_component(self, component_name): component = self._module_info[component_name] path = component.fullpath or YUI_BASE + component.path if component.type == 'js': if self._version != '-min' and path.endswith('-min.js'): path = path[:-7] + self._version + '.js' elif component.type == 'css': if self._version == '' and path.endswith('-min.css'): path = path[:-8] + '.css' return TAGS[component.type] % path def _sort_components(self, component_names=None): if component_names is None: comps = self._components.copy() else: comps = component_names while comps: component_name = comps.pop() component = self._module_info[component_name] direct_deps = component.requires + component.after indirect_deps = [ self._rolled_up_components[r] for r in direct_deps if r in self._rolled_up_components] all_deps = set(direct_deps).union(set(indirect_deps)) deps_left = comps.intersection(all_deps) for r in self._sort_components(deps_left): yield r comps.remove(r) yield component_name
class YUILoader: def __init__(self): self._module_info = Components(MODULE_INFO) self._components = set() self._rolled_up_components = {} self._rollup_counters = {} self.set_version("min") def set_version(self, version): self._version = VERSIONS[version] def add_component(self, new_component_name): if not self._has_component(new_component_name): self._add_requirements(new_component_name) self._count_in_rollups(new_component_name) rollup_name = self._get_satisfied_rollup(new_component_name) if rollup_name: self.add_component(rollup_name) else: self._components.add(new_component_name) self._roll_up_superseded(new_component_name) def add_module(self, module_def): module_data = {} lexer = shlex(module_def, posix=True) def expect(*patterns): token = lexer.get_token() if token not in patterns: raise ValueError, "%s expected instead of %s" % ( " or ".join(repr(s) for s in patterns), token and repr(token) or "end of data", ) return token str_attrs = "name", "type", "path", "fullpath", "varName" list_attrs = "requires", "optional", "after" state = "ATTR" expect("{") while state != "STOP": if state == "ATTR": token = expect(*str_attrs + list_attrs) expect(":") if token in str_attrs: module_data[token] = lexer.get_token() if module_data[token] is None: raise ValueError, "string expected instead of end of data" state = "DELIM" elif token in list_attrs: expect("[") lst = module_data[token] = [] state = "LIST" elif state == "LIST": lst.append(lexer.get_token()) if re.search(r"\W", lst[-1]): raise ValueError, "invalid component name %r" % token if expect(",", "]") == "]": state = "DELIM" elif state == "DELIM": if expect(",", "}") == "}": expect(None) state = "STOP" else: state = "ATTR" if "type" not in module_data: raise ValueError, "type missing in %r" % module_def self._module_info.add(module_data["name"], module_data) return module_data def render(self): return "\n".join(self._render_component(component) for component in self._sort_components()) def _has_component(self, component_name): return component_name in self._components or component_name in self._rolled_up_components def _get_satisfied_rollup(self, component_name): if self._version == "-min": for rollup_name in self._module_info.get_rollups(component_name): rollup_status = self._rollup_counters.get(rollup_name, set()) if len(rollup_status) >= self._module_info[rollup_name].rollup: return rollup_name def _count_in_rollups(self, component_name): for rollup_name in self._module_info.get_rollups(component_name): rolled_up = self._rollup_counters.setdefault(rollup_name, set()) rolled_up.add(component_name) for superseded in self._module_info[component_name].supersedes: self._count_in_rollups(superseded) def _roll_up_superseded(self, component_name): for superseded in self._module_info[component_name].supersedes: self._rolled_up_components[superseded] = component_name if superseded in self._components: self._components.remove(superseded) def _add_requirements(self, component_name): component = self._module_info[component_name] for requirement in component.requires: self.add_component(requirement) if component.skinnable: self.add_component(SKIN["defaultSkin"]) def _render_component(self, component_name): component = self._module_info[component_name] path = component.fullpath or YUI_BASE + component.path if component.type == "js": if self._version != "-min" and path.endswith("-min.js"): path = path[:-7] + self._version + ".js" elif component.type == "css": if self._version == "" and path.endswith("-min.css"): path = path[:-8] + ".css" return TAGS[component.type] % path def _sort_components(self, component_names=None): if component_names is None: comps = self._components.copy() else: comps = component_names while comps: component_name = comps.pop() component = self._module_info[component_name] direct_deps = component.requires + component.after indirect_deps = [self._rolled_up_components[r] for r in direct_deps if r in self._rolled_up_components] all_deps = set(direct_deps).union(set(indirect_deps)) deps_left = comps.intersection(all_deps) for r in self._sort_components(deps_left): yield r comps.remove(r) yield component_name