class Traject(Node): def __init__(self): super(Traject, self).__init__() # XXX caching is not enabled # also could this really be registering things in the main # application registry instead? if it did and we solve caching # for that this would get it automatically. but this would # require each traject base to have its own lookup self._inverse = Registry() def add_pattern(self, path, value): node = self known_variables = set() for segment in reversed(parse_path(path)): step = Step(segment) node = node.add(step) variables = set(step.names) if known_variables.intersection(variables): raise TrajectError("Duplicate variables") known_variables.update(variables) node.value = value def inverse(self, model_class, path, get_variables): # XXX should we do checking for duplicate variables here too? path = Path(path) self._inverse.register('inverse', [model_class], (path.interpolation_str(), get_variables)) def __call__(self, stack): stack = stack[:] node = self variables = {} while stack: segment = stack.pop() if segment.startswith(VIEW_PREFIX): stack.append(segment) return node.value, stack, variables new_node, new_variables = node.get(segment) if new_node is None: stack.append(segment) return node.value, stack, variables node = new_node variables.update(new_variables) return node.value, stack, variables def path(self, model): path, get_variables = self._inverse.component('inverse', [model]) variables = get_variables(model) assert isinstance(variables, dict) return path % variables
class Traject(object): def __init__(self): super(Traject, self).__init__() # XXX caching is not enabled # also could this really be registering things in the main # application registry instead? if it did and we solve caching # for that this would get it automatically. self._root = Node() self._inverse = Registry() def add_pattern(self, path, value, converters=None): node = self._root known_variables = set() for segment in reversed(parse_path(path)): step = Step(segment, converters) node = node.add(step) variables = set(step.names) if known_variables.intersection(variables): raise TrajectError("Duplicate variables") known_variables.update(variables) node.value = value def inverse(self, model_class, path, get_variables, converters, parameter_names): # XXX should we do checking for duplicate variables here too? path = Path(path) self._inverse.register('inverse', [model_class], (path.interpolation_str(), get_variables, converters, parameter_names)) def consume(self, stack): stack = stack[:] node = self._root variables = {} while stack: segment = stack.pop() if segment.startswith(VIEW_PREFIX): stack.append(segment) return node.value, stack, variables new_node, new_variables = node.get(segment) if new_node is None: stack.append(segment) return node.value, stack, variables node = new_node variables.update(new_variables) return node.value, stack, variables def path(self, model): (path, get_variables, converters, parameter_names) = self._inverse.component( 'inverse', [model]) all_variables = get_variables(model) assert isinstance(all_variables, dict) variables = { name: converters.get(name, IDENTITY_CONVERTER).encode(value) for name, value in all_variables.items() if name not in parameter_names} parameters = { name: converters.get(name, IDENTITY_CONVERTER).encode(value) for name, value in all_variables.items() if (name in parameter_names and value is not None) } return path % variables, parameters
class Traject(object): def __init__(self): self._step_matchers = set() self._conflicting_steps = set() self._variable_matchers = {} self._model_factories = {} self._inverse = Registry() # XXX caching? def register(self, path, model_factory, base_argument=False, conflicting=False): pattern = parse(path) seen_names = set() for p in subpatterns(pattern): variable_matcher = VariableMatcher(p[-1], p) if variable_matcher.has_variables(): for name in variable_matcher.names: if name in seen_names: raise TrajectError( "path '%s' has a duplicate variable: %s" % (path, name) ) seen_names.add(name) variable_pattern = p[:-1] + (VARIABLE,) variable_matchers = self._variable_matchers.setdefault( variable_pattern, set()) for m in variable_matchers: if variable_matcher.conflicts(m): raise TrajectError( "path '%s' conflicts with path '%s'" % (path, create(m.pattern))) variable_matchers.add(variable_matcher) else: if conflicting and p in self._step_matchers: raise TrajectError( "path '%s' conflicts with another" % path) if p in self._conflicting_steps: raise TrajectError( "path '%s' conflicts with another" % path) self._step_matchers.add(p) if conflicting: self._conflicting_steps.add(p) v = self._model_factories.get(pattern) if v is not None: existing_model_factory, base_argument = v raise TrajectError( "path '%s' is already used to register model %r" % (path, existing_model_factory)) self._model_factories[pattern] = model_factory, base_argument def register_inverse(self, model_class, path, get_variables): path = interpolation_path(path) self._inverse.register('inverse', [model_class], (path, get_variables)) def match(self, pattern, step): step_pattern = self.match_step(pattern, step) if step_pattern is not None: return step_pattern, {} return self.match_variables(pattern, step) def match_step(self, pattern, step): pattern = pattern + (step,) if pattern in self._step_matchers: return pattern else: return None def match_variables(self, pattern, step): variable_pattern = pattern + (VARIABLE,) for variable_matcher in self._variable_matchers.get(variable_pattern, []): matched = variable_matcher(step) if matched: break else: return None, {} return pattern + (variable_matcher.step,), matched def get_model(self, base, pattern, variables): v = self._model_factories.get(pattern) if v is None: return None model_factory, base_argument = v if base_argument: variables['base'] = base return model_factory(**variables) def get_path(self, model): # XXX what if path cannot be found? path, get_variables = self._inverse.component('inverse', [model]) variables = get_variables(model) assert isinstance(variables, dict) return path % variables
class Traject(object): def __init__(self): self._step_matchers = set() self._conflicting_steps = set() self._variable_matchers = {} self._model_factories = {} self._inverse = Registry() # XXX caching? def register(self, path, model_factory, base_argument=False, conflicting=False): pattern = parse(path) seen_names = set() for p in subpatterns(pattern): variable_matcher = VariableMatcher(p[-1], p) if variable_matcher.has_variables(): for name in variable_matcher.names: if name in seen_names: raise TrajectError( "path '%s' has a duplicate variable: %s" % (path, name)) seen_names.add(name) variable_pattern = p[:-1] + (VARIABLE, ) variable_matchers = self._variable_matchers.setdefault( variable_pattern, set()) for m in variable_matchers: if variable_matcher.conflicts(m): raise TrajectError( "path '%s' conflicts with path '%s'" % (path, create(m.pattern))) variable_matchers.add(variable_matcher) else: if conflicting and p in self._step_matchers: raise TrajectError("path '%s' conflicts with another" % path) if p in self._conflicting_steps: raise TrajectError("path '%s' conflicts with another" % path) self._step_matchers.add(p) if conflicting: self._conflicting_steps.add(p) v = self._model_factories.get(pattern) if v is not None: existing_model_factory, base_argument = v raise TrajectError( "path '%s' is already used to register model %r" % (path, existing_model_factory)) self._model_factories[pattern] = model_factory, base_argument def register_inverse(self, model_class, path, get_variables): path = interpolation_path(path) self._inverse.register('inverse', [model_class], (path, get_variables)) def match(self, pattern, step): step_pattern = self.match_step(pattern, step) if step_pattern is not None: return step_pattern, {} return self.match_variables(pattern, step) def match_step(self, pattern, step): pattern = pattern + (step, ) if pattern in self._step_matchers: return pattern else: return None def match_variables(self, pattern, step): variable_pattern = pattern + (VARIABLE, ) for variable_matcher in self._variable_matchers.get( variable_pattern, []): matched = variable_matcher(step) if matched: break else: return None, {} return pattern + (variable_matcher.step, ), matched def get_model(self, base, pattern, variables): v = self._model_factories.get(pattern) if v is None: return None model_factory, base_argument = v if base_argument: variables['base'] = base return model_factory(**variables) def get_path(self, model): # XXX what if path cannot be found? path, get_variables = self._inverse.component('inverse', [model]) variables = get_variables(model) assert isinstance(variables, dict) return path % variables