Beispiel #1
0
def test_get_locator():
    class _Prop(object):
        @property
        def locator(self):
            return {'foo': 'bar'}

    class _Locator(object):
        @locator_property
        def locator(self):
            return {'foo': 'bar'}

    assert get_locator(_Prop()) == {'foo': 'bar'}
    assert get_locator(_Locator()) == {'foo': 'bar'}
    assert get_locator({'foo': 'bar'}) == {'foo': 'bar'}
Beispiel #2
0
def test_get_locator():
    class _Prop(object):
        @property
        def locator(self):
            return {'foo': 'bar'}

    class _Locator(object):
        @locator_property
        def locator(self):
            return {'foo': 'bar'}

    assert get_locator(_Prop()) == {'foo': 'bar'}
    assert get_locator(_Locator()) == {'foo': 'bar'}
    assert get_locator({'foo': 'bar'}) == {'foo': 'bar'}
Beispiel #3
0
def test_locator_property():
    class _Test(object):
        @locator_property
        def locator(self):
            return {'foo': 'bar'}

        @locator.fancy
        def locator(self):
            return dict(self.locator, awesome='magic')

    assert isinstance(_Test.locator, locator_property)
    t = _Test()
    assert get_locator(t) == {'foo': 'bar'}
    assert get_locator(t.locator) == {'foo': 'bar'}
    assert get_locator(t.locator.fancy) == {'foo': 'bar', 'awesome': 'magic'}
    with pytest.raises(AttributeError):
        t.locator.invalid
Beispiel #4
0
def test_locator_property():
    class _Test(object):
        @locator_property
        def locator(self):
            return {'foo': 'bar'}

        @locator.fancy
        def locator(self):
            return dict(self.locator, awesome='magic')

    assert isinstance(_Test.locator, locator_property)
    t = _Test()
    assert get_locator(t) == {'foo': 'bar'}
    assert get_locator(t.locator) == {'foo': 'bar'}
    assert get_locator(t.locator.fancy) == {'foo': 'bar', 'awesome': 'magic'}
    with pytest.raises(AttributeError):
        t.locator.invalid
Beispiel #5
0
def url_for(endpoint, *targets, **values):
    """Wrapper for Flask's url_for() function.

    Instead of an endpoint you can also pass an URLHandler - in this case **only** its _endpoint will be used.
    However, there is usually no need to do so. This is just so you can use it in places where sometimes a UH
    might be passed instead.

    The `target` argument allows you to pass some object having a `locator` property or `getLocator` method
    returning a dict. This should be used e.g. when generating an URL for an event since ``getLocator()``
    provides the ``{'confId': 123}`` dict instead of you having to pass ``confId=event.getId()`` as a kwarg.

    For details on Flask's url_for, please see its documentation.
    Anyway, the important arguments you can put in `values` besides actual arguments are:
    _external: if set to `True`, an absolute URL is generated
    _secure: if True/False, set _scheme to https/http if possible (only with _external)
    _scheme: a string specifying the desired URL scheme (only with _external) - use _secure if possible!
    _anchor: if provided this is added as #anchor to the URL.
    """

    if hasattr(endpoint, '_endpoint'):
        endpoint = endpoint._endpoint

    secure = values.pop('_secure', None)
    if secure is not None:
        from indico.core.config import Config
        if secure and Config.getInstance().getBaseSecureURL():
            values['_scheme'] = 'https'
        elif not secure:
            values['_scheme'] = 'http'

    if targets:
        locator = {}
        for target in targets:
            if target:  # don't fail on None or mako's Undefined
                locator.update(get_locator(target))
        intersection = set(locator.iterkeys()) & set(values.iterkeys())
        if intersection:
            raise ValueError('url_for kwargs collide with locator: %s' %
                             ', '.join(intersection))
        values.update(locator)

    static_site_mode = bool(ContextManager.get('offlineMode'))
    values.setdefault('_external', static_site_mode)

    for key, value in values.iteritems():
        # Avoid =True and =False in the URL
        if isinstance(value, bool):
            values[key] = int(value)

    url = _url_for(endpoint, **values)
    if static_site_mode and not values['_external']:
        # for static sites we assume all relative urls need to be
        # mangled to a filename
        # we should really fine a better way to handle anything
        # related to offline site urls...
        from indico.modules.events.static.util import url_to_static_filename
        url = url_to_static_filename(url)
    return url
