URL utils

This module contains some utility modules for handling URL building, and the aspect of handling several parts of the URL, each separated by different separators, that may be provided or not (thus, handling separators becomes a bit more complicated).

django_crucrudile.urlutils.pass_tuple(count=1)[source]

Returns a decorator that wraps a function to make it run witout the first part of a tuple in its original arguments and return the omitted arguments contatenated with its original return value.

Parameters:count (int) – Number of arguments to omit, default 1.
Returns:Decorator
Return type:function

Warning

This function is not the actual decorator, but a function that returns that decorator (with the given tuple slice index). If it is used as a decorator, it should be written @pass_tuple() instead of @pass_tuple.

>>> pack_with_42 = lambda x: (42, x)
>>> pack_with_42(8)
(42, 8)
>>> add_2 = pass_tuple()(lambda x: x + 2)
>>> add_2(pack_with_42(8))
(42, 10)
>>> mul_2 = pass_tuple()(lambda x: x*2)
>>> mul_2(add_2(pack_with_42(8)))
(42, 20)
>>> unpack = lambda x: x[0] + x[1]
>>> unpack(mul_2(add_2(pack_with_42(8))))
62
django_crucrudile.urlutils.compose(functions, *args, **kwargs)[source]

Compose functions together

Parameters:functions (list of callables) – Functions to compose
Returns:Composed function
Return type:function

Note

This function will pass all other arguments and keyword arguments to the composed functions.

>>> compose([lambda x: x*2, lambda x: x+2])(5)
12
class django_crucrudile.urlutils.Separated(*args, separator=None, opt_separator=None, required_default=None, **kwargs)[source]

Bases: builtins.object

Accepts separator options in __init__(), and provide get_separator(), that returns the corresponding separator for required and optional parts, based on the separator passed to __init__() (or set at class-level).

Inheritance diagram of Separated

separator = '/'
Attribute separator:
 Separator to use in front of a required item
opt_separator = '/?'
Attribute opt_separator:
 Separator to use in front of an optional item
required_default = True
Attribute required_default:
 If True, items required by default (when None)
get_separator(required=None)[source]

Get the argument separator to use according to the required argument

Parameters:required (bool) – If False, will return the optional argument separator instead of the regular one. Default is True.
Returns:Separator
Return type:str
>>> Separated().get_separator()
'/'
>>> Separated().get_separator(True)
'/'
>>> Separated().get_separator(False)
'/?'
class django_crucrudile.urlutils.Parsable[source]

Bases: builtins.object

Class whose instances may be called, to return a “parsed” version, obtained by passing the original version in the parsers returned by get_parsers().

Inheritance diagram of Parsable

>>> class TestParsable(Parsable, int):
...   def get_parsers(self):
...     return [lambda x: x*2, lambda x: x+2]
>>>
>>> TestParsable(5)()
12
>>> class TestParsable(Parsable, int):
...   def get_parsers(self):
...     return []
>>>
>>> TestParsable(5)()
5
>>> class TestParsable(Parsable, int):
...   def get_parsers(self):
...     return [lambda x: None]
>>>
>>> TestParsable(5)()
>>> class TestParsable(Parsable, int):
...   def get_parsers(self):
...     return [None]
>>>
>>> TestParsable(5)()
Traceback (most recent call last):
  ...
TypeError: the first argument must be callable
>>> class TestParsable(Parsable, int):
...   def get_parsers(self):
...     return [lambda x, y: None]
>>>
>>> TestParsable(5)()
Traceback (most recent call last):
  ...
TypeError: <lambda>() missing 1 required positional argument: 'y'
>>> class TestParsable(Parsable, int):
...   def get_parsers(self):
...     return [lambda: None]
>>>
>>> TestParsable(5)()
Traceback (most recent call last):
  ...
TypeError: <lambda>() takes 0 positional arguments but 1 was given
get_parsers()[source]

Return parsers list. Base implementation returns an empty list. To add new parsers, override this function and append/prepend the functions to use as parsers.

Returns:List of parser functions
Return type:list
class django_crucrudile.urlutils.OptionalPartList(iterable=None, separator=None, opt_separator=None, required_default=None)[source]

Bases: django_crucrudile.urlutils.Separated, django_crucrudile.urlutils.Parsable, builtins.list

