<commit_before>"""
jsonselect

Public interface:
    select
    take a selector and an object. return matched node(s)

Exceptions:
    SelectorSyntaxError
    Raised by Parser when parsing cannot continue.

TODO:
fix (n) psuedo-var
T:nth-child(n)      1   A node of type T which is the nth child
                        of an array parent
T:nth-last-child(n) 2   A node of type T which is the nth child
                        of an array parent counting from the end
T:empty             2   A node of type T which is an array or object
                        with no child
T:has(S)            3   A node of type T which has a child node satisfying
                        the selector S
T:expr(E)           3   A node of type T with a value that satisfies
                        the expression E
T:val(V)            3   A node of type T with a value that is equal to V
T:contains(S)       3   A node of type T with a string value contains
                        the substring S
"""
import re
import numbers
import collections
import functools
import logging

S_TYPE = lambda x, token: ('type', token)
S_IDENTIFIER = lambda x, token: ('identifier', token[1:])
S_QUOTED_IDENTIFIER = lambda x, token: S_IDENTIFIER(None,
                                                    token.replace('"', ''))
S_PCLASS = lambda x, token: ('pclass', token[1:])
S_PCLASS_FUNC = lambda x, token: ('pclass_func', token[1:])
S_OPER = lambda x, token: ('operator', token)
S_EMPTY = lambda x, token:  ('empty', ' ')
S_UNK = lambda x, token: ('unknown', token)
S_INT = lambda x, token: ('int', int(token))
S_FLOAT = lambda x, token: ('float', float(token))
S_WORD = lambda x, token: ('word', token)
S_BINOP = lambda x, token: ('binop', token)
S_VALS = lambda x, token: ('val', token)
S_KEYWORD = lambda x, token: ('keyword', token)
S_VAR = lambda x, token: ('var', token)

SCANNER = re.Scanner([
    (r"[~*,>\)\(]", S_OPER),
    (r"\s", S_EMPTY),
    (r"(-?\d+(\.\d*)([eE][+\-]?\d+)?)", S_FLOAT),
    (r"\d+", S_INT),
    (r"string|boolean|null|array|object|number", S_TYPE),
    (r'\.?\"([^"\\]|\\[^"])*\"', S_QUOTED_IDENTIFIER),
    (u"\.([_a-zA-Z]|[^\0-\0177]|\\[^\s0-9a-fA-F])(?:[_a-zA-Z0-9\-]" \
     u"|[^\u0000-\u0177]|(?:\\[^\s0-9a-fA-F]))*", S_IDENTIFIER),
    (r":(root|empty|first-child|last-child|only-child)", S_PCLASS),
    (r":(nth-child|nth-last-child|has|expr|val|contains)", S_PCLASS_FUNC),
    (r"(&&|\|\||[\$\^<>!\*]=|[=+\-*/%<>])", S_BINOP),
    (r"true|false|null", S_VALS),
    (r"n", S_VAR),
    (r"odd|even", S_KEYWORD),
    (r"\w+", S_WORD),
])

log = logging.getLogger(__name__)

class SelectorSyntaxError(Exception):
    pass

def lex(selector):
    tokens, rest = SCANNER.scan(selector)
    if not len(tokens):
        if rest:
            raise Exception("leftover input: %s" % rest)
    return [tok for tok in tokens if tok[0] != 'empty']

# metadata about a node in the target object graph
Node = collections.namedtuple('Node', [
    'value',
    'parent',       # parent Node. None if root.
    'parent_key',   # if parent is a dict, key which indexes current Node.
    'idx',          # if parent is an array, index of curr Node. starts at 1.
    'siblings'      # if parent is an array, number of elements in it
])

def object_iter(obj, parent=None, parent_key=None, idx=None,
                siblings=None):
    """Yields each node of object graph in postorder."""

    obj_node = Node(value=obj, parent=parent, parent_key=parent_key,
                siblings=siblings, idx=idx)

    if isinstance(obj, list):
        _siblings = len(obj)
        for i, elem in enumerate(obj):
            for node in object_iter(elem, obj_node, None, i + 1, _siblings):
                yield node
    elif isinstance(obj, collections.Mapping):
        for key in obj:
            for node in object_iter(obj[key], obj_node, key):
                yield node
    yield obj_node

class Parser(object):

    """
    Parse jsonselect queries.

    A simple top-down recursive-descendant parser of jsonselect selectors.
    Initialize with the object you wish to match against.

    Clients should only need to call Parser.parse, which accepts
    a list of tokens as generated by jsonselect.lex
    """

    def __init__(self, obj):
        """Create a parser for a particular object."""
        self.obj = obj

    def parse(self, tokens):
        """Accept a list of tokens. Returns matched nodes of self.obj."""
        log.debug(self.obj)

        if self.peek(tokens, 'operator') == '*':
            self.match(tokens, 'operator')
            results = list(object_iter(self.obj))
        else:
            results = self.selector_production(tokens)

        results = [node.value for node in results]
        # single results should be returned as a primitive
        if len(results) == 1:
            return results[0]
        elif not len(results):
            return None
        return results

    def selector_production(self, tokens):
        """Production for a full selector."""

        log.debug(tokens)
        validators = []
        # the following productions should return predicate functions.

        if self.peek(tokens, 'type'):
            type_ = self.match(tokens, 'type')
            validators.append(self.type_production(type_))

        if self.peek(tokens, 'identifier'):
            key = self.match(tokens, 'identifier')
            validators.append(self.key_production(key))

        if self.peek(tokens, 'pclass'):
            pclass = self.match(tokens, 'pclass')
            validators.append(self.pclass_production(pclass))

        if self.peek(tokens, 'pclass_func'):
            pclass_func = self.match(tokens, 'pclass_func')
            if pclass_func[:4] == 'nth-':
                validators.append(self.nth_child_production(pclass_func, tokens))
            else:
                validators.append(
                    self.pclass_func_production(pclass_func, tokens)
                )

        if not len(validators):
            raise SelectorSyntaxError()

        # apply validators from a selector expression to self.obj
        results = self._match_nodes(validators, self.obj)

        if self.peek(tokens, 'operator'):
            operator = self.match(tokens, 'operator')
            rvals = self.selector_production(tokens)
            if operator == ',':
                results.extend(rvals)
            elif operator == '>':
                results = self.parents(results, rvals)
            elif operator == '~':
                results = self.siblings(results, rvals)
        else:
            if len(tokens):
                rvals = self.selector_production(tokens)
                results = self.ancestors(results, rvals)

        return results

    def parents(self, lhs, rhs):
        """Find nodes in rhs which have parents in lhs."""

        return [node for node in rhs if node.parent in lhs]

    def ancestors(self, lhs, rhs):
        """Return nodes from rhs which have ancestors in lhs."""

        def _search(node):
            if node in lhs:
                return True
            if not node.parent:
                return False
            return _search(node.parent)

        return [node for node in rhs if _search(node)]

    def siblings(self, lhs, rhs):
        """Find nodes in rhs having common parents in lhs."""
        parents = [node.parent for node in lhs]

        return [node for node in rhs if node.parent in parents]

    # The following productions should return predicate functions

    def type_production(self, type_):
        assert type_

        type_map = {
            'string': basestring,
            'number': numbers.Number,
            'object': collections.Mapping,
            'array': list,
            'boolean': bool,
            'null': type(None)
        }
        return lambda node: isinstance(node.value, type_map[type_])

    def key_production(self, key):
        assert key

        def validate(node):
            if not node.parent_key:
                return False
            return node.parent_key == key
        return validate

    def pclass_production(self, pclass):

        if pclass == 'first-child':
            return lambda node: node.idx == 1
        elif pclass == 'last-child':
            return lambda node: \
                node.siblings and node.idx == node.siblings
        elif pclass == 'only-child':
            return lambda node: node.siblings == 1
        elif pclass == 'root':
            return lambda node: not node.parent
        elif pclass == 'empty':
            return lambda node: isinstance(node.value, list) and not len(node.value)
        else:
            raise Exception("unrecognized pclass %s" % pclass)

    def pclass_func_production(self, pclass, tokens):
        if pclass == 'has':
            # T:has(S)
            # A node of type T which has a child node satisfying the selector S
            rvals = self.selector_production(tokens)

    def parse_pclass_func_args(self, tokens):
        """Parse arguments to a psuedoclass function.

        Raises SelectorSyntaxError if bad arguments found.

        TODO: trash this function (?)
        """
        expected = ['int', 'binop', 'float', 'var', 'keyword', 'operator']
        args = []

        if self.peek(tokens, 'operator') == '(':
            args.append(tokens.pop(0))
        else:
            raise SelectorSyntaxError()

        while tokens:
            if self.peek(tokens, 'operator') == '(':
                args.extend(self.parse_pclass_func_args(tokens))
            elif self.peek(tokens, 'operator') == ')':
                break
            # TODO: operators should maybe be parsed seperately.
            # Wouldn't expect to see operator tokens here.
            elif self.peek(tokens, expected):
                args.append(tokens.pop(0))
                continue
            else:
                raise SelectorSyntaxError()
        else:
            raise SelectorSyntaxError()

        if self.peek(tokens, 'operator') == ')':
            args.append(tokens.pop(0))
        else:
            raise SelectorSyntaxError()

        return args

    def nth_child_production(self, lexeme, tokens):
        """Parse args and pass them to pclass_func_validator."""

        args = self.parse_pclass_func_args(tokens)
        # TODO: compile this into a global
        x = ''.join([str(t[1]) for t in args])
        nth_pat = r"^\s*\(\s*(?:([+\-]?)([0-9]*)n\s*(?:([+\-])\s*([0-9]))?|(odd|even)|([+\-]?[0-9]+))\s*\)"
        pat = re.match(nth_pat, x)

        if pat.group(5):
            a = 2
            b = 1 if pat.group(5) == 'odd' else 0
        elif pat.group(6):
            a = 0
            b = int(pat.group(6))
        else:
            sign = pat.group(1) if pat.group(1) else '+'
            coef = pat.group(2) if pat.group(2) else '1'
            a = eval(sign + coef)
            b = eval(pat.group(3) + pat.group(4)) if pat.group(3) else 0

        reverse = False
        if lexeme == 'nth-last-child':
            reverse = True

        def validate(node):
            """This crazy function taken from jsonselect.js:444."""

            if not node.siblings:
                return False

            idx = node.idx - 1
            tot = node.siblings

            if reverse:
                idx = tot - idx
            else:
                idx += 1

            if a == 0:
                m = b == idx
            else:
                mod = (idx - b) % a
                m = not mod and (idx * a + b) >= 0
            return m

        return validate

    def _match_nodes(self, validators, obj):
        """Apply each validator in validators to each node in obj.

        Return each node in obj which matches all validators.
        """

        results = []
        for node in object_iter(obj):
            if all([validate(node) for validate in validators]):
                results.append(node)
        return results

    @staticmethod
    def match(tokens, type_):
        if not Parser.peek(tokens, type_):
            raise Exception('match not successful')

        token = tokens.pop(0)
        return token[1]

    @staticmethod
    def peek(tokens, type_):
        if not tokens:
            return False

        if isinstance(type_, list) and tokens[0][0] in type_:
            return tokens[0][1]
        elif tokens[0][0] == type_:
            return tokens[0][1]
        else:
            return False