Beispiel #6
0
def url_for(endpoint, *targets, **values):
    """Wrapper for Flask's url_for() function.

    Instead of an endpoint you can also pass an URLHandler - in this case **only** its _endpoint will be used.
    However, there is usually no need to do so. This is just so you can use it in places where sometimes a UH
    might be passed instead.

    The `target` argument allows you to pass some object having a `locator` property or `getLocator` method
    returning a dict. This should be used e.g. when generating an URL for an event since ``getLocator()``
    provides the ``{'confId': 123}`` dict instead of you having to pass ``confId=event.getId()`` as a kwarg.

    For details on Flask's url_for, please see its documentation.
    Anyway, the important arguments you can put in `values` besides actual arguments are:
    _external: if set to `True`, an absolute URL is generated
    _secure: if True/False, set _scheme to https/http if possible (only with _external)
    _scheme: a string specifying the desired URL scheme (only with _external) - use _secure if possible!
    _anchor: if provided this is added as #anchor to the URL.
    """

    if hasattr(endpoint, '_endpoint'):
        endpoint = endpoint._endpoint

    secure = values.pop('_secure', None)
    if secure is not None:
        from indico.core.config import Config
        if secure and Config.getInstance().getBaseSecureURL():
            values['_scheme'] = 'https'
        elif not secure:
            values['_scheme'] = 'http'

    if targets:
        locator = {}
        for target in targets:
            if target:  # don't fail on None or mako's Undefined
                locator.update(get_locator(target))
        intersection = set(locator.iterkeys()) & set(values.iterkeys())
        if intersection:
            raise ValueError('url_for kwargs collide with locator: %s' % ', '.join(intersection))
        values.update(locator)

    static_site_mode = bool(ContextManager.get('offlineMode'))
    values.setdefault('_external', static_site_mode)

    for key, value in values.iteritems():
        # Avoid =True and =False in the URL
        if isinstance(value, bool):
            values[key] = int(value)

    url = _url_for(endpoint, **values)
    if static_site_mode and not values['_external']:
        # for static sites we assume all relative urls need to be
        # mangled to a filename
        # we should really fine a better way to handle anything
        # related to offline site urls...
        from indico.modules.events.static.util import url_to_static_filename
        url = url_to_static_filename(url)
    return url
Beispiel #7
0
    def normalize_url(self):
        """Performs URL normalization.

        This uses the :attr:`normalize_url_spec` to check if the URL
        params are what they should be and redirects or fails depending
        on the HTTP method used if it's not the case.

        :return: ``None`` or a redirect response
        """
        if not self.normalize_url_spec or not any(self.normalize_url_spec.itervalues()):
            return
        spec = {
            'args': self.normalize_url_spec.get('args', {}),
            'locators': self.normalize_url_spec.get('locators', set()),
            'preserved_args': self.normalize_url_spec.get('preserved_args', set()),
        }
        # Initialize the new view args with preserved arguments (since those would be lost otherwise)
        new_view_args = {k: v for k, v in request.view_args.iteritems() if k in spec['preserved_args']}
        # Retrieve the expected values for all simple arguments (if they are currently present)
        for key, getter in spec['args'].iteritems():
            if key in request.view_args:
                new_view_args[key] = getter(self)
        # Retrieve the expected values from locators
        for getter in spec['locators']:
            value = getter(self)
            if value is None:
                raise NotFound('The URL contains invalid data. Please go to the previous page and refresh it.')
            new_view_args.update(get_locator(value))
        # Get all default values provided by the url map for the endpoint
        defaults = set(itertools.chain.from_iterable(r.defaults
                                                     for r in current_app.url_map.iter_rules(request.endpoint)
                                                     if r.defaults))

        def _convert(v):
            # some legacy code has numeric ids in the locator data, but still takes
            # string ids in the url rule (usually for confId)
            return unicode(v) if isinstance(v, (int, long)) else v

        provided = {k: _convert(v) for k, v in request.view_args.iteritems() if k not in defaults}
        new_view_args = {k: _convert(v) for k, v in new_view_args.iteritems()}
        if new_view_args != provided:
            if request.method in {'GET', 'HEAD'}:
                try:
                    return redirect(url_for(request.endpoint, **dict(request.args.to_dict(), **new_view_args)))
                except BuildError as e:
                    if current_app.debug:
                        raise
                    Logger.get('requestHandler').warn('BuildError during normalization: %s', e)
                    raise NotFound
            else:
                raise NotFound('The URL contains invalid data. Please go to the previous page and refresh it.')
