Example #1
0
    def add_helper(self, helper, name=None, static=False, package=None,
                   replace=False):
        """Add a "helper" function.

        ``helper`` can be a string pointing to the helper or the helper
        itself. If it's a string, ``helper`` and ``package`` will be
        passed to :func:`load_object`.

        Helper functions can be methods that take a ``Helpers`` instance
        as their first arg or they can be static methods. The latter is
        useful for adding third party functions as helpers.

        Helper functions can be accessed via ``request.helpers``. The
        advantage of this is that helpers added as method have access to
        the application and the current request.

        """
        helper = load_object(helper, package=package)
        if name is None:
            name = helper.__name__
        if static:
            helper = staticmethod(helper)
        self.register('helper', helper, name, replace=replace)
        if abcs.AHelpers not in self:
            helpers_factory = self.get_setting('helpers_factory')
            self.register(abcs.AHelpers, load_object(helpers_factory))
Example #2
0
    def query(self, q, cost_func='bicycle', heuristic_func=None, points=None):
        waypoints = self.get_waypoints(q, points)
        graph = get_graph(self.session)

        if ':' in cost_func:
            cost_func = load_object(cost_func)
        else:
            cost_func = load_object('.cost', cost_func)

        paths_info = []
        for s, e in zip(waypoints[:-1], waypoints[1:]):
            path_info = self.find_path(
                graph, s, e, cost_func=cost_func, heuristic_func=heuristic_func)
            paths_info.append(path_info)

        routes = []
        starts = waypoints[:-1]
        ends = waypoints[1:]

        for start, end, path_info in zip(starts, ends, paths_info):
            node_ids, edge_attrs, split_ways = path_info
            directions, linestring, distance = self.make_directions(node_ids, edge_attrs, split_ways)
            route = Route(start, end, directions, linestring, distance)
            routes.append(route)

        return routes[0] if len(routes) == 1 else routes
Example #3
0
    def add_helper(self,
                   helper,
                   name=None,
                   static=False,
                   package=None,
                   replace=False):
        """Add a "helper" function.

        ``helper`` can be a string pointing to the helper or the helper
        itself. If it's a string, ``helper`` and ``package`` will be
        passed to :func:`load_object`.

        Helper functions can be methods that take a ``Helpers`` instance
        as their first arg or they can be static methods. The latter is
        useful for adding third party functions as helpers.

        Helper functions can be accessed via ``request.helpers``. The
        advantage of this is that helpers added as method have access to
        the application and the current request.

        """
        helper = load_object(helper, package=package)
        if name is None:
            name = helper.__name__
        if static:
            helper = staticmethod(helper)
        self.register('helper', helper, name, replace=replace)
        if abcs.AHelpers not in self:
            helpers_factory = self.get_setting('helpers_factory')
            self.register(abcs.AHelpers, load_object(helpers_factory))
Example #4
0
    def add_subscriber(self, event_type, func, priority=None, once=False, **args):
        """Add a subscriber for the specified event type.

        ``args`` will be passed to ``func`` as keyword args. (Note: this
        functionality is somewhat esoteric and should perhaps be
        removed.)

        You can also use the :class:`~tangled.web.events.subscriber`
        decorator to register subscribers.

        """
        event_type = load_object(event_type)
        func = load_object(func)
        subscriber = Subscriber(event_type, func, priority, once, args)
        self.register(event_type, subscriber, subscriber)
Example #5
0
    def register_representation_type(self, representation_type, replace=False):
        """Register a content type.

        This does a few things:

        - Makes the representation type accessible via its key
        - Makes the representation type accessible via its content type
        - Registers the representation's content type

        """
        representation_type = load_object(representation_type)
        key = representation_type.key
        content_type = representation_type.content_type
        quality = representation_type.quality
        self.register(Representation,
                      representation_type,
                      key,
                      replace=replace)
        self.register(Representation,
                      representation_type,
                      content_type,
                      replace=replace)
        self.register('content_type', (content_type, quality),
                      content_type,
                      replace=replace)
Example #6
0
def bycycle(service: arg(choices=('lookup', 'route')), q):
    """Run a byCycle service."""
    module_name = 'bycycle.core.service.{service}'.format(service=service)
    service_factory = load_object(module_name, 'Service')

    if service == 'route':
        q = re.split('\s+to\s+', q, re.I)
        if len(q) < 2:
            abort(1, 'Route must be specified as "A to B"')

    engine = get_engine()
    session_factory = get_session_factory(engine)
    session = session_factory()

    start_time = time.time()
    try:
        service = service_factory(session)
        response = service.query(q)
    except Exception:
        session.rollback()
        raise
    else:
        session.close()
        print(response)
    print('{:.2f} seconds'.format(time.time() - start_time))