Implement Separated and Parsable into a list, to make a separated, parsable URL part list, that handles optional parts and that uses registered parsers (from get_parsers()) when the instance is called.

Provide two base parsers, that convert, if needed, original items in 2-tuples (transform_to_tuple()), and provide a default value for the first item of the tuple if it’s None (apply_required_default()).

Inheritance diagram of OptionalPartList

>>> builder = OptionalPartList(
...   ["<1>", (None, "<2>"), (False, "<3>")]
... )
>>>
>>> list(builder())
[(True, '<1>'), (True, '<2>'), (False, '<3>')]
>>> failing_builder = OptionalPartList(
...   ["<1>", (None, "<2>"), (False, "<3>", "fail")]
... )
>>>
>>> list(failing_builder())
Traceback (most recent call last):
  ...
ValueError: too many values to unpack (expected 2)
get_parsers()[source]

Complement OptionalPartList parsers (from OptionalPartList.get_parsers()) with transform_to_tuple() and apply_required_default().

Returns:List of parser functions
Return type:list
static transform_to_tuple(items)[source]

Transform each item to a tuple if it’s not one

Parameters:items (iterable) – List of items and tuples
Returns:List of tuples
Return type:iterable of tuple
>>> list(OptionalPartList.transform_to_tuple([
...   '<1>',
...   (None, '<2>')
... ]))
[(None, '<1>'), (None, '<2>')]
static apply_required_default(items, default)[source]

Apply default value to first element of item if it’s None.

Parameters:
  • items (iterable) – List of tuples
  • default (boolean) – Value to use if none provided
Returns:

List of tuples, with required default value applied

Return type:

iterable of tuple

>>> list(
...   OptionalPartList.apply_required_default(
...     [
...       ('<provided>', '<1>'),
...       (None, '<2>')
...     ],
...     default='<default>'
...   )
... )
[('<provided>', '<1>'), ('<default>', '<2>')]
class django_crucrudile.urlutils.URLBuilder(iterable=None, separator=None, opt_separator=None, required_default=None)[source]

Bases: django_crucrudile.urlutils.OptionalPartList

Allows building URLs from a list of URL parts. The parts can be required or optional, this information will be used to determine which separator to use.

We subclass OptionalPartList, and add our parsers in get_parsers(), so that they are used when the instance gets called :

Inheritance diagram of URLBuilder

>>> builder = URLBuilder(
...   ["<1>", (False, "<2>"), (True, "<3>")]
... )
>>>
>>> builder()
(True, '<1>/?<2>/<3>')
>>> builder = URLBuilder(
...   ["<1>", "<2>", (False, "<3>")]
... )
>>>
>>> builder()
(True, '<1>/<2>/?<3>')
>>> builder = URLBuilder(
...   [(False, "<1>"), "<2>", (False, "<3>")]
... )
>>>
>>> builder()
(False, '<1>/<2>/?<3>')
>>> builder = URLBuilder(
...   [(False, "<1>"), None, (True, None)]
... )
>>>
>>> builder()
(False, '<1>')
>>> builder = URLBuilder(
...   [(False, "<1>"), 1]
... )
>>>
>>> builder()
Traceback (most recent call last):
  ...
TypeError: sequence item 2: expected str instance, int found
get_parsers()[source]

Complement OptionalPartList parsers (from OptionalPartList.get_parsers()) with filter_empty_items(), add_first_item_required_flag(), flatten() and join().

Returns:List of parser functions
Return type:list
static filter_empty_items(items)[source]

Filter out items that give False when casted to boolean.

Parameters:items (iterable) – List of tuples
Returns:List of URL part specs (with empty items cleared out)
Return type:list of tuple
>>> list(URLBuilder.filter_empty_items([
...   (None, ''),
...   (None, '<not empty>'),
...   (None, []),
...   (None, None),
...   (None, '<not empty 2>'),
... ]))
[(None, '<not empty>'), (None, '<not empty 2>')]
>>> list(URLBuilder.filter_empty_items([
...   (None, '<not empty>'),
...   None
... ]))
Traceback (most recent call last):
  ...
TypeError: 'NoneType' object is not iterable
static add_first_item_required_flag(items)[source]

Return a boolean indicating whether the first item is required, and the list of items.