Beispiel #8
0
def url_for(endpoint, *targets, **values):
    """Wrapper for Flask's url_for() function.

    The `target` argument allows you to pass some object having a `locator` property returning a dict.

    For details on Flask's url_for, please see its documentation.
    The important special arguments you can put in `values` are:

    _external: if set to `True`, an absolute URL is generated
    _scheme: a string specifying the desired URL scheme (only with _external) - use _secure if possible!
    _anchor: if provided this is added as #anchor to the URL.
    """

    if targets:
        locator = {}
        for target in targets:
            if target:  # don't fail on None or mako's Undefined
                locator.update(get_locator(target))
        intersection = set(locator.keys()) & set(values.keys())
        if intersection:
            raise ValueError('url_for kwargs collide with locator: %s' %
                             ', '.join(intersection))
        values.update(locator)

    for key, value in values.items():
        # Avoid =True and =False in the URL
        if isinstance(value, bool):
            values[key] = int(value)

    values.setdefault('_external', False)
    values = dict(sorted(values.items()))
    url = _url_for(endpoint, **values)
    if g.get('static_site'
             ) and 'custom_manifests' in g and not values.get('_external'):
        # for static sites we assume all relative urls need to be
        # mangled to a filename
        # we should really fine a better way to handle anything
        # related to offline site urls...
        from indico.modules.events.static.util import url_to_static_filename
        url = url_to_static_filename(endpoint, url)
        # mark asset as used so that generator can include it
        g.used_url_for_assets.add(url)
    return url
Beispiel #9
0
def url_for(endpoint, *targets, **values):
    """Wrapper for Flask's url_for() function.

    The `target` argument allows you to pass some object having a `locator` property returning a dict.

    For details on Flask's url_for, please see its documentation.
    The important special arguments you can put in `values` are:

    _external: if set to `True`, an absolute URL is generated
    _scheme: a string specifying the desired URL scheme (only with _external) - use _secure if possible!
    _anchor: if provided this is added as #anchor to the URL.
    """

    if targets:
        locator = {}
        for target in targets:
            if target:  # don't fail on None or mako's Undefined
                locator.update(get_locator(target))
        intersection = set(locator.iterkeys()) & set(values.iterkeys())
        if intersection:
            raise ValueError('url_for kwargs collide with locator: %s' % ', '.join(intersection))
        values.update(locator)

    for key, value in values.iteritems():
        # Avoid =True and =False in the URL
        if isinstance(value, bool):
            values[key] = int(value)

    url = _url_for(endpoint, **values)
    if g.get('static_site') and 'custom_manifests' in g and not values.get('_external'):
        # for static sites we assume all relative urls need to be
        # mangled to a filename
        # we should really fine a better way to handle anything
        # related to offline site urls...
        from indico.modules.events.static.util import url_to_static_filename
        url = url_to_static_filename(endpoint, url)
        # mark asset as used so that generator can include it
        g.used_url_for_assets.add(url)
    return url
