diff --git a/tests/test_validator.py b/tests/test_validator.py new file mode 100644 index 0000000..cf89fc1 --- /dev/null +++ b/tests/test_validator.py @@ -0,0 +1,43 @@ +"""Tests for the uritemplate.Validator class.""" +import uritemplate +from uritemplate import exceptions + +import pytest + + +@pytest.mark.parametrize('template', [ + 'https://github.com{/user}', + 'https://github.com/sigmavirus24{/repository}', + '{foo}', + '?{bar}', + 'https://example.com', +]) +def test_valid_uris(template): + """Verify we don't raise an exception.""" + urit = uritemplate.URITemplate(template) + uritemplate.Validator().validate(urit) + + +@pytest.mark.parametrize('template', [ + 'https://github.com{/user', + 'https://github.com/sigmavirus24/repository}', + '{foo}}', + '?{{bar}', +]) +def test_invalid_uris(template): + """Verify we catch invalid URITemplates.""" + urit = uritemplate.URITemplate(template) + with pytest.raises(exceptions.InvalidTemplate): + uritemplate.Validator().validate(urit) + + +@pytest.mark.parametrize('template', [ + 'https://github.com{/user', + 'https://github.com/sigmavirus24/repository}', + '{foo}}', + '?{{bar}', +]) +def test_allow_invalid_uris(template): + """Verify we allow invalid URITemplates.""" + urit = uritemplate.URITemplate(template) + uritemplate.Validator().allow_unbalanced_braces().validate(urit) diff --git a/uritemplate/__init__.py b/uritemplate/__init__.py index 40c0320..4427a08 100644 --- a/uritemplate/__init__.py +++ b/uritemplate/__init__.py @@ -16,11 +16,12 @@ __author__ = 'Ian Cordasco' __license__ = 'Modified BSD or Apache License, Version 2.0' __copyright__ = 'Copyright 2013 Ian Cordasco' -__version__ = '3.0.0' +__version__ = '3.1.0' __version_info__ = tuple(int(i) for i in __version__.split('.') if i.isdigit()) from uritemplate.api import ( URITemplate, expand, partial, variables # noqa: E402 ) +from uritemplate.validator import Validator # noqa: E402 -__all__ = ('URITemplate', 'expand', 'partial', 'variables') +__all__ = ('URITemplate', 'Validator', 'expand', 'partial', 'variables') diff --git a/uritemplate/exceptions.py b/uritemplate/exceptions.py new file mode 100644 index 0000000..8edac8f --- /dev/null +++ b/uritemplate/exceptions.py @@ -0,0 +1,31 @@ +"""Module containing all exceptions for the uritemplate module.""" + + +class TemplateException(Exception): + """Base Exception class for all uritemplate exceptions.""" + + pass + + +class InvalidTemplate(TemplateException): + """Base class for template validation.""" + + message = "The URI template ({0}) is invalid." + + def __init__(self, uri, *args): + """Initialize our exception.""" + super(InvalidTemplate, self).__init__(self.message.format(uri, *args)) + self.uri = uri + + +class UnbalancedBraces(InvalidTemplate): + """The template has unbalanced braces.""" + + message = "The URI template ({0}) has more {1} braces than {2} braces." + + def __init__(self, uri, left_braces_count, right_braces_count): + """Initialize our exception.""" + more, less = 'left', 'right' + if left_braces_count < right_braces_count: + more, less = less, more + super(UnbalancedBraces, self).__init__(uri, more, less) diff --git a/uritemplate/validator.py b/uritemplate/validator.py new file mode 100644 index 0000000..07552b9 --- /dev/null +++ b/uritemplate/validator.py @@ -0,0 +1,58 @@ +"""Module containing all the validation logic for uritemplate.""" +from uritemplate import exceptions as exc + + +class Validator(object): + """Provide configurable validation of URITemplate objects. + + .. versionadded:: 3.1.0 + + .. code-block:: + + from uritemplate import URITemplate, Validator + + + """ + + def __init__(self): + """Initialize our validator.""" + self.enforcing_unbalanced_braces = True + + def allow_unbalanced_braces(self): + """Allow a template to have unbalanced braces. + + .. versionadded:: 3.1.0 + + Returns the validator instance. + """ + self.enforcing_unbalanced_braces = False + return self + + def force_balanced_braces(self): + """Force a template to have balanced braces. + + .. versionadded:: 3.1.0 + + Returns the validator instance. + """ + self.enforcing_unbalanced_braces = True + return self + + def validate(self, template): + """Validate that a template meets the parameters. + + .. versionadded:: 3.1.0 + + :raises: uritemplate.exceptions.InvalidTemplate + :raises: uritemplate.exceptions.UnbalancedBraces + """ + if self.enforcing_unbalanced_braces: + _enforce_balanced_braces(template) + + +def _enforce_balanced_braces(template): + uri = template.uri + left_braces = uri.count('{') + right_braces = uri.count('}') + if left_braces != right_braces: + raise exc.UnbalancedBraces(uri, left_braces, right_braces)