def select(selector, obj):
    """Appy selector to obj and return matching nodes.

    If only one node is found, return it, otherwise return a list of matches.
    Returns False on syntax error. None if no results found.
    """

    parser = Parser(obj)
    try:
        return parser.parse(lex(selector))
    except SelectorSyntaxError:
        return False
<commit_msg>fix todo.<commit_after>"""
jsonselect

Public interface:
    select
    take a selector and an object. return matched node(s)

Exceptions:
    SelectorSyntaxError
    Raised by Parser when parsing cannot continue.

TODO:
T:empty             2   A node of type T which is an array or object
                        with no child
T:has(S)            3   A node of type T which has a child node satisfying
                        the selector S
T:expr(E)           3   A node of type T with a value that satisfies
                        the expression E
T:val(V)            3   A node of type T with a value that is equal to V
T:contains(S)       3   A node of type T with a string value contains
                        the substring S
"""
import re
import numbers
import collections
import functools
import logging

S_TYPE = lambda x, token: ('type', token)
S_IDENTIFIER = lambda x, token: ('identifier', token[1:])
S_QUOTED_IDENTIFIER = lambda x, token: S_IDENTIFIER(None,
                                                    token.replace('"', ''))
S_PCLASS = lambda x, token: ('pclass', token[1:])
S_PCLASS_FUNC = lambda x, token: ('pclass_func', token[1:])
S_OPER = lambda x, token: ('operator', token)
S_EMPTY = lambda x, token:  ('empty', ' ')
S_UNK = lambda x, token: ('unknown', token)
S_INT = lambda x, token: ('int', int(token))
S_FLOAT = lambda x, token: ('float', float(token))
S_WORD = lambda x, token: ('word', token)
S_BINOP = lambda x, token: ('binop', token)
S_VALS = lambda x, token: ('val', token)
S_KEYWORD = lambda x, token: ('keyword', token)
S_VAR = lambda x, token: ('var', token)

SCANNER = re.Scanner([
    (r"[~*,>\)\(]", S_OPER),
    (r"\s", S_EMPTY),
    (r"(-?\d+(\.\d*)([eE][+\-]?\d+)?)", S_FLOAT),
    (r"\d+", S_INT),
    (r"string|boolean|null|array|object|number", S_TYPE),
    (r'\.?\"([^"\\]|\\[^"])*\"', S_QUOTED_IDENTIFIER),
    (u"\.([_a-zA-Z]|[^\0-\0177]|\\[^\s0-9a-fA-F])(?:[_a-zA-Z0-9\-]" \
     u"|[^\u0000-\u0177]|(?:\\[^\s0-9a-fA-F]))*", S_IDENTIFIER),
    (r":(root|empty|first-child|last-child|only-child)", S_PCLASS),
    (r":(nth-child|nth-last-child|has|expr|val|contains)", S_PCLASS_FUNC),
    (r"(&&|\|\||[\$\^<>!\*]=|[=+\-*/%<>])", S_BINOP),
    (r"true|false|null", S_VALS),
    (r"n", S_VAR),
    (r"odd|even", S_KEYWORD),
    (r"\w+", S_WORD),
])

log = logging.getLogger(__name__)

class SelectorSyntaxError(Exception):
    pass

def lex(selector):
    tokens, rest = SCANNER.scan(selector)
    if not len(tokens):
        if rest:
            raise Exception("leftover input: %s" % rest)
    return [tok for tok in tokens if tok[0] != 'empty']

# metadata about a node in the target object graph
Node = collections.namedtuple('Node', [
    'value',
    'parent',       # parent Node. None if root.
    'parent_key',   # if parent is a dict, key which indexes current Node.
    'idx',          # if parent is an array, index of curr Node. starts at 1.
    'siblings'      # if parent is an array, number of elements in it
])

def object_iter(obj, parent=None, parent_key=None, idx=None,
                siblings=None):
    """Yields each node of object graph in postorder."""

    obj_node = Node(value=obj, parent=parent, parent_key=parent_key,
                siblings=siblings, idx=idx)

    if isinstance(obj, list):
        _siblings = len(obj)
        for i, elem in enumerate(obj):
            for node in object_iter(elem, obj_node, None, i + 1, _siblings):
                yield node
    elif isinstance(obj, collections.Mapping):
        for key in obj:
            for node in object_iter(obj[key], obj_node, key):
                yield node
    yield obj_node

class Parser(object):

    """
    Parse jsonselect queries.

    A simple top-down recursive-descendant parser of jsonselect selectors.
    Initialize with the object you wish to match against.

    Clients should only need to call Parser.parse, which accepts
    a list of tokens as generated by jsonselect.lex
    """

    def __init__(self, obj):
        """Create a parser for a particular object."""
        self.obj = obj

    def parse(self, tokens):
        """Accept a list of tokens. Returns matched nodes of self.obj."""
        log.debug(self.obj)

        if self.peek(tokens, 'operator') == '*':
            self.match(tokens, 'operator')
            results = list(object_iter(self.obj))
        else:
            results = self.selector_production(tokens)

        results = [node.value for node in results]
        # single results should be returned as a primitive
        if len(results) == 1:
            return results[0]
        elif not len(results):
            return None
        return results

    def selector_production(self, tokens):
        """Production for a full selector."""

        log.debug(tokens)
        validators = []
        # the following productions should return predicate functions.

        if self.peek(tokens, 'type'):
            type_ = self.match(tokens, 'type')
            validators.append(self.type_production(type_))

        if self.peek(tokens, 'identifier'):
            key = self.match(tokens, 'identifier')
            validators.append(self.key_production(key))

        if self.peek(tokens, 'pclass'):
            pclass = self.match(tokens, 'pclass')
            validators.append(self.pclass_production(pclass))

        if self.peek(tokens, 'pclass_func'):
            pclass_func = self.match(tokens, 'pclass_func')
            if pclass_func[:4] == 'nth-':
                validators.append(self.nth_child_production(pclass_func, tokens))
            else:
                validators.append(
                    self.pclass_func_production(pclass_func, tokens)
                )

        if not len(validators):
            raise SelectorSyntaxError()

        # apply validators from a selector expression to self.obj
        results = self._match_nodes(validators, self.obj)

        if self.peek(tokens, 'operator'):
            operator = self.match(tokens, 'operator')
            rvals = self.selector_production(tokens)
            if operator == ',':
                results.extend(rvals)
            elif operator == '>':
                results = self.parents(results, rvals)
            elif operator == '~':
                results = self.siblings(results, rvals)
        else:
            if len(tokens):
                rvals = self.selector_production(tokens)
                results = self.ancestors(results, rvals)

        return results

    def parents(self, lhs, rhs):
        """Find nodes in rhs which have parents in lhs."""

        return [node for node in rhs if node.parent in lhs]

    def ancestors(self, lhs, rhs):
        """Return nodes from rhs which have ancestors in lhs."""

        def _search(node):
            if node in lhs:
                return True
            if not node.parent:
                return False
            return _search(node.parent)

        return [node for node in rhs if _search(node)]

    def siblings(self, lhs, rhs):
        """Find nodes in rhs having common parents in lhs."""
        parents = [node.parent for node in lhs]

        return [node for node in rhs if node.parent in parents]

    # The following productions should return predicate functions

    def type_production(self, type_):
        assert type_

        type_map = {
            'string': basestring,
            'number': numbers.Number,
            'object': collections.Mapping,
            'array': list,
            'boolean': bool,
            'null': type(None)
        }
        return lambda node: isinstance(node.value, type_map[type_])

    def key_production(self, key):
        assert key

        def validate(node):
            if not node.parent_key:
                return False
            return node.parent_key == key
        return validate

    def pclass_production(self, pclass):

        if pclass == 'first-child':
            return lambda node: node.idx == 1
        elif pclass == 'last-child':
            return lambda node: \
                node.siblings and node.idx == node.siblings
        elif pclass == 'only-child':
            return lambda node: node.siblings == 1
        elif pclass == 'root':
            return lambda node: not node.parent
        elif pclass == 'empty':
            return lambda node: isinstance(node.value, list) and not len(node.value)
        else:
            raise Exception("unrecognized pclass %s" % pclass)

    def pclass_func_production(self, pclass, tokens):
        if pclass == 'has':
            # T:has(S)
            # A node of type T which has a child node satisfying the selector S
            rvals = self.selector_production(tokens)

    def parse_pclass_func_args(self, tokens):
        """Parse arguments to a psuedoclass function.

        Raises SelectorSyntaxError if bad arguments found.

        TODO: trash this function (?)
        """
        expected = ['int', 'binop', 'float', 'var', 'keyword', 'operator']
        args = []

        if self.peek(tokens, 'operator') == '(':
            args.append(tokens.pop(0))
        else:
            raise SelectorSyntaxError()

        while tokens:
            if self.peek(tokens, 'operator') == '(':
                args.extend(self.parse_pclass_func_args(tokens))
            elif self.peek(tokens, 'operator') == ')':
                break
            # TODO: operators should maybe be parsed seperately.
            # Wouldn't expect to see operator tokens here.
            elif self.peek(tokens, expected):
                args.append(tokens.pop(0))
                continue
            else:
                raise SelectorSyntaxError()
        else:
            raise SelectorSyntaxError()

        if self.peek(tokens, 'operator') == ')':
            args.append(tokens.pop(0))
        else:
            raise SelectorSyntaxError()

        return args

    def nth_child_production(self, lexeme, tokens):
        """Parse args and pass them to pclass_func_validator."""

        args = self.parse_pclass_func_args(tokens)
        # TODO: compile this into a global
        x = ''.join([str(t[1]) for t in args])
        nth_pat = r"^\s*\(\s*(?:([+\-]?)([0-9]*)n\s*(?:([+\-])\s*([0-9]))?|(odd|even)|([+\-]?[0-9]+))\s*\)"
        pat = re.match(nth_pat, x)

        if pat.group(5):
            a = 2
            b = 1 if pat.group(5) == 'odd' else 0
        elif pat.group(6):
            a = 0
            b = int(pat.group(6))
        else:
            sign = pat.group(1) if pat.group(1) else '+'
            coef = pat.group(2) if pat.group(2) else '1'
            a = eval(sign + coef)
            b = eval(pat.group(3) + pat.group(4)) if pat.group(3) else 0

        reverse = False
        if lexeme == 'nth-last-child':
            reverse = True

        def validate(node):
            """This crazy function taken from jsonselect.js:444."""

            if not node.siblings:
                return False

            idx = node.idx - 1
            tot = node.siblings

            if reverse:
                idx = tot - idx
            else:
                idx += 1

            if a == 0:
                m = b == idx
            else:
                mod = (idx - b) % a
                m = not mod and (idx * a + b) >= 0
            return m

        return validate

    def _match_nodes(self, validators, obj):
        """Apply each validator in validators to each node in obj.

        Return each node in obj which matches all validators.
        """

        results = []
        for node in object_iter(obj):
            if all([validate(node) for validate in validators]):
                results.append(node)
        return results

    @staticmethod
    def match(tokens, type_):
        if not Parser.peek(tokens, type_):
            raise Exception('match not successful')

        token = tokens.pop(0)
        return token[1]

    @staticmethod
    def peek(tokens, type_):
        if not tokens:
            return False

        if isinstance(type_, list) and tokens[0][0] in type_:
            return tokens[0][1]
        elif tokens[0][0] == type_:
            return tokens[0][1]
        else:
            return False