Example #7
0
 def _handlers(self):
     """Set up the handler chain."""
     settings = self.get_settings(prefix="tangled.app.handler.")
     # System handler chain
     handlers = [settings["exc"]]
     if self.has_any("static_directory"):
         # Only enable static file handler if there's at least one
         # local static directory registered.
         dirs = self.get_all("static_directory")
         if any(isinstance(d, LocalDirectory) for d in dirs):
             handlers.append(settings["static_files"])
     handlers.append(settings["tweaker"])
     handlers.append(settings["notifier"])
     handlers.append(settings["resource_finder"])
     if self.get_setting("csrf.enabled"):
         handlers.append(settings["csrf"])
     if "auth" in settings:
         handlers.append(settings["auth"])
     # Handlers added by extensions and applications
     handlers += self.get_all(abcs.AHandler, [])
     # Main handler
     handlers.append(settings["main"])
     # Wrap handlers
     wrapped_handlers = []
     next_handler = None
     for handler in reversed(handlers):
         handler = load_object(handler)
         handler = HandlerWrapper(handler, next_handler)
         wrapped_handlers.append(handler)
         next_handler = handler
     wrapped_handlers.reverse()
     return wrapped_handlers
Example #8
0
 def _handlers(self):
     """Set up the handler chain."""
     settings = self.get_settings(prefix='tangled.app.handler.')
     # System handler chain
     handlers = [settings['exc']]
     if self.has_any('static_directory'):
         # Only enable static file handler if there's at least one
         # local static directory registered.
         dirs = self.get_all('static_directory')
         if any(isinstance(d, LocalDirectory) for d in dirs):
             handlers.append(settings['static_files'])
     handlers.append(settings['tweaker'])
     handlers.append(settings['notifier'])
     handlers.append(settings['resource_finder'])
     if self.get_setting('csrf.enabled'):
         handlers.append(settings['csrf'])
     if 'auth' in settings:
         handlers.append(settings['auth'])
     # Handlers added by extensions and applications
     handlers += self.get_all(abcs.AHandler, [])
     # Main handler
     handlers.append(settings['main'])
     # Wrap handlers
     wrapped_handlers = []
     next_handler = None
     for handler in reversed(handlers):
         handler = load_object(handler)
         handler = HandlerWrapper(handler, next_handler)
         wrapped_handlers.append(handler)
         next_handler = handler
     wrapped_handlers.reverse()
     return wrapped_handlers
Example #9
0
 def __init__(self, parser, args):
     app_factory = self.args.app_factory
     if app_factory is None:
         app_factory = self.settings.pop('factory', None)
         if app_factory is None:
             parser.error(
                 'App factory must be specified via --app-factory or '
                 'in settings')
     self.args.app_factory = load_object(app_factory, 'make_app')
Example #10
0
 def __init__(self, parser, args):
     app_factory = self.args.app_factory
     if app_factory is None:
         app_factory = self.settings.get('factory', None)
         if app_factory is None:
             parser.error(
                 '\n    An app factory must be specified via --app-factory or factory setting.'
                 '\n    Did you specify a settings file via -f?'
                 '\n    If so, does it contain an [app] section with a factory setting?')
     self.args.app_factory = load_object(app_factory, 'make_app')
Example #11
0
 def __init__(self, parser, args):
     app_factory = self.args.app_factory
     if app_factory is None:
         app_factory = self.settings.get('factory', None)
         if app_factory is None:
             parser.error(
                 '\n    An app factory must be specified via --app-factory or factory setting.'
                 '\n    Did you specify a settings file via -f?'
                 '\n    If so, does it contain an [app] section with a factory setting?'
             )
     self.args.app_factory = load_object(app_factory, 'make_app')
Example #12
0
def get_type(name: str):
    """Get the type corresponding to ``name``."""
    if name is None:
        return str
    if name == 'object':
        return load_object
    if hasattr(builtins, name):
        return getattr(builtins, name)
    if is_object_path(name):
        return load_object(name)
    raise TypeError('Unknown type: %s' % name)
Example #13
0
    def add_subscriber(self,
                       event_type,
                       func,
                       priority=None,
                       once=False,
                       **args):
        """Add a subscriber for the specified event type.

        ``args`` will be passed to ``func`` as keyword args. (Note: this
        functionality is somewhat esoteric and should perhaps be
        removed.)

        You can also use the :class:`~tangled.web.events.subscriber`
        decorator to register subscribers.

        """
        event_type = load_object(event_type)
        func = load_object(func)
        subscriber = Subscriber(event_type, func, priority, once, args)
        self.register(event_type, subscriber, subscriber)