Parameters:items (iterable) – List of tuples
Returns:Tuple with “first item required” flag, and item list
Return type:tuple : (boolean, list)
>>> output = URLBuilder.add_first_item_required_flag(
...   [(False, '<opt>'), (True, '<req>')]
... )
>>>
>>> output[0], list(output[1])
(False, [(False, '<opt>'), (True, '<req>')])
>>> output = URLBuilder.add_first_item_required_flag(
...   []
... )
>>>
>>> output[0], list(output[1])
(False, [])
>>> output = URLBuilder.add_first_item_required_flag(
...   [(None, )*3]
... )
Traceback (most recent call last):
  ...
ValueError: too many values to unpack (expected 2)
static flatten(items, get_separator)[source]

Flatten items, adding the separator where required.

Parameters:items (iterable) – List of tuples
Returns:List of URL parts with separators
Return type:iterable of str

Warning

This function is decorated using pass_tuple(), the first part of the tuple in its arguments will be omitted, and inserted at the beginning of the return value, automatically. See the documentation of pass_tuple() for more information.

>>> get_separator = lambda x: '/'
>>> output = URLBuilder.flatten(
...   (None, [(True, '<1>'), (True, '<2>')]),
...   get_separator
... )
>>>
>>> output[0], list(output[1])
(None, ['<1>', '/', '<2>'])
>>> from mock import Mock
>>>
>>> get_separator = Mock()
>>> get_separator.side_effect = ['/']
>>> output = URLBuilder.flatten(
...   (None, [(True, '<1>'), (True, '<2>')]),
...   get_separator
... )
>>>
>>> output[0], list(output[1])
(None, ['<1>', '/', '<2>'])
>>> get_separator.assert_called_once_with(True)
static join(items)[source]

Concatenate items into a string

Parameters:items (list of str) – List of URL parts, with separators
Returns:Joined URL parts
Return type:str

Warning

This function is decorated using pass_tuple(), the first part of the tuple in its arguments will be passed automatically. See the documentation of pass_tuple() for more information.

>>> URLBuilder.join((None, ['a', 'b']))
(None, 'ab')
>>> URLBuilder.join((None, [['a'], 'b']))
Traceback (most recent call last):
  ...
TypeError: sequence item 0: expected str instance, list found
>>> URLBuilder.join((None, ['a', None]))
Traceback (most recent call last):
  ...
TypeError: sequence item 1: expected str instance, NoneType found

Decorators

This module defines the pass_tuple() decorator, that makes a function :
  • run witout the first part of its original arguments
  • return the omitted arguments and its original return value

This allows to use chain of functions in Parsable that work only on a slice of the arguments (for example, to run some operations while passing a boolean flag).

django_crucrudile.urlutils.pass_tuple(count=1)[source]

Returns a decorator that wraps a function to make it run witout the first part of a tuple in its original arguments and return the omitted arguments contatenated with its original return value.

Parameters:count (int) – Number of arguments to omit, default 1.
Returns:Decorator
Return type:function

Warning

This function is not the actual decorator, but a function that returns that decorator (with the given tuple slice index). If it is used as a decorator, it should be written @pass_tuple() instead of @pass_tuple.

>>> pack_with_42 = lambda x: (42, x)
>>> pack_with_42(8)
(42, 8)
>>> add_2 = pass_tuple()(lambda x: x + 2)
>>> add_2(pack_with_42(8))
(42, 10)
>>> mul_2 = pass_tuple()(lambda x: x*2)
>>> mul_2(add_2(pack_with_42(8)))
(42, 20)
>>> unpack = lambda x: x[0] + x[1]
>>> unpack(mul_2(add_2(pack_with_42(8))))
62

Functions

This module defines the compose() function, that compose a list of functions into a single function that returns its arguments, passed in chain to each of the functions. This function is used by Parsable to compose the parsers returned by Parsable.get_parsers(), in Parsable.__call__().

django_crucrudile.urlutils.compose(functions, *args, **kwargs)[source]

Compose functions together

Parameters:functions (list of callables) – Functions to compose
Returns:Composed function
Return type:function

Note

This function will pass all other arguments and keyword arguments to the composed functions.

>>> compose([lambda x: x*2, lambda x: x+2])(5)
12

Classes

Generic

Note

These classes provide bases for the URLBuilder and django_crucrudile.routes.mixins.arguments.parser.ArgumentsParser classes.