def select(selector, obj):
    """Appy selector to obj and return matching nodes.

    If only one node is found, return it, otherwise return a list of matches.
    Returns False on syntax error. None if no results found.
    """

    parser = Parser(obj)
    try:
        return parser.parse(lex(selector))
    except SelectorSyntaxError:
        return False
<|endoftext|>


<commit_before>from argparse import ArgumentParser
import requests

from parse_this import create_parser, Self

class OutpanException(Exception):
    """Exception raised when a requests fails."""

def _check_request_status(response):
    """Help method to check if an error occured.

    Args:
        response -- API server response
    """
    if not response.content:
        # Some method return an empty string we just return None
        return None
    try:
        data = response.json()
    except ValueError:
        # We couldn't decode a json object we just return the
        # response object
        return response
    else:
        # We raise if an error occurred, if not we return the json data
        if "error" in data:
            raise OutpanException("%(message)s - code: %(code)s"
                                  % data["error"])
        return data

class OutpanApi(object):
    """Access the Outpan API.

    Usage:
        from outpan import OutpanAPI
        api = OutpanAPI(<you_api_key>)
        api.get_product("0078915030900")
    """

    _API_URL = "http://www.outpan.com/api"

    def __init__(self, api_key=None):
        """
        Args:
            api_key -- the api key provided by outpan when you register
        """
        self._api_key = api_key

    def set_api_key(self, api_key):
        """
        Args:
            api_key -- the api key provided by outpan when you register
        """
        self._api_key = api_key

    def _get_params(self, params):
        """Returns the given parameters extended with the api key parameter.

        Args:
            params -- parameters of the request you want to extend
        """
        params.update({"apikey": self._api_key})
        return params

    def _get_url(self, resource):
        """Return the full URL for the given resource

        Args:
            resource -- path to the resource you want to query
        """
        return "%s/%s" % (self.__class__._API_URL, resource)

    @create_parser(Self, str, params_delim="--")
    def get_product(self, barcode):
        """Returns the product data specified by the barcode.

        Args:
            barcode -- the barcode you are looking for
        Returns:
            A dictionary containing the product data. It could be mostly empty
            if outpan doesn't have data for the product, it is up to the caller
            to verify the data
        """
        params = self._get_params({"barcode": barcode})
        full_url = self._get_url("get-product.php")
        response = requests.get(full_url, params=params)
        return _check_request_status(response)

    @create_parser(Self, str, str, params_delim="--")
    def add_edit_product_name(self, barcode, name):
        """Add or Edit the name of the product specify by the barcode.

        Args:
            barcode -- the barcode of the product you want to add/edit
            name -- name of the product you want to add/edit
        Note:
            Whether the barcode has already a name associateed with it or not,
            it will be replaced with the new name submitted by the API request.
            If the barcode exists the name will be replaced.
        Returns:
            Because the content of the request is empty the decorator used on
            this method will return None
        """
        params = self._get_params({"barcode": barcode, "name": name})
        full_url = self._get_url("edit-name.php")
        response = requests.get(full_url, params=params)
        return _check_request_status(response)

    @create_parser(Self, str, str, str, params_delim="--")
    def add_edit_product_attribute(self, barcode, attr_name, attr_value):
        """Add or edit an attribute to the product defined by the barcode.

        Args:
            barcode -- barcode of the object you want to edit
            attr_name -- name of the attribute to add/edit
            attr_value -- value of the attribute to add/edit
        Note:
            If the attribute already exists its value will be replaced
        Returns:
            Because the content of the request is empty the decorated used on
            this method will return None
        """
        params = self._get_params({"barcode": barcode, "attr_name": attr_name,
                                   "attr_value": attr_value})
        full_url = self._get_url("edit-attr.php")
        response = requests.get(full_url, params=params)
        return _check_request_status(response)

if __name__ == "__main__":
    outpan_parser = ArgumentParser(description="Allows you to access the Outpan.com API.")
    outpan_parser.add_argument("api_key", help="API key provided on your profile page.")
    sub_parsers = outpan_parser.add_subparsers(title="Outpan API methods",
                                               description="Methods allowed by the Outpan API",
                                               dest="method")
    get_product_parser = OutpanApi.get_product.parser
    sub_parsers.add_parser("get-product", parents=[get_product_parser],
                           add_help=False,
                           description=get_product_parser.description)
    add_edit_name_parser = OutpanApi.add_edit_product_name.parser
    sub_parsers.add_parser("add-edit-name",
                           parents=[add_edit_name_parser],
                           add_help=False,
                           description=add_edit_name_parser.description)
    add_edit_attr_parser = OutpanApi.add_edit_product_attribute.parser
    sub_parsers.add_parser("add-edit-attr",
                           parents=[add_edit_attr_parser],
                           add_help=False,
                           description=add_edit_attr_parser.description)
    namespace = outpan_parser.parse_args()
    outpan_api = OutpanApi(namespace.api_key)
    if namespace.method == "get-product":
        print(outpan_api.get_product(namespace.barcode))
    if namespace.method == "add-edit-name":
        print(outpan_api.add_edit_product_name(namespace.barcode,
                                               namespace.name))
    if namespace.method == "add-edit-attr":
        print(outpan_api.add_edit_product_attribute(namespace.barcode,
                                                    namespace.attr_name,
                                                    namespace.attr_value))
<commit_msg>Adapt class to new features found in parse_this package<commit_after>from argparse import ArgumentParser
import requests

from parse_this import create_parser, Self, parse_class

class OutpanException(Exception):
    """Exception raised when a requests fails."""

def _check_request_status(response):
    """Help method to check if an error occured.

    Args:
        response -- API server response
    """
    if not response.content:
        # Some method return an empty string we just return None
        return None
    try:
        data = response.json()
    except ValueError:
        # We couldn't decode a json object we just return the
        # response object
        return response
    else:
        # We raise if an error occurred, if not we return the json data
        if "error" in data:
            raise OutpanException("%(message)s - code: %(code)s"
                                  % data["error"])
        return data

@parse_class
class OutpanApi(object):
    """Access the Outpan API.

    Usage:
        from outpan import OutpanAPI
        api = OutpanAPI(<you_api_key>)
        api.get_product("0078915030900")
    """

    _API_URL = "http://www.outpan.com/api"

    @create_parser(Self, str)
    def __init__(self, api_key):
        """
        Args:
            api_key -- the api key provided by outpan when you register
        """
        self._api_key = api_key

    def _get_params(self, params):
        """Returns the given parameters extended with the api key parameter.

        Args:
            params -- parameters of the request you want to extend
        """
        params.update({"apikey": self._api_key})
        return params

    def _get_url(self, resource):
        """Return the full URL for the given resource

        Args:
            resource -- path to the resource you want to query
        """
        return "%s/%s" % (self.__class__._API_URL, resource)

    @create_parser(Self, str, params_delim="--")
    def get_product(self, barcode):
        """Returns the product data specified by the barcode.

        Args:
            barcode -- the barcode you are looking for

        Returns:
            A dictionary containing the product data. It could be mostly empty
            if outpan doesn't have data for the product, it is up to the caller
            to verify the data
        """
        params = self._get_params({"barcode": barcode})
        full_url = self._get_url("get-product.php")
        response = requests.get(full_url, params=params)
        return _check_request_status(response)

    @create_parser(Self, str, str, params_delim="--")
    def add_edit_product_name(self, barcode, name):
        """Add or Edit the name of the product specify by the barcode.

        Args:
            barcode -- the barcode of the product you want to add/edit
            name -- name of the product you want to add/edit

        Note:
            Whether the barcode has already a name associateed with it or not,
            it will be replaced with the new name submitted by the API request.
            If the barcode exists the name will be replaced.

        Returns:
            Because the content of the request is empty the decorator used on
            this method will return None
        """
        params = self._get_params({"barcode": barcode, "name": name})
        full_url = self._get_url("edit-name.php")
        response = requests.get(full_url, params=params)
        return _check_request_status(response)

    @create_parser(Self, str, str, str, params_delim="--")
    def add_edit_product_attribute(self, barcode, attr_name, attr_value):
        """Add or edit an attribute to the product defined by the barcode.

        Args:
            barcode -- barcode of the object you want to edit
            attr_name -- name of the attribute to add/edit
            attr_value -- value of the attribute to add/edit

        Note:
            If the attribute already exists its value will be replaced

        Returns:
            Because the content of the request is empty the decorated used on
            this method will return None
        """
        params = self._get_params({"barcode": barcode, "attr_name": attr_name,
                                   "attr_value": attr_value})
        full_url = self._get_url("edit-attr.php")
        response = requests.get(full_url, params=params)
        return _check_request_status(response)

if __name__ == "__main__":
    namespace = OutpanApi.parser.parse_args()
    outpan_api = OutpanApi(namespace.api_key)
    if namespace.method == "get-product":
        print(outpan_api.get_product(namespace.barcode))
    if namespace.method == "add-edit-product-name":
        print(outpan_api.add_edit_product_name(namespace.barcode,
                                               namespace.name))
    if namespace.method == "add-edit-product-attribute":
        print(outpan_api.add_edit_product_attribute(namespace.barcode,
                                                    namespace.attr_name,
                                                    namespace.attr_value))
<|endoftext|>


<commit_before>import msgpackrpc
from types import *

class InterfaceMismatch(Exception):
    pass
class TypeMismatch(InterfaceMismatch):
    pass
class UnknownMethod(InterfaceMismatch):
    pass

def error_handler(e):
    if e == 1:
        raise UnknownMethod()
    elif e == 2:
        # TODO(unno) we cannot get which arugment is illegal
        raise TypeMismatch()
    else:
        raise msgpackrpc.error.RPCError(e)

class Client(object):
    def __init__(self, client, name):
        self.client = client
        self.name = name

    def call(self, method, args, ret_type, args_type):
        if len(args) != len(args_type):
            # This error does not occurr if a client code is correctly generated
            message = "\"%s\" takes %d argument, but %d given" \
                % (method, len(args_type), len(args))
            raise TypeError(message)

        values = [self.name]
        for (v, t) in zip(args, args_type):
            values.append(t.to_msgpack(v))

        future = self.client.call_async(method, *values)
        future.attach_error_handler(error_handler)
        ret = future.get()

        if ret_type != None:
            return ret_type.from_msgpack(ret)