Example #14
0
def get_type(name: str):
    """Get the type corresponding to ``name``."""
    if name is None:
        return str
    if name == 'object':
        return load_object
    if hasattr(builtins, name):
        return getattr(builtins, name)
    if is_object_path(name):
        return load_object(name)
    raise TypeError('Unknown type: %s' % name)
Example #15
0
    def include(self, obj):
        """Include some other code.

        If a callable is passed, that callable will be called with this
        app instance as its only argument.

        If a module is passed, it must contain a function named
        ``include``, which will be called as described above.

        """
        obj = load_object(obj, 'include')
        return obj(self)
Example #16
0
    def include(self, obj):
        """Include some other code.

        If a callable is passed, that callable will be called with this
        app instance as its only argument.

        If a module is passed, it must contain a function named
        ``include``, which will be called as described above.

        """
        obj = load_object(obj, 'include')
        return obj(self)
Example #17
0
 def make_app(self, app_factory, settings_file, settings):
     settings = self.make_settings(settings_file, settings)
     if app_factory is None:
         app_factory = settings.get('factory')
         if app_factory is None:
             abort(1, (
                 'An app factory must be specified via the factory setting '
                 'or using the --factory option.'
                 '\nDid you specify a settings file via --settings-file?'
                 '\nIf so, does it contain an [app] section with a factory setting?'
             ))
     app_factory = load_object(app_factory, 'make_app')
     app = app_factory(settings)
     return app
Example #18
0
 def make_app(self, app_factory, settings_file, settings):
     settings = self.make_settings(settings_file, settings)
     if app_factory is None:
         app_factory = settings.get('factory')
         if app_factory is None:
             abort(1, (
                 'An app factory must be specified via the factory setting '
                 'or using the --factory option.'
                 '\nDid you specify a settings file via --settings-file?'
                 '\nIf so, does it contain an [app] section with a factory setting?'
             ))
     app_factory = load_object(app_factory, 'make_app')
     app = app_factory(settings)
     return app
Example #19
0
    def register_representation_type(self, representation_type, replace=False):
        """Register a content type.

        This does a few things:

        - Makes the representation type accessible via its key
        - Makes the representation type accessible via its content type
        - Registers the representation's content type

        """
        representation_type = load_object(representation_type)
        key = representation_type.key
        content_type = representation_type.content_type
        quality = representation_type.quality
        self.register(Representation, representation_type, key, replace=replace)
        self.register(Representation, representation_type, content_type, replace=replace)
        self.register("content_type", (content_type, quality), content_type, replace=replace)
Example #20
0
def shell(config,
          shell_: dict(choices=('bpython', 'ipython', 'python')) = 'bpython',
          locals_:
          'Pass shell locals using name=package.module:object syntax' = {}):
    banner = ['Tangled Shell']

    if locals_:
        banner.append('Locals:')

        for k, v in locals_.items():
            v = load_object(v)
            locals_[k] = v

            v = str(v)
            v = v.replace('\n', '\n       ' + ' ' * len(k))
            banner.append('    {} = {}'.format(k, v))

    banner = '\n'.join(banner)

    if shell_ == 'bpython':
        try:
            import bpython
        except ImportError:
            printer.warning('bpython is not installed; falling back to python')
            shell_ = 'python'
        else:
            from bpython import embed
            banner = 'bpython {}\n{}'.format(bpython.__version__, banner)
            embed(locals_=locals_, banner=banner)

    if shell_ == 'ipython':
        try:
            import IPython
        except ImportError:
            printer.warning('IPython is not installed; falling back to python')
            shell_ = 'python'
        else:
            from IPython.terminal.embed import InteractiveShellEmbed
            InteractiveShellEmbed(user_ns=locals_, banner2=banner)()

    if shell_ == 'python':
        banner = 'python {}\n{}'.format(sys.version, banner)
        code.interact(banner=banner, local=locals_)