class django_crucrudile.urlutils.Separated(*args, separator=None, opt_separator=None, required_default=None, **kwargs)[source]

Bases: builtins.object

Accepts separator options in __init__(), and provide get_separator(), that returns the corresponding separator for required and optional parts, based on the separator passed to __init__() (or set at class-level).

Inheritance diagram of Separated

__init__(*args, separator=None, opt_separator=None, required_default=None, **kwargs)[source]

Initialize, set separator options

Parameters:
separator = '/'
Attribute separator:
 Separator to use in front of a required item
opt_separator = '/?'
Attribute opt_separator:
 Separator to use in front of an optional item
required_default = True
Attribute required_default:
 If True, items required by default (when None)
get_separator(required=None)[source]

Get the argument separator to use according to the required argument

Parameters:required (bool) – If False, will return the optional argument separator instead of the regular one. Default is True.
Returns:Separator
Return type:str
>>> Separated().get_separator()
'/'
>>> Separated().get_separator(True)
'/'
>>> Separated().get_separator(False)
'/?'
class django_crucrudile.urlutils.Parsable[source]

Bases: builtins.object

Class whose instances may be called, to return a “parsed” version, obtained by passing the original version in the parsers returned by get_parsers().

Inheritance diagram of Parsable

>>> class TestParsable(Parsable, int):
...   def get_parsers(self):
...     return [lambda x: x*2, lambda x: x+2]
>>>
>>> TestParsable(5)()
12
>>> class TestParsable(Parsable, int):
...   def get_parsers(self):
...     return []
>>>
>>> TestParsable(5)()
5
>>> class TestParsable(Parsable, int):
...   def get_parsers(self):
...     return [lambda x: None]
>>>
>>> TestParsable(5)()
>>> class TestParsable(Parsable, int):
...   def get_parsers(self):
...     return [None]
>>>
>>> TestParsable(5)()
Traceback (most recent call last):
  ...
TypeError: the first argument must be callable
>>> class TestParsable(Parsable, int):
...   def get_parsers(self):
...     return [lambda x, y: None]
>>>
>>> TestParsable(5)()
Traceback (most recent call last):
  ...
TypeError: <lambda>() missing 1 required positional argument: 'y'
>>> class TestParsable(Parsable, int):
...   def get_parsers(self):
...     return [lambda: None]
>>>
>>> TestParsable(5)()
Traceback (most recent call last):
  ...
TypeError: <lambda>() takes 0 positional arguments but 1 was given
get_parsers()[source]

Return parsers list. Base implementation returns an empty list. To add new parsers, override this function and append/prepend the functions to use as parsers.

Returns:List of parser functions
Return type:list
__call__()[source]

Compose the parsers in get_parsers() using compose(), and use the composed function to get the parsed version from the original version.

Returns:output of parsers

See also

For doctests that use this member, see Parsable

URL parts classes

Optional URL parts list

digraph opt_part_list_parsers {
    bgcolor="transparent"
    edge[fontsize=10, weight=1.2]
    node[fontsize=12, nodesep=0.75, ranksep=0.75]

    subgraph ttt {
        rank="same"
        rankdir="LR"
        "..."[style="filled", fillcolor="#BBFFBB", color="green"]
        "[(required, ...)]"[style="filled", fillcolor="#BBFFBB", color="green"]
        "transform_to_tuple"[style="filled", fillcolor="#BBBBFF", color="blue"]
    }

    subgraph ard {
        rank="same"
        rankdir="LR"
        "[(None, ...)]"
        "[(bool, ...)]"
        "apply_required_default"[style="filled", fillcolor="#BBBBFF", color="blue"]
    }

    "..." -> "transform_to_tuple"

    subgraph rev {
        edge[dir="back"]
        "transform_to_tuple" -> "[(required, ...)]"
    }

    "transform_to_tuple" -> "[(required, ...)] "

    "[(required, ...)] " -> "[(None, ...)]"
    "[(required, ...)] " -> "[(bool, ...)]"

    "[(None, ...)]" -> "apply_required_default"

    subgraph rev {
        edge[dir="back"]
        "apply_required_default" -> "[(bool, ...)]"
    }

    "apply_required_default" -> "[(bool, ...)] "

    subgraph types {
        node[style=filled, color="#eeeeee",
             fontcolor="#555555", fontsize=10]

        "[(required, ...)]"
        "..."
        "[(required, ...)] "[style="filled", fillcolor="#FFFFBB"]
        "[(None, ...)]"[style="filled", fillcolor="#FFFFBB"]
        "[(bool, ...)]"[style="filled", fillcolor="#FFFFBB"]
        "[(bool, ...)] "[style="filled", fillcolor="#FFBBBB", color="red"]
    }

    subgraph io {
       rank="other"
       rankdir="TB"
       node[style="filled"]
       edge[style="invis"]
       "Input"[fillcolor="#BBFFBB", color="green"]
       "Output"[fillcolor="#FFBBBB", color="red"]
       "Intermediate\n values"[fillcolor="#FFFFBB"]
       "Parser"[fillcolor="#BBBBFF", color="blue"]

       "Input" -> "Parser" -> "Intermediate\n values" -> "Output"
    }

}

