Пример #1
0
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
Пример #2
0
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