Example #21
0
def fire_actions(where,
                 tags=(),
                 args=(),
                 kwargs=None,
                 _registry=_ACTION_REGISTRY):
    """Fire actions previously registered via :func:`register_action`.

    ``where`` is typically a package or module. Only actions registered
    in that package or module will be fired.

    ``where`` can also be some other type of object, such as a class, in
    which case only the actions registered on the class and its methods
    will be fired. Currently, this is considered non-standard usage, but
    it's useful for testing.

    If no ``tags`` are specified, all registered actions under ``where``
    will be fired.

    ``*args`` and ``**kwargs`` will be passed to each action that is
    fired.

    """
    where = load_object(where)
    where_fq_name = fully_qualified_name(where)
    tags = (tags, ) if isinstance(tags, str) else tags
    kwargs = {} if kwargs is None else kwargs

    if hasattr(where, '__path__'):
        # Load all modules in package
        path = where.__path__
        prefix = where.__name__ + '.'
        for (_, name, is_pkg) in pkgutil.walk_packages(path, prefix):
            if name not in sys.modules:
                __import__(name)

    tags = _registry.keys() if not tags else tags

    for tag in tags:
        tag_actions = _registry[tag]
        for fq_name, wrapped_actions in tag_actions.items():
            if fq_name.startswith(where_fq_name):
                for action in wrapped_actions:
                    action(*args, **kwargs)
Example #22
0
def fire_actions(where, tags=(), args=(), kwargs=None,
                 _registry=_ACTION_REGISTRY):
    """Fire actions previously registered via :func:`register_action`.

    ``where`` is typically a package or module. Only actions registered
    in that package or module will be fired.

    ``where`` can also be some other type of object, such as a class, in
    which case only the actions registered on the class and its methods
    will be fired. Currently, this is considered non-standard usage, but
    it's useful for testing.

    If no ``tags`` are specified, all registered actions under ``where``
    will be fired.

    ``*args`` and ``**kwargs`` will be passed to each action that is
    fired.

    """
    where = load_object(where)
    where_fq_name = fully_qualified_name(where)
    tags = (tags,) if isinstance(tags, str) else tags
    kwargs = {} if kwargs is None else kwargs

    if hasattr(where, '__path__'):
        # Load all modules in package
        path = where.__path__
        prefix = where.__name__ + '.'
        for (_, name, is_pkg) in pkgutil.walk_packages(path, prefix):
            if name not in sys.modules:
                __import__(name)

    tags = _registry.keys() if not tags else tags

    for tag in tags:
        tag_actions = _registry[tag]
        for fq_name, wrapped_actions in tag_actions.items():
            if fq_name.startswith(where_fq_name):
                for action in wrapped_actions:
                    action(*args, **kwargs)
Example #23
0
 def test_load_object_pass_name(self):
     obj = util.load_object('tangled.util', 'load_object')
     self.assertIs(obj, util.load_object)
Example #24
0
def as_func_args(func, converters={}, item_sep=', ', _skip_first=False):
    r"""Make a converter that converts lines of args based on ``func``.

    This inspects ``func`` and configures converters based on the types
    of its args as follows:

        - If an arg has no default, no converter will be used unless
          a converter is specified for it via ``converters``
        - If an arg has a default that's a built-in type, the
          corresponding converter will be used unless a different
          converter is specified via ``converters``
        - If an arg has a default that's *not* a built-in type, that
          type will be used as the converter unless a different
          converter is specified via ``converters``

    The resulting converter would typically be used to parse lines of
    args from a settings file.

    Examples::

        >>> lines = ('1, 1, 1, 1\n1, 0, 1, 1')
        >>> c = as_func_args((lambda a, b=True, i=1, s='s': None), converters={'a': int})
        >>> args_list = c(lines)
        >>> args = args_list[0]
        >>> args['a']
        1
        >>> args['b']
        True
        >>> args['i']
        1
        >>> args['s']
        '1'
        >>> args = args_list[1]
        >>> args['b']
        False
        >>> c = as_func_args(as_func_args, converters={'func': 'object'})
        >>> args = c('tangled.converters:as_func_args')[0]
        >>> args['func'].__name__
        'as_func_args'

    """
    func = load_object(func)
    signature = inspect.signature(func)
    parameters = iter(signature.parameters.values())
    if _skip_first:
        next(parameters)

    arg_converters = OrderedDict()
    for i, p in enumerate(parameters):
        name = p.name
        if name in converters:
            converter = converters[name]
        elif name is None:
            # Positional-only arg
            name = (None, i)
            converter = 'self'
        elif p.default is p.empty:
            converter = 'self'
        elif p.default is None:
            converter = 'self'
        else:
            c = p.default.__class__
            is_builtin = c is __builtins__.get(c.__name__)
            converter = c.__name__ if is_builtin else c
        arg_converters[name] = get_converter(converter)

    line_splitter = as_seq_of_seq(item_sep=item_sep)

    def converter(lines):
        lines = line_splitter(lines)
        args_list = []
        for line in lines:
            args = OrderedDict()
            for v, name in zip(line, arg_converters):
                converter = arg_converters[name]
                args[name] = converter(v)
            args_list.append(args)
        return args_list

    return converter