class django_crucrudile.urlutils.OptionalPartList(iterable=None, separator=None, opt_separator=None, required_default=None)[source]

Bases: django_crucrudile.urlutils.Separated, django_crucrudile.urlutils.Parsable, builtins.list

Implement Separated and Parsable into a list, to make a separated, parsable URL part list, that handles optional parts and that uses registered parsers (from get_parsers()) when the instance is called.

Provide two base parsers, that convert, if needed, original items in 2-tuples (transform_to_tuple()), and provide a default value for the first item of the tuple if it’s None (apply_required_default()).

Inheritance diagram of OptionalPartList

>>> builder = OptionalPartList(
...   ["<1>", (None, "<2>"), (False, "<3>")]
... )
>>>
>>> list(builder())
[(True, '<1>'), (True, '<2>'), (False, '<3>')]
>>> failing_builder = OptionalPartList(
...   ["<1>", (None, "<2>"), (False, "<3>", "fail")]
... )
>>>
>>> list(failing_builder())
Traceback (most recent call last):
  ...
ValueError: too many values to unpack (expected 2)
__add__(other)[source]

Concatenate with other iterable, creating a new object..

We override list.__add__() to return a new OptionalPartList instance, instead of a list instance.

Parameters:other (iterable) – Iterable to concatenate with
Returns:Concatenated object
Return type:type(self)
>>> a = OptionalPartList(['foo'])
>>> b = OptionalPartList(['bar'])
>>>
>>> a + b
['foo', 'bar']
>>>
>>> type(a + b)
<class 'django_crucrudile.urlutils.OptionalPartList'>
>>>
>>> (a + b) is a
False
>>>
>>> (a + b) is b
False
>>> (a + None) is a
True
__init__(iterable=None, separator=None, opt_separator=None, required_default=None)[source]

Initialize, use empty list as iterable if None provided.

Parameters:
get_parsers()[source]

Complement OptionalPartList parsers (from OptionalPartList.get_parsers()) with transform_to_tuple() and apply_required_default().

Returns:List of parser functions
Return type:list
static transform_to_tuple(items)[source]

Transform each item to a tuple if it’s not one

Parameters:items (iterable) – List of items and tuples
Returns:List of tuples
Return type:iterable of tuple
>>> list(OptionalPartList.transform_to_tuple([
...   '<1>',
...   (None, '<2>')
... ]))
[(None, '<1>'), (None, '<2>')]
static apply_required_default(items, default)[source]

Apply default value to first element of item if it’s None.

Parameters:
  • items (iterable) – List of tuples
  • default (boolean) – Value to use if none provided
Returns:

List of tuples, with required default value applied

Return type:

iterable of tuple

>>> list(
...   OptionalPartList.apply_required_default(
...     [
...       ('<provided>', '<1>'),
...       (None, '<2>')
...     ],
...     default='<default>'
...   )
... )
[('<provided>', '<1>'), ('<default>', '<2>')]

URL Builder