class ClientBase(object):
    def __init__(self, host, port, name, timeout=10):
        address = msgpackrpc.Address(host, port)
        self.client = msgpackrpc.Client(address, timeout=timeout)
        self.jubatus_client = Client(self.client, name)

    def get_client(self):
        return self.client

    def save(self, id):
        return self.jubatus_client.call("save", [id], TBool(), [TString()])

    def load(self, id):
      return self.jubatus_client.call("load", [id], TBool(), [TString()])

    def get_config(self):
        return self.jubatus_client.call("get_config", [], TString(), [])

    def get_status(self):
        return self.jubatus_client.call(
            "get_status",
            [],
            TMap(TString(), TMap(TString(), TString())),
            [])

    def get_proxy_status(self):
        return self.jubatus_client.call(
            "get_proxy_status",
            [],
            TMap(TString(), TMap(TString(), TString())),
            []),
<commit_msg>Fixed typo<commit_after>import msgpackrpc
from types import *

class InterfaceMismatch(Exception):
    pass
class TypeMismatch(InterfaceMismatch):
    pass
class UnknownMethod(InterfaceMismatch):
    pass

def error_handler(e):
    if e == 1:
        raise UnknownMethod()
    elif e == 2:
        # TODO(unno) we cannot get which arugment is illegal
        raise TypeMismatch()
    else:
        raise msgpackrpc.error.RPCError(e)

class Client(object):
    def __init__(self, client, name):
        self.client = client
        self.name = name

    def call(self, method, args, ret_type, args_type):
        if len(args) != len(args_type):
            # This error does not occurr if a client code is correctly generated
            message = "\"%s\" takes %d argument, but %d given" \
                % (method, len(args_type), len(args))
            raise TypeError(message)

        values = [self.name]
        for (v, t) in zip(args, args_type):
            values.append(t.to_msgpack(v))

        future = self.client.call_async(method, *values)
        future.attach_error_handler(error_handler)
        ret = future.get()

        if ret_type != None:
            return ret_type.from_msgpack(ret)

class ClientBase(object):
    def __init__(self, host, port, name, timeout=10):
        address = msgpackrpc.Address(host, port)
        self.client = msgpackrpc.Client(address, timeout=timeout)
        self.jubatus_client = Client(self.client, name)

    def get_client(self):
        return self.client

    def save(self, id):
        return self.jubatus_client.call("save", [id], TBool(), [TString()])

    def load(self, id):
      return self.jubatus_client.call("load", [id], TBool(), [TString()])

    def get_config(self):
        return self.jubatus_client.call("get_config", [], TString(), [])

    def get_status(self):
        return self.jubatus_client.call(
            "get_status",
            [],
            TMap(TString(), TMap(TString(), TString())),
            [])

    def get_proxy_status(self):
        return self.jubatus_client.call(
            "get_proxy_status",
            [],
            TMap(TString(), TMap(TString(), TString())),
            [])
<|endoftext|>


<commit_before>from __future__ import print_function
import base64
import requests
from pprint import pprint

from parse_this import create_parser, Self, parse_class

class OutpanException(Exception):
    """Exception raised when a requests fails."""

def _check_request_status(response):
    """Help method to check if an error occured.

    Args:
        response -- API server response
    """
    if not response.content:
        # Some method return an empty string we just return None
        return None
    try:
        data = response.json()
    except ValueError:
        # We couldn't decode a json object we just return the
        # response object
        return response
    else:
        # We raise if an error occurred, if not we return the json data
        if "error" in data:
            raise OutpanException("%(message)s - code: %(code)s"
                                  % data["error"])
        return data

@parse_class(description="Simply access the outpan.com API with your api key.")
class OutpanApi(object):
    """Access outpan.com v1 API with your api key."""

    _API_URL = "https://api.outpan.com/v1/products/"

    @create_parser(Self, str, delimiter_chars="--")
    def __init__(self, api_key):
        """
        Args:
            api_key -- the api key provided by outpan when you register
        """
        encoded_key = base64.encodestring(bytearray("%s:" % api_key, "utf-8"))
        self._auth_header = {"Authorization": "Basic %s"
                             % encoded_key.decode("ascii")}

    def _get_resource(self, resource_path):
        """Send a GET request to a specific path.

        Args:
            resource_path -- the API path to query
        """
        response = requests.get(self._API_URL + "/" + resource_path,
                                headers=self._auth_header)
        return _check_request_status(response)

    @create_parser(Self, str, delimiter_chars="--")
    def get_product(self, barcode):
        """Returns the name, attributes, images and videos of the product
        identified by the barcode.

        Args:
            barcode -- the barcode/GTIN of the product
        """
        return self._get_resource(barcode)

    @create_parser(Self, str, delimiter_chars="--")
    def name(self, barcode):
        """Returns a dict containing the barcode and name of the product.

        Args:
            barcode -- the barcode of the product
        """
        return self._get_resource("%s/name" % barcode)

    @create_parser(Self, str, delimiter_chars="--")
    def attributes(self, barcode):
        """Returns a dict containing the barcode and attributes of the product.

        Args:
            barcode -- the barcode of the product
        """
        return self._get_resource("%s/attributes" % barcode)

    @create_parser(Self, str, delimiter_chars="--")
    def images(self, barcode):
        """Returns a dict containing the barcode and image links of the product.

        Args:
            barcode -- the barcode of the product
        """
        return self._get_resource("%s/images" % barcode)

    @create_parser(Self, str, delimiter_chars="--")
    def videos(self, barcode):
        """Returns a dict containing the barcode and videos links of the
        product.

        Args:
            barcode -- the barcode of the product
        """
        return self._get_resource("%s/videos" % barcode)

def run_cli():
    result = OutpanApi.parser.call()
    pprint(result if result else "SUCCESS")

if __name__ == "__main__":
    RESULT = OutpanApi.parser.call()
    pprint(RESULT if RESULT else "SUCCESS")
<commit_msg>Rename class to V1 since V2 will be introduced<commit_after>from __future__ import print_function
import base64
import requests
from pprint import pprint

from parse_this import create_parser, Self, parse_class

class OutpanException(Exception):
    """Exception raised when a requests fails."""

def _check_request_status(response):
    """Help method to check if an error occured.

    Args:
        response -- API server response
    """
    if not response.content:
        # Some method return an empty string we just return None
        return None
    try:
        data = response.json()
    except ValueError:
        # We couldn't decode a json object we just return the
        # response object
        return response
    else:
        # We raise if an error occurred, if not we return the json data
        if "error" in data:
            raise OutpanException("%(message)s - code: %(code)s"
                                  % data["error"])
        return data

@parse_class(description="Simply access the outpan.com API with your api key.")
class OutpanApiV1(object):
    """Access outpan.com v1 API with your api key."""

    _API_URL = "https://api.outpan.com/v1/products/"

    @create_parser(Self, str, delimiter_chars="--")
    def __init__(self, api_key):
        """
        Args:
            api_key -- the api key provided by outpan when you register
        """
        encoded_key = base64.encodestring(bytearray("%s:" % api_key, "utf-8"))
        self._auth_header = {"Authorization": "Basic %s"
                             % encoded_key.decode("ascii")}

    def _get_resource(self, resource_path):
        """Send a GET request to a specific path.

        Args:
            resource_path -- the API path to query
        """
        response = requests.get(self._API_URL + "/" + resource_path,
                                headers=self._auth_header)
        return _check_request_status(response)

    @create_parser(Self, str, delimiter_chars="--")
    def get_product(self, barcode):
        """Returns the name, attributes, images and videos of the product
        identified by the barcode.

        Args:
            barcode -- the barcode/GTIN of the product
        """
        return self._get_resource(barcode)

    @create_parser(Self, str, delimiter_chars="--")
    def name(self, barcode):
        """Returns a dict containing the barcode and name of the product.

        Args:
            barcode -- the barcode of the product
        """
        return self._get_resource("%s/name" % barcode)

    @create_parser(Self, str, delimiter_chars="--")
    def attributes(self, barcode):
        """Returns a dict containing the barcode and attributes of the product.

        Args:
            barcode -- the barcode of the product
        """
        return self._get_resource("%s/attributes" % barcode)

    @create_parser(Self, str, delimiter_chars="--")
    def images(self, barcode):
        """Returns a dict containing the barcode and image links of the product.

        Args:
            barcode -- the barcode of the product
        """
        return self._get_resource("%s/images" % barcode)

    @create_parser(Self, str, delimiter_chars="--")
    def videos(self, barcode):
        """Returns a dict containing the barcode and videos links of the
        product.

        Args:
            barcode -- the barcode of the product
        """
        return self._get_resource("%s/videos" % barcode)

def run_cli():
    result = OutpanApiV1.parser.call()
    pprint(result if result else "SUCCESS")

if __name__ == "__main__":
    RESULT = OutpanApiV1.parser.call()
    pprint(RESULT if RESULT else "SUCCESS")
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""
Select2 Widgets based on https://github.com/applegrew/django-select2.

These components are responsible for rendering
the necessary HTML data markups. Since this whole
package is to render choices using Select2 JavaScript
library, hence these components are meant to be used
with choice fields.

Widgets are generally of two types:

    1. **Light** --
    They are not meant to be used when there
    are too many options, say, in thousands.
    This is because all those options would
    have to be pre-rendered onto the page
    and JavaScript would be used to search
    through them. Said that, they are also one
    the most easiest to use. They are a
    drop-in-replacement for Django's default
    select widgets.

    2. **Heavy** --
    They are suited for scenarios when the number of options
    are large and need complex queries (from maybe different
    sources) to get the options.
    This dynamic fetching of options undoubtedly requires
    Ajax communication with the server. Django-Select2 includes
    a helper JS file which is included automatically,
    so you need not worry about writing any Ajax related JS code.
    Although on the server side you do need to create a view
    specifically to respond to the queries.

Heavy widgets have the word 'Heavy' in their name.
Light widgets are normally named, i.e. there is no
'Light' word in their names.