Example #25
0
def as_object(v):
    v = v.strip()
    if not v:
        return None
    return load_object(v)
Example #26
0
    def __init__(self, settings, **extra_settings):
        if not isinstance(settings, abcs.AAppSettings):
            settings = make_app_settings(settings, **extra_settings)
        self.settings = settings

        package = settings.get('package')

        # Register default representations (content type => repr. type).
        # Includes can override this.
        for obj in vars(representations).values():
            is_representation_type = (
                isinstance(obj, type) and
                issubclass(obj, Representation) and
                obj is not Representation)
            if is_representation_type:
                self.register_representation_type(obj)

        self.add_config_field('*/*', 'quality', None)
        self.add_config_field('*/*', 'type', None)
        self.add_config_field('*/*', 'status', None)
        self.add_config_field('*/*', 'location', None)
        self.add_config_field('*/*', 'response_attrs', dict)

        # Handlers added from settings have precedence over handlers
        # added via includes.
        handlers = self.get_setting('handlers')
        for handler in handlers:
            self.add_handler(handler)

        # Mount static directories and resources from settings before
        # those from includes. It's assumed that only the main
        # application will specify static directories and resources this
        # way.
        for static_args in self.get_setting('static_directories'):
            self.mount_static_directory(**static_args)

        resources_package = self.get_setting('tangled.app.resources.package', package)

        for resource_args in self.get_setting('resources'):
            factory = resource_args['factory']
            if factory.startswith('.'):
                if resources_package is None:
                    raise ValueError(
                        'Package-relative resource factory "{factory}" requires the package '
                        'containing resources to be specified (set either the `package` or '
                        '`tangled.app.resources.package` setting).'
                        .format(factory=factory))
                factory = '{package}{factory}'.format(package=resources_package, factory=factory)
                resource_args['factory'] = factory
            self.mount_resource(**resource_args)

        # Before config is loaded via load_config()
        if self.get_setting('csrf.enabled'):
            self.include('.csrf')

        for include in self.get_setting('includes'):
            self.include(include)

        for where in self.get_setting('load_config'):
            self.load_config(where)

        request_factory = self.get_setting('request_factory')
        request_factory = load_object(request_factory)
        self.register(abcs.ARequest, request_factory)

        response_factory = self.get_setting('response_factory')
        response_factory = load_object(response_factory)
        self.register(abcs.AResponse, response_factory)

        # TODO: Not sure this belongs here
        if not self.testing:
            self._configure_logging()

        self.name = self.get_setting('name') or 'id={}'.format(id(self))
        process_registry.register(abcs.AApplication, self, self.name)

        for subscriber in self.get_setting('on_created'):
            self.on_created(subscriber)

        if not self.get_setting('tangled.app.defer_created', False):
            self.created()
Example #27
0
 def test_load_object_pass_name(self):
     obj = util.load_object('tangled.util', 'load_object')
     self.assertIs(obj, util.load_object)
Example #28
0
    def mount_resource(self, name, factory, path, methods=(), method=None, add_slash=False,
                       replace=False, _level=3):
        """Mount a resource at the specified path.

        Resources mounted later will take precedence over resources
        mounted earlier.

        Basic example::

            app.mount_resource('home', 'mypackage.resources:Home', '/')

        URL vars are delimited with angle brackets like so::

            # The user's ID will be extracted from the URL and passed to
            # the User resource's methods.
            app.mount_resource(
                'user', 'mypackage.resources:User', '/user/<id>')

        A unique ``name`` for the mounted resource must be specified.
        This can be *any* string. It's used when generating resource
        URLs via :meth:`.request.Request.resource_url`.

        Pass ``replace=True`` to replace a resource mounted with a given
        name with a different resource (possibly mounted at a different
        path and/or with different methods).

        ``factory`` is a class or other callable that produces objects
        that implement the resource interface (typically a subclass of
        :class:`.resource.resource.Resource`). The factory may be passed
        as an object path like ``'package.module:factory'``.

        ``path`` is an application-relative path that may or may not
        include URL vars. It *must* begin with a slash.

        If ``path`` ends with a slash or ``add_slash=True``, requests to
        ``path`` without a trailing slash will be redirected to ``path``
        with a slash appended.

        A list of HTTP ``methods`` can be passed to constrain which
        methods the resource will respond to. By default, it's assumed
        that a resource will respond to all methods. Note however that
        when subclassing :class:`.resource.resource.Resource`,
        unimplemented methods will return a ``405 Method Not Allowed``
        response, so it's often unnecessary to specify the list of
        allowed methods here; this is mainly useful if you want to
        mount different resources at the same path for different
        methods.

        **Mounting Subresources**

        Subresources can be mounted like this::

            parent = app.mount_resource('parent', factory, '/parent')
            parent.mount('child', 'child')

        or like this::

            with app.mount_resource('parent', factory, '/parent') as parent:
                parent.mount('child', 'child')

        In either case, the subresource's ``name`` will be prepended
        with its parent's name plus a slash, and its ``path`` will be
        prepended with its parent's path plus a slash. If no ``factory``
        is specified, the parent's factory will be used. ``methods``
        will be propagated as well. ``method`` and ``add_slash`` are
        *not* propagated.

        In the examples above, the child's name would be
        ``parent/child`` and its path would be ``/parent/child``.

        """
        factory = load_object(factory, level=_level)
        mounted_resource = MountedResource(self, name, factory, path, methods, method, add_slash)
        self.register(abcs.AMountedResource, mounted_resource, mounted_resource.name, replace)
        return SubResourceMounter(self, mounted_resource)