digraph url_builder_parsers {
    bgcolor="transparent"
    edge[fontsize=10, weight=1.2]
    node[fontsize=12, nodesep=0.75, ranksep=0.75]
    subgraph ttt {
        rank="same"
        rankdir="LR"
        "..."[style="filled", fillcolor="#BBFFBB", color="green"]
        "transform_to_tuple"[style="filled", fillcolor="#BBBBFF", color="blue"]           "[(required, ...)]"[style="filled", fillcolor="#BBFFBB", color="green"]

    }

    subgraph ard {
        rank="same"
        rankdir="LR"
        "[(None, ...)]"[style="filled", fillcolor="#FFFFBB"]
        "apply_required_default"[style="filled", fillcolor="#BBBBFF", color="blue"]           "[(bool, ...)]"[style="filled", fillcolor="#FFFFBB"]

    }

    subgraph fei {
        rank="same"
        rankdir="LR"
        "[(required, None)]"[style="filled", fillcolor="#FFFFBB"]
        "filter_empty_items"[style="filled", fillcolor="#BBBBFF", color="blue"]
        "[(required, not None)]"[style="filled", fillcolor="#FFFFBB"]
    }

    subgraph afirf {
        rank="same"
        rankdir="LR"
        "add_first_item_required_flag"[style="filled", fillcolor="#BBBBFF", color="blue"]
        "[(required, not None)] "[style="filled", fillcolor="#FFFFBB"]
    }

    subgraph flatten {
        rank="same"
        rankdir="RL"
        "flatten"[style="filled", fillcolor="#BBBBFF", color="blue"]
        "(bool, [(required, not None)])"[style="filled", fillcolor="#FFFFBB"]
    }

    subgraph join {
        rank="same"
        rankdir="RL"
        "join"[style="filled", fillcolor="#BBBBFF", color="blue"]
        "(bool, [str])"[style="filled", fillcolor="#FFFFBB"]
    }

    "[(required, ...)]" -> "transform_to_tuple"

    subgraph rev {
       edge[dir="back"]
       "transform_to_tuple" -> "..."
    }

    "transform_to_tuple" -> "[(required, ...)] "

    "[(required, ...)] " -> "[(None, ...)]"
    "[(required, ...)] " -> "[(bool, ...)]"

    "[(None, ...)]" -> "apply_required_default"
    subgraph rev {
       edge[dir="back"]
       "apply_required_default" -> "[(bool, ...)]"
    }
    "apply_required_default" -> "[(bool, ...)] "

    "[(bool, ...)] " -> "[(required, None)]"
    "[(bool, ...)] " -> "[(required, not None)]"

    "[(required, None)]" -> "filter_empty_items"

    subgraph rev {
        edge[dir="back"]
        "filter_empty_items" -> "[(required, not None)]"
    }

    "filter_empty_items" -> "[(required, not None)] "


    subgraph rev {
        edge[dir="back"]
        "add_first_item_required_flag" -> "[(required, not None)] "
    }

    "add_first_item_required_flag" -> "(bool, [(required, not None)])"
    "(bool, [(required, not None)])" -> "flatten"
    "flatten" -> "(bool, [str])"

    subgraph rev {
        edge[dir="back"]
        "join" -> "(bool, [str])"
    }

    "join" -> "(bool, str)"

    subgraph types {
        node[style=filled, color="#eeeeee",
             fontcolor="#555555", fontsize=10]

        "[(required, ...)]"
        "..."
        "[(required, ...)] "[style="filled", fillcolor="#FFFFBB"]
        "[(None, ...)]"[style="filled", fillcolor="#FFFFBB"]
        "[(bool, ...)]"[style="filled", fillcolor="#FFFFBB"]
        "[(bool, ...)] "[style="filled", fillcolor="#FFFFBB"]
    }

    "(bool, str)"[style="filled", fillcolor="#BBFFBB", color="green"]

    subgraph io {
        rank="other"
        rankdir="TB"
        node[style="filled"]
        edge[style="invis"]
        "Input"[fillcolor="#BBFFBB", color="green"]
        "Output"[fillcolor="#FFBBBB", color="red"]
        "Intermediate\n values"[fillcolor="#FFFFBB"]
        "Parser"[fillcolor="#BBBBFF", color="blue"]
        "Input" -> "Parser" -> "Intermediate\n values" -> "Output"
    }
}

class django_crucrudile.urlutils.URLBuilder(iterable=None, separator=None, opt_separator=None, required_default=None)[source]

Bases: django_crucrudile.urlutils.OptionalPartList

Allows building URLs from a list of URL parts. The parts can be required or optional, this information will be used to determine which separator to use.

We subclass OptionalPartList, and add our parsers in get_parsers(), so that they are used when the instance gets called :

Inheritance diagram of URLBuilder