Beispiel #10
0
    def normalize_url(self):
        """Performs URL normalization.

        This uses the :attr:`normalize_url_spec` to check if the URL
        params are what they should be and redirects or fails depending
        on the HTTP method used if it's not the case.

        :return: ``None`` or a redirect response
        """
        if current_app.debug and self.normalize_url_spec is RH.normalize_url_spec:
            # in case of ``class SomeRH(RH, MixinWithNormalization)``
            # the default value from `RH` overwrites the normalization
            # rule from ``MixinWithNormalization``.  this is never what
            # the developer wants so we fail if it happens.  the proper
            # solution is ``class SomeRH(MixinWithNormalization, RH)``
            cls = next((x for x in inspect.getmro(self.__class__)
                        if (x is not RH and x is not self.__class__
                            and hasattr(x, 'normalize_url_spec')
                            and getattr(x, 'normalize_url_spec',
                                        None) is not RH.normalize_url_spec)),
                       None)
            if cls is not None:
                raise Exception(
                    'Normalization rule of {} in {} is overwritten by base RH. Put mixins with class-level '
                    'attributes on the left of the base class'.format(
                        cls, self.__class__))
        if not self.normalize_url_spec or not any(
                self.normalize_url_spec.itervalues()):
            return
        spec = {
            'args': self.normalize_url_spec.get('args', {}),
            'locators': self.normalize_url_spec.get('locators', set()),
            'preserved_args':
            self.normalize_url_spec.get('preserved_args', set()),
            'endpoint': self.normalize_url_spec.get('endpoint', None)
        }
        # Initialize the new view args with preserved arguments (since those would be lost otherwise)
        new_view_args = {
            k: v
            for k, v in request.view_args.iteritems()
            if k in spec['preserved_args']
        }
        # Retrieve the expected values for all simple arguments (if they are currently present)
        for key, getter in spec['args'].iteritems():
            if key in request.view_args:
                new_view_args[key] = getter(self)
        # Retrieve the expected values from locators
        prev_locator_args = {}
        for getter in spec['locators']:
            value = getter(self)
            if value is None:
                raise NotFound(
                    'The URL contains invalid data. Please go to the previous page and refresh it.'
                )
            locator_args = get_locator(value)
            reused_keys = set(locator_args) & prev_locator_args.viewkeys()
            if any(locator_args[k] != prev_locator_args[k]
                   for k in reused_keys):
                raise NotFound(
                    'The URL contains invalid data. Please go to the previous page and refresh it.'
                )
            new_view_args.update(locator_args)
            prev_locator_args.update(locator_args)
        # Get all default values provided by the url map for the endpoint
        defaults = set(
            itertools.chain.from_iterable(
                r.defaults
                for r in current_app.url_map.iter_rules(request.endpoint)
                if r.defaults))

        def _convert(v):
            # some legacy code has numeric ids in the locator data, but still takes
            # string ids in the url rule (usually for confId)
            return unicode(v) if isinstance(v, (int, long)) else v

        provided = {
            k: _convert(v)
            for k, v in request.view_args.iteritems() if k not in defaults
        }
        new_view_args = {k: _convert(v) for k, v in new_view_args.iteritems()}
        if new_view_args != provided:
            if request.method in {'GET', 'HEAD'}:
                endpoint = spec['endpoint'] or request.endpoint
                try:
                    return redirect(
                        url_for(
                            endpoint,
                            **dict(request.args.to_dict(), **new_view_args)))
                except BuildError as e:
                    if current_app.debug:
                        raise
                    logger.warn('BuildError during normalization: %s', e)
                    raise NotFound
            else:
                raise NotFound(
                    'The URL contains invalid data. Please go to the previous page and refresh it.'
                )