Example #29
0
    def mount_resource(self,
                       name,
                       factory,
                       path,
                       methods=(),
                       method=None,
                       add_slash=False,
                       replace=False,
                       _level=3):
        """Mount a resource at the specified path.

        Resources mounted later will take precedence over resources
        mounted earlier.

        Basic example::

            app.mount_resource('home', 'mypackage.resources:Home', '/')

        URL vars are delimited with angle brackets like so::

            # The user's ID will be extracted from the URL and passed to
            # the User resource's methods.
            app.mount_resource(
                'user', 'mypackage.resources:User', '/user/<id>')

        A unique ``name`` for the mounted resource must be specified.
        This can be *any* string. It's used when generating resource
        URLs via :meth:`.request.Request.resource_url`.

        Pass ``replace=True`` to replace a resource mounted with a given
        name with a different resource (possibly mounted at a different
        path and/or with different methods).

        ``factory`` is a class or other callable that produces objects
        that implement the resource interface (typically a subclass of
        :class:`.resource.resource.Resource`). The factory may be passed
        as an object path like ``'package.module:factory'``.

        ``path`` is an application-relative path that may or may not
        include URL vars. It *must* begin with a slash.

        If ``path`` ends with a slash or ``add_slash=True``, requests to
        ``path`` without a trailing slash will be redirected to ``path``
        with a slash appended.

        A list of HTTP ``methods`` can be passed to constrain which
        methods the resource will respond to. By default, it's assumed
        that a resource will respond to all methods. Note however that
        when subclassing :class:`.resource.resource.Resource`,
        unimplemented methods will return a ``405 Method Not Allowed``
        response, so it's often unnecessary to specify the list of
        allowed methods here; this is mainly useful if you want to
        mount different resources at the same path for different
        methods.

        **Mounting Subresources**

        Subresources can be mounted like this::

            parent = app.mount_resource('parent', factory, '/parent')
            parent.mount('child', 'child')

        or like this::

            with app.mount_resource('parent', factory, '/parent') as parent:
                parent.mount('child', 'child')

        In either case, the subresource's ``name`` will be prepended
        with its parent's name plus a slash, and its ``path`` will be
        prepended with its parent's path plus a slash. If no ``factory``
        is specified, the parent's factory will be used. ``methods``
        will be propagated as well. ``method`` and ``add_slash`` are
        *not* propagated.

        In the examples above, the child's name would be
        ``parent/child`` and its path would be ``/parent/child``.

        """
        factory = load_object(factory, level=_level)
        mounted_resource = MountedResource(self, name, factory, path, methods,
                                           method, add_slash)
        self.register(abcs.AMountedResource, mounted_resource,
                      mounted_resource.name, replace)
        return SubResourceMounter(self, mounted_resource)
Example #30
0
 def test_load_object(self):
     obj = util.load_object('tangled.util:load_object')
     self.assertIs(obj, util.load_object)
Example #31
0
def local_type(val):
    k, v = val.split('=')
    v = load_object(v)
    return k, v
Example #32
0
 def exc_log_message_factory(self):
     factory = self.get_setting('exc_log_message_factory')
     factory = load_object(factory)
     return factory
Example #33
0
 def load_config(self, where):
     """Load config registered via decorators."""
     where = load_object(where, level=3)
     fire_actions(where, tags='tangled.web', args=(self,))
Example #34
0
 def _request_finished_handler(self):
     """Calls finished callbacks in exc handling context."""
     exc_handler = load_object(self.get_setting('handler.exc'))
     handler = load_object('.handlers:request_finished_handler')
     handler = HandlerWrapper(exc_handler, HandlerWrapper(handler, None))
     return handler