"""
from __future__ import absolute_import, unicode_literals

from copy import copy
from itertools import chain

from django import forms
from django.conf import settings
from django.core import signing
from django.core.urlresolvers import reverse_lazy
from django.forms.models import ModelChoiceIterator
from django.utils.encoding import force_text

DEFAULT_SELECT2_JS = '//cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.min.js'
DEFAULT_SELECT2_CSS = '//cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.min.css'

__all__ = ['Select2Widget', 'Select2MultipleWidget', 'Select2TagWidget',
           'HeavySelect2Widget', 'HeavySelect2MultipleWidget', 'HeavySelect2TagWidget']

class Select2Mixin(object):
    """
    The base mixin of all Select2 widgets.

    This mixin is responsible for rendering the necessary
    data attributes for select2 as well as adding the static
    form media.
    """

    def build_attrs(self, base_attrs, extra_attrs=None):
        """Add select2 data attributes."""
        attrs = super(Select2Mixin, self).build_attrs(base_attrs, extra_attrs)
        if self.is_required:
            attrs.setdefault('data-allow-clear', 'false')
        else:
            attrs.setdefault('data-allow-clear', 'true')
            attrs.setdefault('data-placeholder', '')

        attrs.setdefault('data-minimum-input-length', 0)
        if 'class' in attrs:
            attrs['class'] += ' django-select2'
        else:
            attrs['class'] = 'django-select2'
        return attrs

    def render_options(self, *args, **kwargs):
        """Render options including an empty one, if the field is not required."""
        output = '<option></option>' if not self.is_required else ''
        output += super(Select2Mixin, self).render_options(*args, **kwargs)
        return output

    def _get_media(self):
        """
        Construct Media as a dynamic property.

        .. Note:: For more information visit
            https://docs.djangoproject.com/en/1.8/topics/forms/media/#media-as-a-dynamic-property
        """
        return forms.Media(
            js=(getattr(settings, 'SELECT2_JS_URL', DEFAULT_SELECT2_JS),
                'django_select2.js'),
            css={'screen': (getattr(settings, 'SELECT2_CSS_URL', DEFAULT_SELECT2_CSS),)}
        )

    media = property(_get_media)

class Select2TagMixin(object):
    """Mixin to add select2 tag functionality."""

    def build_attrs(self, base_attrs, extra_attrs=None):
        """Add select2's tag attributes."""
        extra_attrs = extra_attrs or {}
        extra_attrs.setdefault('data-minimum-input-length', 1)
        extra_attrs.setdefault('data-tags', 'true')
        extra_attrs.setdefault('data-token-separators', [",", " "])
        return super(Select2TagMixin, self).build_attrs(base_attrs, extra_attrs)

class Select2Widget(Select2Mixin, forms.Select):
    """
    Select2 drop in widget.

    Example usage::

        class MyModelForm(forms.ModelForm):
            class Meta:
                model = MyModel
                fields = ('my_field', )
                widgets = {
                    'my_field': Select2Widget
                }

    or::

        class MyForm(forms.Form):
            my_choice = forms.ChoiceField(widget=Select2Widget)

    """

    pass

class Select2MultipleWidget(Select2Mixin, forms.SelectMultiple):
    """
    Select2 drop in widget for multiple select.

    Works just like :class:`.Select2Widget` but for multi select.
    """

    pass

class Select2TagWidget(Select2TagMixin, Select2Mixin, forms.SelectMultiple):
    """
    Select2 drop in widget for for tagging.

    Example for :class:`.django.contrib.postgres.fields.ArrayField`::

        class MyWidget(Select2TagWidget):

            def value_from_datadict(self, data, files, name):
                values = super(MyWidget, self).value_from_datadict(data, files, name):
                return ",".join(values)

    """

    pass

class HeavySelect2Mixin(Select2Mixin):
    """Mixin that adds select2's ajax options."""

    def __init__(self, attrs=None, choices=(), **kwargs):
        self.choices = choices
        if attrs is not None:
            self.attrs = attrs.copy()
        else:
            self.attrs = {}

        self.data_view = kwargs.pop('data_view', None)
        self.data_url = kwargs.pop('data_url', None)

        if not (self.data_view or self.data_url):
            raise ValueError('You must ether specify "data_view" or "data_url".')

    def get_url(self):
        """Return url from instance or by reversing :attr:`.data_view`."""
        if self.data_url:
            return self.data_url
        return reverse_lazy(self.data_view)

    def build_attrs(self, base_attrs, extra_attrs=None):
        """Set select2's ajax attributes."""
        attrs = super(HeavySelect2Mixin, self).build_attrs(base_attrs, extra_attrs)

        # encrypt instance Id
        self.widget_id = signing.dumps(id(self))

        attrs['data-field_id'] = self.widget_id
        attrs.setdefault('data-ajax--url', self.get_url())
        attrs.setdefault('data-ajax--cache', "true")
        attrs.setdefault('data-ajax--type', "GET")
        attrs.setdefault('data-minimum-input-length', 2)

        attrs['class'] += ' django-select2-heavy'
        return attrs

    def format_value(self, value):
        result = super(HeavySelect2Mixin, self).format_value(value)
        chosen = copy(self.choices)
        chosen.queryset = chosen.queryset.filter(pk__in=[
            int(i) for i in result if isinstance(i, (int, long)) or i.isdigit()
        ])
        self.choices = set(chosen)
        return result

class HeavySelect2Widget(HeavySelect2Mixin, forms.Select):
    """
    Select2 widget with AJAX support.

    Usage example::

        class MyWidget(HeavySelectWidget):
            data_view = 'my_view_name'

    or::

        class MyForm(forms.Form):
            my_field = forms.ChoicesField(
                widget=HeavySelectWidget(
                    data_url='/url/to/json/response'
                )
            )

    """

    pass

class HeavySelect2MultipleWidget(HeavySelect2Mixin, forms.SelectMultiple):
    """Select2 multi select widget similar to :class:`.HeavySelect2Widget`."""

    pass

class HeavySelect2TagWidget(Select2TagMixin, HeavySelect2MultipleWidget):
    """Select2 tag widget."""

    pass
<commit_msg>Don't break non-model uses; #588<commit_after># -*- coding: utf-8 -*-
"""
Select2 Widgets based on https://github.com/applegrew/django-select2.

These components are responsible for rendering
the necessary HTML data markups. Since this whole
package is to render choices using Select2 JavaScript
library, hence these components are meant to be used
with choice fields.

Widgets are generally of two types:

    1. **Light** --
    They are not meant to be used when there
    are too many options, say, in thousands.
    This is because all those options would
    have to be pre-rendered onto the page
    and JavaScript would be used to search
    through them. Said that, they are also one
    the most easiest to use. They are a
    drop-in-replacement for Django's default
    select widgets.

    2. **Heavy** --
    They are suited for scenarios when the number of options
    are large and need complex queries (from maybe different
    sources) to get the options.
    This dynamic fetching of options undoubtedly requires
    Ajax communication with the server. Django-Select2 includes
    a helper JS file which is included automatically,
    so you need not worry about writing any Ajax related JS code.
    Although on the server side you do need to create a view
    specifically to respond to the queries.

Heavy widgets have the word 'Heavy' in their name.
Light widgets are normally named, i.e. there is no
'Light' word in their names.

"""
from __future__ import absolute_import, unicode_literals

from copy import copy
from itertools import chain

from django import forms
from django.conf import settings
from django.core import signing
from django.core.urlresolvers import reverse_lazy
from django.forms.models import ModelChoiceIterator
from django.utils.encoding import force_text

DEFAULT_SELECT2_JS = '//cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.min.js'
DEFAULT_SELECT2_CSS = '//cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.min.css'

__all__ = ['Select2Widget', 'Select2MultipleWidget', 'Select2TagWidget',
           'HeavySelect2Widget', 'HeavySelect2MultipleWidget', 'HeavySelect2TagWidget']

class Select2Mixin(object):
    """
    The base mixin of all Select2 widgets.

    This mixin is responsible for rendering the necessary
    data attributes for select2 as well as adding the static
    form media.
    """

    def build_attrs(self, base_attrs, extra_attrs=None):
        """Add select2 data attributes."""
        attrs = super(Select2Mixin, self).build_attrs(base_attrs, extra_attrs)
        if self.is_required:
            attrs.setdefault('data-allow-clear', 'false')
        else:
            attrs.setdefault('data-allow-clear', 'true')
            attrs.setdefault('data-placeholder', '')

        attrs.setdefault('data-minimum-input-length', 0)
        if 'class' in attrs:
            attrs['class'] += ' django-select2'
        else:
            attrs['class'] = 'django-select2'
        return attrs

    def render_options(self, *args, **kwargs):
        """Render options including an empty one, if the field is not required."""
        output = '<option></option>' if not self.is_required else ''
        output += super(Select2Mixin, self).render_options(*args, **kwargs)
        return output

    def _get_media(self):
        """
        Construct Media as a dynamic property.

        .. Note:: For more information visit
            https://docs.djangoproject.com/en/1.8/topics/forms/media/#media-as-a-dynamic-property
        """
        return forms.Media(
            js=(getattr(settings, 'SELECT2_JS_URL', DEFAULT_SELECT2_JS),
                'django_select2.js'),
            css={'screen': (getattr(settings, 'SELECT2_CSS_URL', DEFAULT_SELECT2_CSS),)}
        )

    media = property(_get_media)

class Select2TagMixin(object):
    """Mixin to add select2 tag functionality."""

    def build_attrs(self, base_attrs, extra_attrs=None):
        """Add select2's tag attributes."""
        extra_attrs = extra_attrs or {}
        extra_attrs.setdefault('data-minimum-input-length', 1)
        extra_attrs.setdefault('data-tags', 'true')
        extra_attrs.setdefault('data-token-separators', [",", " "])
        return super(Select2TagMixin, self).build_attrs(base_attrs, extra_attrs)

class Select2Widget(Select2Mixin, forms.Select):
    """
    Select2 drop in widget.

    Example usage::

        class MyModelForm(forms.ModelForm):
            class Meta:
                model = MyModel
                fields = ('my_field', )
                widgets = {
                    'my_field': Select2Widget
                }

    or::

        class MyForm(forms.Form):
            my_choice = forms.ChoiceField(widget=Select2Widget)

    """

    pass

class Select2MultipleWidget(Select2Mixin, forms.SelectMultiple):
    """
    Select2 drop in widget for multiple select.

    Works just like :class:`.Select2Widget` but for multi select.
    """

    pass

class Select2TagWidget(Select2TagMixin, Select2Mixin, forms.SelectMultiple):
    """
    Select2 drop in widget for for tagging.

    Example for :class:`.django.contrib.postgres.fields.ArrayField`::

        class MyWidget(Select2TagWidget):

            def value_from_datadict(self, data, files, name):
                values = super(MyWidget, self).value_from_datadict(data, files, name):
                return ",".join(values)

    """

    pass

class HeavySelect2Mixin(Select2Mixin):
    """Mixin that adds select2's ajax options."""

    def __init__(self, attrs=None, choices=(), **kwargs):
        self.choices = choices
        if attrs is not None:
            self.attrs = attrs.copy()
        else:
            self.attrs = {}

        self.data_view = kwargs.pop('data_view', None)
        self.data_url = kwargs.pop('data_url', None)

        if not (self.data_view or self.data_url):
            raise ValueError('You must ether specify "data_view" or "data_url".')

    def get_url(self):
        """Return url from instance or by reversing :attr:`.data_view`."""
        if self.data_url:
            return self.data_url
        return reverse_lazy(self.data_view)

    def build_attrs(self, base_attrs, extra_attrs=None):
        """Set select2's ajax attributes."""
        attrs = super(HeavySelect2Mixin, self).build_attrs(base_attrs, extra_attrs)

        # encrypt instance Id
        self.widget_id = signing.dumps(id(self))

        attrs['data-field_id'] = self.widget_id
        attrs.setdefault('data-ajax--url', self.get_url())
        attrs.setdefault('data-ajax--cache', "true")
        attrs.setdefault('data-ajax--type', "GET")
        attrs.setdefault('data-minimum-input-length', 2)

        attrs['class'] += ' django-select2-heavy'
        return attrs

    def format_value(self, value):
        result = super(HeavySelect2Mixin, self).format_value(value)
        if isinstance(self.choices, ModelChoiceIterator):
            chosen = copy(self.choices)
            chosen.queryset = chosen.queryset.filter(pk__in=[
                int(i) for i in result if isinstance(i, (int, long)) or i.isdigit()
            ])
            self.choices = set(chosen)
        return result