Beispiel #11
0
def test_get_locator_none():
    with pytest.raises(TypeError):
        get_locator(object())
    with pytest.raises(TypeError):
        get_locator(None)
Beispiel #12
0
def test_get_locator_none():
    with pytest.raises(TypeError):
        get_locator(object())
    with pytest.raises(TypeError):
        get_locator(None)
Beispiel #13
0
    def normalize_url(self):
        """Performs URL normalization.

        This uses the :attr:`normalize_url_spec` to check if the URL
        params are what they should be and redirects or fails depending
        on the HTTP method used if it's not the case.

        :return: ``None`` or a redirect response
        """
        if current_app.debug and self.normalize_url_spec is RH.normalize_url_spec:
            # in case of ``class SomeRH(RH, MixinWithNormalization)``
            # the default value from `RH` overwrites the normalization
            # rule from ``MixinWithNormalization``.  this is never what
            # the developer wants so we fail if it happens.  the proper
            # solution is ``class SomeRH(MixinWithNormalization, RH)``
            cls = next((x
                        for x in inspect.getmro(self.__class__)
                        if (x is not RH and x is not self.__class__ and hasattr(x, 'normalize_url_spec') and
                            getattr(x, 'normalize_url_spec', None) is not RH.normalize_url_spec)),
                       None)
            if cls is not None:
                raise Exception('Normalization rule of {} in {} is overwritten by base RH. Put mixins with class-level '
                                'attributes on the left of the base class'.format(cls, self.__class__))
        if not self.normalize_url_spec or not any(self.normalize_url_spec.itervalues()):
            return
        spec = {
            'args': self.normalize_url_spec.get('args', {}),
            'locators': self.normalize_url_spec.get('locators', set()),
            'preserved_args': self.normalize_url_spec.get('preserved_args', set()),
            'endpoint': self.normalize_url_spec.get('endpoint', None)
        }
        # Initialize the new view args with preserved arguments (since those would be lost otherwise)
        new_view_args = {k: v for k, v in request.view_args.iteritems() if k in spec['preserved_args']}
        # Retrieve the expected values for all simple arguments (if they are currently present)
        for key, getter in spec['args'].iteritems():
            if key in request.view_args:
                new_view_args[key] = getter(self)
        # Retrieve the expected values from locators
        prev_locator_args = {}
        for getter in spec['locators']:
            value = getter(self)
            if value is None:
                raise NotFound('The URL contains invalid data. Please go to the previous page and refresh it.')
            locator_args = get_locator(value)
            reused_keys = set(locator_args) & prev_locator_args.viewkeys()
            if any(locator_args[k] != prev_locator_args[k] for k in reused_keys):
                raise NotFound('The URL contains invalid data. Please go to the previous page and refresh it.')
            new_view_args.update(locator_args)
            prev_locator_args.update(locator_args)
        # Get all default values provided by the url map for the endpoint
        defaults = set(itertools.chain.from_iterable(r.defaults
                                                     for r in current_app.url_map.iter_rules(request.endpoint)
                                                     if r.defaults))

        def _convert(v):
            # some legacy code has numeric ids in the locator data, but still takes
            # string ids in the url rule (usually for confId)
            return unicode(v) if isinstance(v, (int, long)) else v

        provided = {k: _convert(v) for k, v in request.view_args.iteritems() if k not in defaults}
        new_view_args = {k: _convert(v) for k, v in new_view_args.iteritems()}
        if new_view_args != provided:
            if request.method in {'GET', 'HEAD'}:
                endpoint = spec['endpoint'] or request.endpoint
                try:
                    return redirect(url_for(endpoint, **dict(request.args.to_dict(), **new_view_args)))
                except BuildError as e:
                    if current_app.debug:
                        raise
                    logger.warn('BuildError during normalization: %s', e)
                    raise NotFound
            else:
                raise NotFound('The URL contains invalid data. Please go to the previous page and refresh it.')