>>> builder = URLBuilder(
...   ["<1>", (False, "<2>"), (True, "<3>")]
... )
>>>
>>> builder()
(True, '<1>/?<2>/<3>')
>>> builder = URLBuilder(
...   ["<1>", "<2>", (False, "<3>")]
... )
>>>
>>> builder()
(True, '<1>/<2>/?<3>')
>>> builder = URLBuilder(
...   [(False, "<1>"), "<2>", (False, "<3>")]
... )
>>>
>>> builder()
(False, '<1>/<2>/?<3>')
>>> builder = URLBuilder(
...   [(False, "<1>"), None, (True, None)]
... )
>>>
>>> builder()
(False, '<1>')
>>> builder = URLBuilder(
...   [(False, "<1>"), 1]
... )
>>>
>>> builder()
Traceback (most recent call last):
  ...
TypeError: sequence item 2: expected str instance, int found
get_parsers()[source]

Complement OptionalPartList parsers (from OptionalPartList.get_parsers()) with filter_empty_items(), add_first_item_required_flag(), flatten() and join().

Returns:List of parser functions
Return type:list
static filter_empty_items(items)[source]

Filter out items that give False when casted to boolean.

Parameters:items (iterable) – List of tuples
Returns:List of URL part specs (with empty items cleared out)
Return type:list of tuple
>>> list(URLBuilder.filter_empty_items([
...   (None, ''),
...   (None, '<not empty>'),
...   (None, []),
...   (None, None),
...   (None, '<not empty 2>'),
... ]))
[(None, '<not empty>'), (None, '<not empty 2>')]
>>> list(URLBuilder.filter_empty_items([
...   (None, '<not empty>'),
...   None
... ]))
Traceback (most recent call last):
  ...
TypeError: 'NoneType' object is not iterable
static add_first_item_required_flag(items)[source]

Return a boolean indicating whether the first item is required, and the list of items.

Parameters:items (iterable) – List of tuples
Returns:Tuple with “first item required” flag, and item list
Return type:tuple : (boolean, list)
>>> output = URLBuilder.add_first_item_required_flag(
...   [(False, '<opt>'), (True, '<req>')]
... )
>>>
>>> output[0], list(output[1])
(False, [(False, '<opt>'), (True, '<req>')])
>>> output = URLBuilder.add_first_item_required_flag(
...   []
... )
>>>
>>> output[0], list(output[1])
(False, [])
>>> output = URLBuilder.add_first_item_required_flag(
...   [(None, )*3]
... )
Traceback (most recent call last):
  ...
ValueError: too many values to unpack (expected 2)
static flatten(items, get_separator)[source]

Flatten items, adding the separator where required.

Parameters:items (iterable) – List of tuples
Returns:List of URL parts with separators
Return type:iterable of str

Warning

This function is decorated using pass_tuple(), the first part of the tuple in its arguments will be omitted, and inserted at the beginning of the return value, automatically. See the documentation of pass_tuple() for more information.

>>> get_separator = lambda x: '/'
>>> output = URLBuilder.flatten(
...   (None, [(True, '<1>'), (True, '<2>')]),
...   get_separator
... )
>>>
>>> output[0], list(output[1])
(None, ['<1>', '/', '<2>'])
>>> from mock import Mock
>>>
>>> get_separator = Mock()
>>> get_separator.side_effect = ['/']
>>> output = URLBuilder.flatten(
...   (None, [(True, '<1>'), (True, '<2>')]),
...   get_separator
... )
>>>
>>> output[0], list(output[1])
(None, ['<1>', '/', '<2>'])
>>> get_separator.assert_called_once_with(True)
static join(items)[source]

Concatenate items into a string

Parameters:items (list of str) – List of URL parts, with separators
Returns:Joined URL parts
Return type:str

Warning

This function is decorated using pass_tuple(), the first part of the tuple in its arguments will be passed automatically. See the documentation of pass_tuple() for more information.

>>> URLBuilder.join((None, ['a', 'b']))
(None, 'ab')
>>> URLBuilder.join((None, [['a'], 'b']))
Traceback (most recent call last):
  ...
TypeError: sequence item 0: expected str instance, list found
>>> URLBuilder.join((None, ['a', None]))
Traceback (most recent call last):
  ...
TypeError: sequence item 1: expected str instance, NoneType found