class HeavySelect2Widget(HeavySelect2Mixin, forms.Select):
    """
    Select2 widget with AJAX support.

    Usage example::

        class MyWidget(HeavySelectWidget):
            data_view = 'my_view_name'

    or::

        class MyForm(forms.Form):
            my_field = forms.ChoicesField(
                widget=HeavySelectWidget(
                    data_url='/url/to/json/response'
                )
            )

    """

    pass

class HeavySelect2MultipleWidget(HeavySelect2Mixin, forms.SelectMultiple):
    """Select2 multi select widget similar to :class:`.HeavySelect2Widget`."""

    pass

class HeavySelect2TagWidget(Select2TagMixin, HeavySelect2MultipleWidget):
    """Select2 tag widget."""

    pass
<|endoftext|>


<commit_before># Copyright 2018 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""This script is used to synthesize generated parts of this library."""
from pathlib import Path
from typing import List, Optional

import synthtool as s
from synthtool import gcp

common = gcp.CommonTemplates()

# This library ships clients for 3 different APIs,
# firestore, firestore_admin and firestore_bundle.
# firestore_bundle is not versioned
firestore_default_version = "v1"
firestore_admin_default_version = "v1"

# This is a customized version of the s.get_staging_dirs() function from synthtool to
# cater for copying 3 different folders from googleapis-gen
# which are firestore, firestore/admin and firestore/bundle.
# Source https://github.com/googleapis/synthtool/blob/master/synthtool/transforms.py#L280
def get_staging_dirs(
    default_version: Optional[str] = None, sub_directory: Optional[str] = None
) -> List[Path]:
    """Returns the list of directories, one per version, copied from
    https://github.com/googleapis/googleapis-gen. Will return in lexical sorting
    order with the exception of the default_version which will be last (if specified).

    Args:
      default_version (str): the default version of the API. The directory for this version
        will be the last item in the returned list if specified.
      sub_directory (str): if a `sub_directory` is provided, only the directories within the
        specified `sub_directory` will be returned.

    Returns: the empty list if no file were copied.
    """

    staging = Path("owl-bot-staging")

    if sub_directory:
        staging /= sub_directory

    if staging.is_dir():
        # Collect the subdirectories of the staging directory.
        versions = [v.name for v in staging.iterdir() if v.is_dir()]
        # Reorder the versions so the default version always comes last.
        versions = [v for v in versions if v != default_version]
        versions.sort()
        if default_version is not None:
            versions += [default_version]
        dirs = [staging / v for v in versions]
        for dir in dirs:
            s._tracked_paths.add(dir)
        return dirs
    else:
        return []

def update_fixup_scripts(library):
    # Add message for missing 'libcst' dependency
    s.replace(
        library / "scripts/fixup*.py",
        """import libcst as cst""",
        """try:
    import libcst as cst
except ImportError:
    raise ImportError('Run `python -m pip install "libcst >= 0.2.5"` to install libcst.')

    """,
    )

for library in get_staging_dirs(default_version=firestore_default_version, sub_directory="firestore"):
    s.move(library / f"google/cloud/firestore_{library.name}", excludes=[f"__init__.py"])
    s.move(library / f"tests/", f"tests")
    update_fixup_scripts(library)
    s.move(library / "scripts")

for library in get_staging_dirs(default_version=firestore_admin_default_version, sub_directory="firestore_admin"):
    s.move(library / f"google/cloud/firestore_admin_{library.name}", excludes=[f"__init__.py"])
    s.move(library / f"tests", f"tests")
    update_fixup_scripts(library)
    s.move(library / "scripts")

for library in get_staging_dirs(sub_directory="firestore_bundle"):
    s.replace(
        library / "google/cloud/bundle/types/bundle.py",
        "from google.firestore.v1 import document_pb2  # type: ignore\n"
        "from google.firestore.v1 import query_pb2  # type: ignore",
        "from google.cloud.firestore_v1.types import document as document_pb2  # type: ignore\n"
        "from google.cloud.firestore_v1.types import query as query_pb2 # type: ignore"
    )

    s.replace(
        library / "google/cloud/bundle/__init__.py",
        "from .types.bundle import BundleMetadata\n"
        "from .types.bundle import NamedQuery\n",
        "from .types.bundle import BundleMetadata\n"
        "from .types.bundle import NamedQuery\n"
        "\n"
        "from .bundle import FirestoreBundle\n",
    )

    s.replace(
        library / "google/cloud/bundle/__init__.py",
        "\'BundledQuery\',",
        "\"BundledQuery\",\n\"FirestoreBundle\",",
    )

    s.move(
        library / f"google/cloud/bundle",
        f"google/cloud/firestore_bundle",
    )
    s.move(library / f"tests", f"tests")

s.remove_staging_dirs()

# ----------------------------------------------------------------------------
# Add templated files
# ----------------------------------------------------------------------------
templated_files = common.py_library(
    samples=False,  # set to True only if there are samples
    system_test_python_versions=["3.7"],
    unit_test_external_dependencies=["aiounittest"],
    system_test_external_dependencies=["pytest-asyncio"],
    microgenerator=True,
    cov_level=100,
    split_system_tests=True,
)

s.move(templated_files)

s.replace(
    "noxfile.py",
    """\"--quiet\",
            f\"--junitxml=system_\{session.python\}_sponge_log.xml\",
            system_test""",
    """\"--verbose\",
            f\"--junitxml=system_{session.python}_sponge_log.xml\",
            system_test""",
)

# Add pytype support
s.replace(
    ".gitignore",
    """\
.pytest_cache
""",
    """\
.pytest_cache
.pytype
""",
)

s.replace(
    ".gitignore",
    """\
pylintrc
pylintrc.test
""",
    """\
pylintrc
pylintrc.test
.make/**
""",
)

s.replace(
    "setup.cfg",
    """\
universal = 1
""",
    """\
universal = 1
[pytype]
python_version = 3.8
inputs =
    google/cloud/
exclude =
    tests/
output = .pytype/
# Workaround for https://github.com/google/pytype/issues/150
disable = pyi-error
""",
)

s.replace(
    "noxfile.py",
    """\
BLACK_VERSION = "black==19.10b0"
""",
    """\
PYTYPE_VERSION = "pytype==2020.7.24"
BLACK_VERSION = "black==19.10b0"
""",
)

s.replace(
    "noxfile.py",
    """\
@nox.session\(python=DEFAULT_PYTHON_VERSION\)
def lint_setup_py\(session\):
""",
    '''\
@nox.session(python="3.7")
def pytype(session):
    """Run pytype
    """
    session.install(PYTYPE_VERSION)
    session.run("pytype",)
@nox.session(python=DEFAULT_PYTHON_VERSION)
def lint_setup_py(session):
''',
)

s.replace(
    ".coveragerc",
    """\
    raise NotImplementedError
omit =
""",
    """\
    raise NotImplementedError
    # Ignore setuptools-less fallback
    except pkg_resources.DistributionNotFound:
omit =
""",
)

s.shell.run(["nox", "-s", "blacken"], hide_output=False)

s.replace(
    ".kokoro/build.sh",
    "# Setup service account credentials.",
    """\
# Setup firestore account credentials
export FIRESTORE_APPLICATION_CREDENTIALS=${KOKORO_GFILE_DIR}/firebase-credentials.json

# Setup service account credentials.""",
)