Example #35
0
    def __init__(self, settings, **extra_settings):
        if not isinstance(settings, abcs.AAppSettings):
            settings = make_app_settings(settings, **extra_settings)
        self.settings = settings

        package = settings.get('package')

        # Register default representations (content type => repr. type).
        # Includes can override this.
        for obj in vars(representations).values():
            is_representation_type = (isinstance(obj, type)
                                      and issubclass(obj, Representation)
                                      and obj is not Representation)
            if is_representation_type:
                self.register_representation_type(obj)

        self.add_config_field('*/*', 'quality', None)
        self.add_config_field('*/*', 'type', None)
        self.add_config_field('*/*', 'status', None)
        self.add_config_field('*/*', 'location', None)
        self.add_config_field('*/*', 'response_attrs', dict)

        # Handlers added from settings have precedence over handlers
        # added via includes.
        handlers = self.get_setting('handlers')
        for handler in handlers:
            self.add_handler(handler)

        # Mount static directories and resources from settings before
        # those from includes. It's assumed that only the main
        # application will specify static directories and resources this
        # way.
        for static_args in self.get_setting('static_directories'):
            self.mount_static_directory(**static_args)

        resources_package = self.get_setting('tangled.app.resources.package',
                                             package)

        for resource_args in self.get_setting('resources'):
            factory = resource_args['factory']
            if factory.startswith('.'):
                if resources_package is None:
                    raise ValueError(
                        'Package-relative resource factory "{factory}" requires the package '
                        'containing resources to be specified (set either the `package` or '
                        '`tangled.app.resources.package` setting).'.format(
                            factory=factory))
                factory = '{package}{factory}'.format(
                    package=resources_package, factory=factory)
                resource_args['factory'] = factory
            self.mount_resource(**resource_args)

        # Before config is loaded via load_config()
        if self.get_setting('csrf.enabled'):
            self.include('.csrf')

        for include in self.get_setting('includes'):
            self.include(include)

        for where in self.get_setting('load_config'):
            self.load_config(where)

        request_factory = self.get_setting('request_factory')
        request_factory = load_object(request_factory)
        self.register(abcs.ARequest, request_factory)

        response_factory = self.get_setting('response_factory')
        response_factory = load_object(response_factory)
        self.register(abcs.AResponse, response_factory)

        # TODO: Not sure this belongs here
        if not self.testing:
            self._configure_logging()

        self.name = self.get_setting('name') or 'id={}'.format(id(self))
        process_registry.register(abcs.AApplication, self, self.name)

        for subscriber in self.get_setting('on_created'):
            self.on_created(subscriber)

        if not self.get_setting('tangled.app.defer_created', False):
            self.created()
Example #36
0
 def __init__(self, callable_, next_handler):
     if isinstance(callable_, str):
         callable_ = load_object(callable_)
     self.callable_ = callable_
     self.next = next_handler
Example #37
0
 def test_load_object_with_dotted_name(self):
     obj = util.load_object('sys:implementation.name')
     self.assertEqual(obj, sys.implementation.name)
Example #38
0
    def __init__(self, name, factory, path, methods=(), method_name=None,
                 add_slash=False):
        if not path.startswith('/'):
            path = '/' + path

        if path == '/':
            add_slash = False
        else:
            if path.endswith('/'):
                add_slash = True
            elif add_slash:
                path = '{}/'.format(path)

        self.name = name
        self.factory = factory
        self.path = path  # normalized path
        self.methods = set(as_tuple(methods, sep=','))
        self.method_name = method_name
        self.add_slash = add_slash

        urlvars_info = {}
        path_regex = []
        format_string = []
        i = 0

        for match in re.finditer(self.urlvar_regex, path):
            info = match.groupdict()
            identifier = info['identifier']

            if identifier in urlvars_info:
                raise ValueError('{} already present'.format(identifier))

            regex = info['regex'] or r'[\w-]+'

            converter = info['converter']
            if converter:
                if ':' in converter:
                    converter = load_object(converter)
                else:
                    converter = get_converter(converter)
            else:
                converter = str

            urlvars_info[identifier] = {'regex': regex, 'converter': converter}

            # Get the non-matching part of the string after the previous
            # match and before the current match.
            s, e = match.span()
            if i != s:
                before_match = path[i:s]
                path_regex.append(before_match)
                format_string.append(before_match)
            i = e

            path_regex.append(r'(?P<{}>{})'.format(identifier, regex))
            format_string.extend(('{', identifier, '}'))

        if i != len(path):
            remainder = path[i:]
            path_regex.append(remainder)
            format_string.append(remainder)

        path_regex = ''.join(path_regex)

        self.urlvars_info = urlvars_info
        self.segments = path_regex.strip('/').split('/')
        self.format_string = ''.join(format_string)

        if add_slash:
            path_regex = r'{}(?:/?)'.format(path_regex.rstrip('/'))

        self.path_regex = path_regex