# Add a section on updating conformance tests to contributing.
s.replace(
    "CONTRIBUTING.rst",
    "\nTest Coverage",
    """*************
Updating Conformance Tests
**************************

The firestore client libraries use a shared set of conformance tests, the source of which can be found at https://github.com/googleapis/conformance-tests.

To update the copy of these conformance tests used by this repository, run the provided Makefile:

   $ make -f Makefile_v1

*************
Test Coverage"""
)
<commit_msg>chore: generate python samples templates in owlbot.py (#427)<commit_after># Copyright 2018 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""This script is used to synthesize generated parts of this library."""
from pathlib import Path
from typing import List, Optional

import synthtool as s
from synthtool import gcp

common = gcp.CommonTemplates()

# This library ships clients for 3 different APIs,
# firestore, firestore_admin and firestore_bundle.
# firestore_bundle is not versioned
firestore_default_version = "v1"
firestore_admin_default_version = "v1"

# This is a customized version of the s.get_staging_dirs() function from synthtool to
# cater for copying 3 different folders from googleapis-gen
# which are firestore, firestore/admin and firestore/bundle.
# Source https://github.com/googleapis/synthtool/blob/master/synthtool/transforms.py#L280
def get_staging_dirs(
    default_version: Optional[str] = None, sub_directory: Optional[str] = None
) -> List[Path]:
    """Returns the list of directories, one per version, copied from
    https://github.com/googleapis/googleapis-gen. Will return in lexical sorting
    order with the exception of the default_version which will be last (if specified).

    Args:
      default_version (str): the default version of the API. The directory for this version
        will be the last item in the returned list if specified.
      sub_directory (str): if a `sub_directory` is provided, only the directories within the
        specified `sub_directory` will be returned.

    Returns: the empty list if no file were copied.
    """

    staging = Path("owl-bot-staging")

    if sub_directory:
        staging /= sub_directory

    if staging.is_dir():
        # Collect the subdirectories of the staging directory.
        versions = [v.name for v in staging.iterdir() if v.is_dir()]
        # Reorder the versions so the default version always comes last.
        versions = [v for v in versions if v != default_version]
        versions.sort()
        if default_version is not None:
            versions += [default_version]
        dirs = [staging / v for v in versions]
        for dir in dirs:
            s._tracked_paths.add(dir)
        return dirs
    else:
        return []

def update_fixup_scripts(library):
    # Add message for missing 'libcst' dependency
    s.replace(
        library / "scripts/fixup*.py",
        """import libcst as cst""",
        """try:
    import libcst as cst
except ImportError:
    raise ImportError('Run `python -m pip install "libcst >= 0.2.5"` to install libcst.')

    """,
    )

for library in get_staging_dirs(default_version=firestore_default_version, sub_directory="firestore"):
    s.move(library / f"google/cloud/firestore_{library.name}", excludes=[f"__init__.py"])
    s.move(library / f"tests/", f"tests")
    update_fixup_scripts(library)
    s.move(library / "scripts")

for library in get_staging_dirs(default_version=firestore_admin_default_version, sub_directory="firestore_admin"):
    s.move(library / f"google/cloud/firestore_admin_{library.name}", excludes=[f"__init__.py"])
    s.move(library / f"tests", f"tests")
    update_fixup_scripts(library)
    s.move(library / "scripts")

for library in get_staging_dirs(sub_directory="firestore_bundle"):
    s.replace(
        library / "google/cloud/bundle/types/bundle.py",
        "from google.firestore.v1 import document_pb2  # type: ignore\n"
        "from google.firestore.v1 import query_pb2  # type: ignore",
        "from google.cloud.firestore_v1.types import document as document_pb2  # type: ignore\n"
        "from google.cloud.firestore_v1.types import query as query_pb2 # type: ignore"
    )

    s.replace(
        library / "google/cloud/bundle/__init__.py",
        "from .types.bundle import BundleMetadata\n"
        "from .types.bundle import NamedQuery\n",
        "from .types.bundle import BundleMetadata\n"
        "from .types.bundle import NamedQuery\n"
        "\n"
        "from .bundle import FirestoreBundle\n",
    )

    s.replace(
        library / "google/cloud/bundle/__init__.py",
        "\'BundledQuery\',",
        "\"BundledQuery\",\n\"FirestoreBundle\",",
    )

    s.move(
        library / f"google/cloud/bundle",
        f"google/cloud/firestore_bundle",
    )
    s.move(library / f"tests", f"tests")

s.remove_staging_dirs()

# ----------------------------------------------------------------------------
# Add templated files
# ----------------------------------------------------------------------------
templated_files = common.py_library(
    samples=False,  # set to True only if there are samples
    system_test_python_versions=["3.7"],
    unit_test_external_dependencies=["aiounittest"],
    system_test_external_dependencies=["pytest-asyncio"],
    microgenerator=True,
    cov_level=100,
    split_system_tests=True,
)
python.py_samples(skip_readmes=True)

s.move(templated_files)

s.replace(
    "noxfile.py",
    """\"--quiet\",
            f\"--junitxml=system_\{session.python\}_sponge_log.xml\",
            system_test""",
    """\"--verbose\",
            f\"--junitxml=system_{session.python}_sponge_log.xml\",
            system_test""",
)

# Add pytype support
s.replace(
    ".gitignore",
    """\
.pytest_cache
""",
    """\
.pytest_cache
.pytype
""",
)

s.replace(
    ".gitignore",
    """\
pylintrc
pylintrc.test
""",
    """\
pylintrc
pylintrc.test
.make/**
""",
)

s.replace(
    "setup.cfg",
    """\
universal = 1
""",
    """\
universal = 1
[pytype]
python_version = 3.8
inputs =
    google/cloud/
exclude =
    tests/
output = .pytype/
# Workaround for https://github.com/google/pytype/issues/150
disable = pyi-error
""",
)

s.replace(
    "noxfile.py",
    """\
BLACK_VERSION = "black==19.10b0"
""",
    """\
PYTYPE_VERSION = "pytype==2020.7.24"
BLACK_VERSION = "black==19.10b0"
""",
)

s.replace(
    "noxfile.py",
    """\
@nox.session\(python=DEFAULT_PYTHON_VERSION\)
def lint_setup_py\(session\):
""",
    '''\
@nox.session(python="3.7")
def pytype(session):
    """Run pytype
    """
    session.install(PYTYPE_VERSION)
    session.run("pytype",)
@nox.session(python=DEFAULT_PYTHON_VERSION)
def lint_setup_py(session):
''',
)

s.replace(
    ".coveragerc",
    """\
    raise NotImplementedError
omit =
""",
    """\
    raise NotImplementedError
    # Ignore setuptools-less fallback
    except pkg_resources.DistributionNotFound:
omit =
""",
)

s.shell.run(["nox", "-s", "blacken"], hide_output=False)

s.replace(
    ".kokoro/build.sh",
    "# Setup service account credentials.",
    """\
# Setup firestore account credentials
export FIRESTORE_APPLICATION_CREDENTIALS=${KOKORO_GFILE_DIR}/firebase-credentials.json

# Setup service account credentials.""",
)

# Add a section on updating conformance tests to contributing.
s.replace(
    "CONTRIBUTING.rst",
    "\nTest Coverage",
    """*************
Updating Conformance Tests
**************************

The firestore client libraries use a shared set of conformance tests, the source of which can be found at https://github.com/googleapis/conformance-tests.

To update the copy of these conformance tests used by this repository, run the provided Makefile:

   $ make -f Makefile_v1

*************
Test Coverage"""
)
<|endoftext|>


<commit_before># Copyright 2019 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""This script is used to synthesize generated parts of this library."""

import logging
from pathlib import Path
import subprocess

import synthtool as s
from synthtool.languages import php
from synthtool import _tracked_paths

logging.basicConfig(level=logging.DEBUG)

src = Path(f"../{php.STAGING_DIR}/WebSecurityScanner").resolve()
dest = Path().resolve()

# Added so that we can pass copy_excludes in the owlbot_main() call
_tracked_paths.add(src)

php.owlbot_main(
    src=src,
    dest=dest,
    copy_excludes=[
        src / "**/*_*.php"
    ]
)

# remove class_alias code
s.replace(
    "src/V*/*/*.php",
    r"^// Adding a class alias for backwards compatibility with the previous class name.$"
    + "\n"
    + r"^class_alias\(.*\);$"
    + "\n",
    '')

# document and utilize apiEndpoint instead of serviceAddress
s.replace(
    "**/Gapic/*GapicClient.php",
    r"'serviceAddress' =>",
    r"'apiEndpoint' =>")
s.replace(
    "**/Gapic/*GapicClient.php",
    r"@type string \$serviceAddress\n\s+\*\s+The address",
    r"""@type string $serviceAddress
     *           **Deprecated**. This option will be removed in a future major release. Please
     *           utilize the `$apiEndpoint` option instead.
     *     @type string $apiEndpoint
     *           The address""")
s.replace(
    "**/Gapic/*GapicClient.php",
    r"\$transportConfig, and any \$serviceAddress",
    r"$transportConfig, and any `$apiEndpoint`")

### [START] protoc backwards compatibility fixes

# roll back to private properties.
s.replace(
    "src/**/V*/**/*.php",
    r"Generated from protobuf field ([^\n]{0,})\n\s{5}\*/\n\s{4}protected \$",
    r"""Generated from protobuf field \1
     */
    private $""")

# prevent proto messages from being marked final
s.replace(
    "src/**/V*/**/*.php",
    r"final class",
    r"class")

# Replace "Unwrapped" with "Value" for method names.
s.replace(
    "src/**/V*/**/*.php",
    r"public function ([s|g]\w{3,})Unwrapped",
    r"public function \1Value"
)

### [END] protoc backwards compatibility fixes

# fix relative cloud.google.com links
s.replace(
    "src/**/V*/**/*.php",
    r"(.{0,})\]\((/.{0,})\)",
    r"\1](https://cloud.google.com\2)"
)
<commit_msg>fix: owlbot copy excludes (#4987)<commit_after># Copyright 2019 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""This script is used to synthesize generated parts of this library."""

import logging
from pathlib import Path
import subprocess

import synthtool as s
from synthtool.languages import php
from synthtool import _tracked_paths

logging.basicConfig(level=logging.DEBUG)

src = Path(f"../{php.STAGING_DIR}/WebSecurityScanner").resolve()
dest = Path().resolve()

# Added so that we can pass copy_excludes in the owlbot_main() call
_tracked_paths.add(src)

php.owlbot_main(
    src=src,
    dest=dest,
    copy_excludes=[
        src / "**/[A-Z]*_*.php"
    ]
)

# remove class_alias code
s.replace(
    "src/V*/*/*.php",
    r"^// Adding a class alias for backwards compatibility with the previous class name.$"
    + "\n"
    + r"^class_alias\(.*\);$"
    + "\n",
    '')

# document and utilize apiEndpoint instead of serviceAddress
s.replace(
    "**/Gapic/*GapicClient.php",
    r"'serviceAddress' =>",
    r"'apiEndpoint' =>")
s.replace(
    "**/Gapic/*GapicClient.php",
    r"@type string \$serviceAddress\n\s+\*\s+The address",
    r"""@type string $serviceAddress
     *           **Deprecated**. This option will be removed in a future major release. Please
     *           utilize the `$apiEndpoint` option instead.
     *     @type string $apiEndpoint
     *           The address""")
s.replace(
    "**/Gapic/*GapicClient.php",
    r"\$transportConfig, and any \$serviceAddress",
    r"$transportConfig, and any `$apiEndpoint`")

### [START] protoc backwards compatibility fixes

# roll back to private properties.
s.replace(
    "src/**/V*/**/*.php",
    r"Generated from protobuf field ([^\n]{0,})\n\s{5}\*/\n\s{4}protected \$",
    r"""Generated from protobuf field \1
     */
    private $""")

# prevent proto messages from being marked final
s.replace(
    "src/**/V*/**/*.php",
    r"final class",
    r"class")

# Replace "Unwrapped" with "Value" for method names.
s.replace(
    "src/**/V*/**/*.php",
    r"public function ([s|g]\w{3,})Unwrapped",
    r"public function \1Value"
)

### [END] protoc backwards compatibility fixes

# fix relative cloud.google.com links
s.replace(
    "src/**/V*/**/*.php",
    r"(.{0,})\]\((/.{0,})\)",
    r"\1](https://cloud.google.com\2)"
)
<|endoftext|>


<commit_before># Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""This script is used to synthesize generated parts of this library."""

import os

import synthtool as s
import synthtool.gcp as gcp
from synthtool.languages import python

common = gcp.CommonTemplates()

default_version = "v1"

for library in s.get_staging_dirs(default_version):
    s.move(library, excludes=["setup.py", "README.rst", "docs/index.rst"])

s.remove_staging_dirs()

# ----------------------------------------------------------------------------
# Add templated files
# ----------------------------------------------------------------------------

templated_files = common.py_library(cov_level=99, microgenerator=True)
python.py_samples(skip_readmes=True)

# the microgenerator has a good coveragerc file
excludes = [".coveragerc"]
s.move(
  templated_files, excludes=excludes
)

s.shell.run(["nox", "-s", "blacken"], hide_output=False)

# ----------------------------------------------------------------------------
# Main Branch migration
# ----------------------------------------------------------------------------

s.replace(
  "*.rst",
  "master",
  "main"
)

s.replace(
  "*.rst",
  "google-cloud-python/blob/main",
  "google-cloud-python/blob/master"
)

s.replace(
  "CONTRIBUTING.rst",
  "kubernetes/community/blob/main",
  "kubernetes/community/blob/master"
)

s.replace(
  ".kokoro/*",
  "master",
  "main"
)

<commit_msg>chore: revert owlbot main branch templates (#68)<commit_after># Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""This script is used to synthesize generated parts of this library."""

import os

import synthtool as s
import synthtool.gcp as gcp
from synthtool.languages import python

common = gcp.CommonTemplates()

default_version = "v1"

for library in s.get_staging_dirs(default_version):
    s.move(library, excludes=["setup.py", "README.rst", "docs/index.rst"])

s.remove_staging_dirs()

# ----------------------------------------------------------------------------
# Add templated files
# ----------------------------------------------------------------------------

templated_files = common.py_library(cov_level=99, microgenerator=True)
python.py_samples(skip_readmes=True)

# the microgenerator has a good coveragerc file
excludes = [".coveragerc"]
s.move(
  templated_files, excludes=excludes
)

s.shell.run(["nox", "-s", "blacken"], hide_output=False)

<|endoftext|>


<commit_before>from __future__ import absolute_import

from .struct import Struct
from .types import Array, Int16, Int32, Int64, Schema, String

class OffsetCommitResponse_v0(Struct):
    API_KEY = 8
    API_VERSION = 0
    SCHEMA = Schema(
        ('topics', Array(
            ('topic', String('utf-8')),
            ('partitions', Array(
                ('partition', Int32),
                ('error_code', Int16)))))
    )

class OffsetCommitResponse_v1(Struct):
    API_KEY = 8
    API_VERSION = 1
    SCHEMA = OffsetCommitResponse_v0.SCHEMA

class OffsetCommitResponse_v2(Struct):
    API_KEY = 8
    API_VERSION = 2
    SCHEMA = OffsetCommitResponse_v1.SCHEMA

class OffsetCommitRequest_v0(Struct):
    API_KEY = 8
    API_VERSION = 0  # Zookeeper-backed storage
    RESPONSE_TYPE = OffsetCommitResponse_v0
    SCHEMA = Schema(
        ('consumer_group', String('utf-8')),
        ('topics', Array(
            ('topic', String('utf-8')),
            ('partitions', Array(
                ('partition', Int32),
                ('offset', Int64),
                ('metadata', String('utf-8'))))))
    )

class OffsetCommitRequest_v1(Struct):
    API_KEY = 8
    API_VERSION = 1  # Kafka-backed storage
    RESPONSE_TYPE = OffsetCommitResponse_v1
    SCHEMA = Schema(
        ('consumer_group', String('utf-8')),
        ('consumer_group_generation_id', Int32),
        ('consumer_id', String('utf-8')),
        ('topics', Array(
            ('topic', String('utf-8')),
            ('partitions', Array(
                ('partition', Int32),
                ('offset', Int64),
                ('timestamp', Int64),
                ('metadata', String('utf-8'))))))
    )

class OffsetCommitRequest_v2(Struct):
    API_KEY = 8
    API_VERSION = 2  # added retention_time, dropped timestamp
    RESPONSE_TYPE = OffsetCommitResponse_v2
    SCHEMA = Schema(
        ('consumer_group', String('utf-8')),
        ('consumer_group_generation_id', Int32),
        ('consumer_id', String('utf-8')),
        ('retention_time', Int64),
        ('topics', Array(
            ('topic', String('utf-8')),
            ('partitions', Array(
                ('partition', Int32),
                ('offset', Int64),
                ('metadata', String('utf-8'))))))
    )
    DEFAULT_GENERATION_ID = -1
    DEFAULT_RETENTION_TIME = -1

OffsetCommitRequest = [OffsetCommitRequest_v0, OffsetCommitRequest_v1,
                       OffsetCommitRequest_v2]
OffsetCommitResponse = [OffsetCommitResponse_v0, OffsetCommitResponse_v1,
                        OffsetCommitResponse_v2]

class OffsetFetchResponse_v0(Struct):
    API_KEY = 9
    API_VERSION = 0
    SCHEMA = Schema(
        ('topics', Array(
            ('topic', String('utf-8')),
            ('partitions', Array(
                ('partition', Int32),
                ('offset', Int64),
                ('metadata', String('utf-8')),
                ('error_code', Int16)))))
    )

class OffsetFetchResponse_v1(Struct):
    API_KEY = 9
    API_VERSION = 1
    SCHEMA = OffsetFetchResponse_v0.SCHEMA

class OffsetFetchRequest_v0(Struct):
    API_KEY = 9
    API_VERSION = 0  # zookeeper-backed storage
    RESPONSE_TYPE = OffsetFetchResponse_v0
    SCHEMA = Schema(
        ('consumer_group', String('utf-8')),
        ('topics', Array(
            ('topic', String('utf-8')),
            ('partitions', Array(Int32))))
    )

class OffsetFetchRequest_v1(Struct):
    API_KEY = 9
    API_VERSION = 1  # kafka-backed storage
    RESPONSE_TYPE = OffsetFetchResponse_v1
    SCHEMA = OffsetFetchRequest_v0.SCHEMA

OffsetFetchRequest = [OffsetFetchRequest_v0, OffsetFetchRequest_v1]
OffsetFetchResponse = [OffsetFetchResponse_v0, OffsetFetchResponse_v1]

class GroupCoordinatorResponse_v0(Struct):
    API_KEY = 10
    API_VERSION = 0
    SCHEMA = Schema(
        ('error_code', Int16),
        ('coordinator_id', Int32),
        ('host', String('utf-8')),
        ('port', Int32)
    )

class GroupCoordinatorRequest_v0(Struct):
    API_KEY = 10
    API_VERSION = 0
    RESPONSE_TYPE = GroupCoordinatorResponse_v0
    SCHEMA = Schema(
        ('consumer_group', String('utf-8'))
    )

GroupCoordinatorRequest = [GroupCoordinatorRequest_v0]
GroupCoordinatorResponse = [GroupCoordinatorResponse_v0]
<commit_msg>KIP-88 / KAFKA-3853: OffsetFetch v2 structs (#971)<commit_after>from __future__ import absolute_import

from .struct import Struct
from .types import Array, Int16, Int32, Int64, Schema, String

class OffsetCommitResponse_v0(Struct):
    API_KEY = 8
    API_VERSION = 0
    SCHEMA = Schema(
        ('topics', Array(
            ('topic', String('utf-8')),
            ('partitions', Array(
                ('partition', Int32),
                ('error_code', Int16)))))
    )

class OffsetCommitResponse_v1(Struct):
    API_KEY = 8
    API_VERSION = 1
    SCHEMA = OffsetCommitResponse_v0.SCHEMA

class OffsetCommitResponse_v2(Struct):
    API_KEY = 8
    API_VERSION = 2
    SCHEMA = OffsetCommitResponse_v1.SCHEMA

class OffsetCommitRequest_v0(Struct):
    API_KEY = 8
    API_VERSION = 0  # Zookeeper-backed storage
    RESPONSE_TYPE = OffsetCommitResponse_v0
    SCHEMA = Schema(
        ('consumer_group', String('utf-8')),
        ('topics', Array(
            ('topic', String('utf-8')),
            ('partitions', Array(
                ('partition', Int32),
                ('offset', Int64),
                ('metadata', String('utf-8'))))))
    )

class OffsetCommitRequest_v1(Struct):
    API_KEY = 8
    API_VERSION = 1  # Kafka-backed storage
    RESPONSE_TYPE = OffsetCommitResponse_v1
    SCHEMA = Schema(
        ('consumer_group', String('utf-8')),
        ('consumer_group_generation_id', Int32),
        ('consumer_id', String('utf-8')),
        ('topics', Array(
            ('topic', String('utf-8')),
            ('partitions', Array(
                ('partition', Int32),
                ('offset', Int64),
                ('timestamp', Int64),
                ('metadata', String('utf-8'))))))
    )

class OffsetCommitRequest_v2(Struct):
    API_KEY = 8
    API_VERSION = 2  # added retention_time, dropped timestamp
    RESPONSE_TYPE = OffsetCommitResponse_v2
    SCHEMA = Schema(
        ('consumer_group', String('utf-8')),
        ('consumer_group_generation_id', Int32),
        ('consumer_id', String('utf-8')),
        ('retention_time', Int64),
        ('topics', Array(
            ('topic', String('utf-8')),
            ('partitions', Array(
                ('partition', Int32),
                ('offset', Int64),
                ('metadata', String('utf-8'))))))
    )
    DEFAULT_GENERATION_ID = -1
    DEFAULT_RETENTION_TIME = -1

OffsetCommitRequest = [OffsetCommitRequest_v0, OffsetCommitRequest_v1,
                       OffsetCommitRequest_v2]
OffsetCommitResponse = [OffsetCommitResponse_v0, OffsetCommitResponse_v1,
                        OffsetCommitResponse_v2]

class OffsetFetchResponse_v0(Struct):
    API_KEY = 9
    API_VERSION = 0
    SCHEMA = Schema(
        ('topics', Array(
            ('topic', String('utf-8')),
            ('partitions', Array(
                ('partition', Int32),
                ('offset', Int64),
                ('metadata', String('utf-8')),
                ('error_code', Int16)))))
    )

class OffsetFetchResponse_v1(Struct):
    API_KEY = 9
    API_VERSION = 1
    SCHEMA = OffsetFetchResponse_v0.SCHEMA

class OffsetFetchResponse_v2(Struct):
    # Added in KIP-88
    API_KEY = 9
    API_VERSION = 2
    SCHEMA = Schema(
        ('topics', Array(
            ('topic', String('utf-8')),
            ('partitions', Array(
                ('partition', Int32),
                ('offset', Int64),
                ('metadata', String('utf-8')),
                ('error_code', Int16))))),
        ('error_code', Int16)
    )

class OffsetFetchRequest_v0(Struct):
    API_KEY = 9
    API_VERSION = 0  # zookeeper-backed storage
    RESPONSE_TYPE = OffsetFetchResponse_v0
    SCHEMA = Schema(
        ('consumer_group', String('utf-8')),
        ('topics', Array(
            ('topic', String('utf-8')),
            ('partitions', Array(Int32))))
    )

class OffsetFetchRequest_v1(Struct):
    API_KEY = 9
    API_VERSION = 1  # kafka-backed storage
    RESPONSE_TYPE = OffsetFetchResponse_v1
    SCHEMA = OffsetFetchRequest_v0.SCHEMA

class OffsetFetchRequest_v2(Struct):
    # KIP-88: Allows passing null topics to return offsets for all partitions
    # that the consumer group has a stored offset for, even if no consumer in
    # the group is currently consuming that partition.
    API_KEY = 9
    API_VERSION = 2
    RESPONSE_TYPE = OffsetFetchResponse_v2
    SCHEMA = OffsetFetchRequest_v1.SCHEMA

OffsetFetchRequest = [OffsetFetchRequest_v0, OffsetFetchRequest_v1,
    OffsetFetchRequest_v2]
OffsetFetchResponse = [OffsetFetchResponse_v0, OffsetFetchResponse_v1,
    OffsetFetchResponse_v2]

class GroupCoordinatorResponse_v0(Struct):
    API_KEY = 10
    API_VERSION = 0
    SCHEMA = Schema(
        ('error_code', Int16),
        ('coordinator_id', Int32),
        ('host', String('utf-8')),
        ('port', Int32)
    )

class GroupCoordinatorRequest_v0(Struct):
    API_KEY = 10
    API_VERSION = 0
    RESPONSE_TYPE = GroupCoordinatorResponse_v0
    SCHEMA = Schema(
        ('consumer_group', String('utf-8'))
    )

GroupCoordinatorRequest = [GroupCoordinatorRequest_v0]
GroupCoordinatorResponse = [GroupCoordinatorResponse_v0]
<|endoftext|>