Example #39
0
 def test_load_module(self):
     module = util.load_object('tangled.util')
     self.assertIs(module, util)
Example #40
0
 def test_load_module(self):
     module = util.load_object('tangled.util')
     self.assertIs(module, util)
Example #41
0
 def load_config(self, where):
     """Load config registered via decorators."""
     where = load_object(where, level=3)
     fire_actions(where, tags='tangled.web', args=(self, ))
Example #42
0
    def mount_resource(self, name, factory, path, methods=(), method_name=None,
                       add_slash=False, _level=3):
        """Mount a resource at the specified path.

        Basic example::

            app.mount_resource('home', 'mypackage.resources:Home', '/')

        Specifying URL vars::

            app.mount_resource(
                'user', 'mypackage.resources:User', '/user/<id>')

        A unique ``name`` for the mounted resource must be specified.
        This can be *any* string. It's used when generating resource
        URLs via :meth:`.request.Request.resource_url`.

        A ``factory`` must also be specified. This can be any class or
        function that produces objects that implement the resource
        interface (typically a subclass of
        :class:`.resource.resource.Resource`). The factory may be passed
        as a string with the following format:
        ``package.module:factory``.

        The ``path`` is an application relative path that may or may not
        include URL vars.

        A list of HTTP ``methods`` can be passed to constrain which
        methods the resource will respond to. By default, it's assumed
        that a resource will respond to all methods. Note however that
        when subclassing :class:`.resource.resource.Resource`,
        unimplemented methods will return a ``405 Method Not Allowed``
        response, so it's often unnecessary to specify the list of
        allowed methods here; this is mainly useful if you want to
        mount different resources at the same path for different
        methods.

        If ``path`` ends with a slash or ``add_slash`` is True, requests
        to ``path`` without a trailing slash will be redirected to the
        ``path`` with a slash appended.

        About URL vars:

        The format of a URL var is ``<(converter)identifier:regex>``.
        Angle brackets delimit URL vars. Only the ``identifier`` is
        required; it can be any valid Python identifier.

        If a ``converter`` is specified, it can be a built-in name,
        the name of a converter in :mod:`tangled.util.converters`, or
        a ``package.module:callable`` path that points to a callable
        that accepts a single argument. URL vars found in a request path
        will be converted automatically.

        The ``regex`` can be *almost* any regular expression. The
        exception is that ``<`` and ``>`` can't be used. In practice,
        this means that named groups (``(?P<name>regex)``) can't be used
        (which would be pointless anyway), nor can "look behinds".

        **Mounting Subresources**

        Subresources can be mounted like this::

            parent = app.mount_resource('parent', factory, '/parent')
            parent.mount('child', 'child')

        or like this::

            with app.mount_resource('parent', factory, '/parent') as parent:
                parent.mount('child', 'child')

        In either case, the subresource's ``name`` will be prepended
        with its parent's name plus a slash, and its ``path`` will be
        prepended with its parent's path plus a slash. If no ``factory``
        is specified, the parent's factory will be used. ``methods``
        will be propagated as well. ``method_name`` and ``add_slash``
        are *not* propagated.

        In the examples above, the child's name would be
        ``parent/child`` and its path would be ``/parent/child``.

        """
        mounted_resource = MountedResource(
            name,
            load_object(factory, level=_level),
            path,
            methods=methods,
            method_name=method_name,
            add_slash=add_slash)
        self.register(
            abcs.AMountedResource, mounted_resource, mounted_resource.name)
        tree = self.get_required(abcs.AMountedResourceTree)
        tree.add(mounted_resource)
        return SubResourceMounter(self, mounted_resource)
Example #43
0
 def test_load_object_with_dotted_name(self):
     obj = util.load_object('sys:implementation.name')
     self.assertEqual(obj, sys.implementation.name)
Example #44
0
 def __init__(self, callable_, next_handler):
     if isinstance(callable_, str):
         callable_ = load_object(callable_)
     self.callable_ = callable_
     self.next = next_handler
Example #45
0
 def test_load_object(self):
     obj = util.load_object('tangled.util:load_object')
     self.assertIs(obj, util.load_object)
Example #46
0
 def exc_log_message_factory(self):
     factory = self.get_setting('exc_log_message_factory')
     factory = load_object(factory)
     return factory