<commit_before>from argparse import _HelpAction, _SubParsersAction, ArgumentParser
from docutils import nodes
from sphinx.util.compat import Directive
from sphinx.util.compat import make_admonition
from docutils.parsers.rst.directives import flag, unchanged, nonnegative_int
from sphinxarg.parser import parse_parser, parser_navigate

def map_nested_definitions(nested_content):

    if nested_content is None:
        raise Exception('Nested content should be iterable, not null')

    # build definition dictionary
    definitions = {}
    for item in nested_content:
        if isinstance(item, nodes.definition_list):
            for subitem in item:
                if isinstance(subitem, nodes.definition_list_item):
                    if len(subitem.children) > 0:
                        classifier = '@after'
                        idx = subitem.first_child_matching_class(nodes.classifier)
                        if not idx is None:
                            ci = subitem[idx]
                            if len(ci.children) > 0:
                                classifier = ci.children[0].astext()

                        if not classifier is None and not classifier in ('@replace', '@before', '@after'):
                            raise Exception('Unknown classifier: %s' % classifier)

                        idx = subitem.first_child_matching_class(nodes.term)
                        if not idx is None:
                            ch = subitem[idx]
                            if len(ch.children) > 0:
                                term = ch.children[0].astext()
                                idx = subitem.first_child_matching_class(nodes.definition)
                                if not idx is None:
                                    def_node = subitem[idx]
                                    def_node.attributes['classifier'] = classifier
                                    definitions[term] = def_node
    return definitions

def print_arg_list(data, nested_content):

    definitions = map_nested_definitions(nested_content)

    items = []

    if 'args' in data:
        for arg in data['args']:
            my_def = [nodes.paragraph(text=arg['help'])] if arg['help'] else []

            name = arg['name']

            my_def = apply_definition(definitions, my_def, name)

            if len(my_def) == 0:
                my_def.append(nodes.paragraph(text='Undocumented'))

            if 'choices' in arg:
                my_def.append(nodes.paragraph(text=('Possible choices: %s' % ', '.join(arg['choices']))))

            items.append(
                nodes.option_list_item('',
                    nodes.option_group('', nodes.option_string(text=name)),
                    nodes.description('', *my_def)
                )
            )

    return nodes.option_list('', *items) if items else None

def print_opt_list(data, nested_content):

    definitions = map_nested_definitions(nested_content)

    items = []

    if 'options' in data:
        for opt in data['options']:

            names = []

            my_def = [nodes.paragraph(text=opt['help'])] if opt['help'] else []

            for name in opt['name']:

                option_declaration = [nodes.option_string(text=name)]
                if not opt['default'] is None and opt['default'] != '==SUPPRESS==':
                    option_declaration += nodes.option_argument('', text='=' + str(opt['default']))

                names.append(nodes.option('', *option_declaration))

                my_def = apply_definition(definitions, my_def, name)

            if len(my_def) == 0:
                my_def.append(nodes.paragraph(text='Undocumented'))

            if 'choices' in opt:
                my_def.append(nodes.paragraph(text=('Possible choices: %s' % ', '.join(opt['choices']))))

            items.append(
                nodes.option_list_item('',
                    nodes.option_group('', *names),
                    nodes.description('', *my_def)
                )
            )

    return nodes.option_list('', *items) if items else None

def print_command_args_and_opts(arg_list, opt_list, sub_list=None):

    items = []

    if arg_list:
        items.append(nodes.definition_list_item('',
            nodes.term(text='Positional arguments:'),
            nodes.definition('', arg_list)
        ))

    if opt_list:
        items.append(nodes.definition_list_item('',
            nodes.term(text='Options:'),
            nodes.definition('', opt_list)
        ))

    if sub_list and len(sub_list):
        items.append(nodes.definition_list_item('',
            nodes.term(text='Sub-commands:'),
            nodes.definition('', sub_list)
        ))

    return nodes.definition_list('', *items)

def apply_definition(definitions, my_def, name):
    if name in definitions:
        definition = definitions[name]

        classifier = definition['classifier']

        if classifier == '@replace':
            return definition.children

        if classifier == '@after':
            return my_def + definition.children

        if classifier == '@before':
            return definition.children + my_def

        raise Exception('Unknown classifier: %s' % classifier)

    return my_def

def print_subcommand_list(data, nested_content):

    definitions = map_nested_definitions(nested_content)

    items = []

    if 'children' in data:
        for child in data['children']:

            my_def = [nodes.paragraph(text=child['help'])] if child['help'] else []
            name = child['name']
            nested_def = definitions[name] if name in definitions else []

            my_def = apply_definition(definitions, my_def, name)

            if len(my_def) == 0:
                my_def.append(nodes.paragraph(text='Undocumented'))

            my_def.append(nodes.literal_block(text=child['usage']))

            my_def.append(print_command_args_and_opts(
                print_arg_list(child, nested_content),
                print_opt_list(child, nested_content)
            ))

            items.append(
                nodes.definition_list_item(
                    '',
                    nodes.term('', '', nodes.strong(text=name)),
                    nodes.definition('', *my_def)
                )
            )

    return nodes.definition_list('', *items)

class ArgParseDirective(Directive):

    has_content = True

    option_spec = dict(module=unchanged, func=unchanged, ref=unchanged, prog=unchanged, path=unchanged, nodefault=flag)

    def run(self):

        if 'module' in self.options and 'func' in self.options:
            module_name = self.options['module']
            attr_name = self.options['func']
        elif 'ref' in self.options:
            _parts = self.options['ref'].split('.')
            module_name = '.'.join(_parts[0:-1])
            attr_name = _parts[-1]
        else:
            raise self.error(':module: and :func: should be specified, or :ref:')

        mod = __import__(module_name, globals(), locals(), [attr_name])
        if not hasattr(mod, attr_name):
            raise self.error('Module "%s" has no attribute "%s"\nIncorrect argparse :module: or :func: values?' % (module_name, attr_name))

        func = getattr(mod, attr_name)

        if isinstance(func, ArgumentParser):
            parser = func
        else:
            parser = func()

        if not 'path' in self.options:
            self.options['path'] = ''
        path = str(self.options['path'])

        parser.prog = self.options['prog']

        result = parse_parser(parser,
                              skip_default_values='nodefault' in self.options)
        result = parser_navigate(result, path)

        nested_content = nodes.paragraph()
        self.state.nested_parse(self.content, self.content_offset, nested_content)
        nested_content = nested_content.children

        items = []

        # add common content between
        for item in nested_content:
            if not isinstance(item, nodes.definition_list):
                items.append(item)

        if 'description' in result:
            items.append(nodes.paragraph(text=result['description']))

        items.append(nodes.literal_block(text=result['usage']))

        items.append(print_command_args_and_opts(
            print_arg_list(result, nested_content),
            print_opt_list(result, nested_content),
            print_subcommand_list(result, nested_content)
        ))

        if 'epilog' in result:
            items.append(nodes.paragraph(text=result['epilog']))

        return items

def setup(app):
    app.add_directive('argparse', ArgParseDirective)
<commit_msg>remove unused imports<commit_after>from argparse import ArgumentParser
from docutils import nodes
from sphinx.util.compat import Directive
from docutils.parsers.rst.directives import flag, unchanged
from sphinxarg.parser import parse_parser, parser_navigate

def map_nested_definitions(nested_content):

    if nested_content is None:
        raise Exception('Nested content should be iterable, not null')

    # build definition dictionary
    definitions = {}
    for item in nested_content:
        if isinstance(item, nodes.definition_list):
            for subitem in item:
                if isinstance(subitem, nodes.definition_list_item):
                    if len(subitem.children) > 0:
                        classifier = '@after'
                        idx = subitem.first_child_matching_class(nodes.classifier)
                        if not idx is None:
                            ci = subitem[idx]
                            if len(ci.children) > 0:
                                classifier = ci.children[0].astext()

                        if not classifier is None and not classifier in ('@replace', '@before', '@after'):
                            raise Exception('Unknown classifier: %s' % classifier)

                        idx = subitem.first_child_matching_class(nodes.term)
                        if not idx is None:
                            ch = subitem[idx]
                            if len(ch.children) > 0:
                                term = ch.children[0].astext()
                                idx = subitem.first_child_matching_class(nodes.definition)
                                if not idx is None:
                                    def_node = subitem[idx]
                                    def_node.attributes['classifier'] = classifier
                                    definitions[term] = def_node
    return definitions

def print_arg_list(data, nested_content):

    definitions = map_nested_definitions(nested_content)

    items = []

    if 'args' in data:
        for arg in data['args']:
            my_def = [nodes.paragraph(text=arg['help'])] if arg['help'] else []

            name = arg['name']

            my_def = apply_definition(definitions, my_def, name)

            if len(my_def) == 0:
                my_def.append(nodes.paragraph(text='Undocumented'))

            if 'choices' in arg:
                my_def.append(nodes.paragraph(text=('Possible choices: %s' % ', '.join(arg['choices']))))

            items.append(
                nodes.option_list_item('',
                    nodes.option_group('', nodes.option_string(text=name)),
                    nodes.description('', *my_def)
                )
            )

    return nodes.option_list('', *items) if items else None

def print_opt_list(data, nested_content):

    definitions = map_nested_definitions(nested_content)

    items = []

    if 'options' in data:
        for opt in data['options']:

            names = []

            my_def = [nodes.paragraph(text=opt['help'])] if opt['help'] else []

            for name in opt['name']:

                option_declaration = [nodes.option_string(text=name)]
                if not opt['default'] is None and opt['default'] != '==SUPPRESS==':
                    option_declaration += nodes.option_argument('', text='=' + str(opt['default']))

                names.append(nodes.option('', *option_declaration))

                my_def = apply_definition(definitions, my_def, name)

            if len(my_def) == 0:
                my_def.append(nodes.paragraph(text='Undocumented'))

            if 'choices' in opt:
                my_def.append(nodes.paragraph(text=('Possible choices: %s' % ', '.join(opt['choices']))))

            items.append(
                nodes.option_list_item('',
                    nodes.option_group('', *names),
                    nodes.description('', *my_def)
                )
            )

    return nodes.option_list('', *items) if items else None

def print_command_args_and_opts(arg_list, opt_list, sub_list=None):

    items = []

    if arg_list:
        items.append(nodes.definition_list_item('',
            nodes.term(text='Positional arguments:'),
            nodes.definition('', arg_list)
        ))

    if opt_list:
        items.append(nodes.definition_list_item('',
            nodes.term(text='Options:'),
            nodes.definition('', opt_list)
        ))

    if sub_list and len(sub_list):
        items.append(nodes.definition_list_item('',
            nodes.term(text='Sub-commands:'),
            nodes.definition('', sub_list)
        ))

    return nodes.definition_list('', *items)

def apply_definition(definitions, my_def, name):
    if name in definitions:
        definition = definitions[name]

        classifier = definition['classifier']

        if classifier == '@replace':
            return definition.children

        if classifier == '@after':
            return my_def + definition.children

        if classifier == '@before':
            return definition.children + my_def

        raise Exception('Unknown classifier: %s' % classifier)

    return my_def

def print_subcommand_list(data, nested_content):

    definitions = map_nested_definitions(nested_content)

    items = []

    if 'children' in data:
        for child in data['children']:

            my_def = [nodes.paragraph(text=child['help'])] if child['help'] else []
            name = child['name']
            nested_def = definitions[name] if name in definitions else []

            my_def = apply_definition(definitions, my_def, name)

            if len(my_def) == 0:
                my_def.append(nodes.paragraph(text='Undocumented'))

            my_def.append(nodes.literal_block(text=child['usage']))

            my_def.append(print_command_args_and_opts(
                print_arg_list(child, nested_content),
                print_opt_list(child, nested_content)
            ))

            items.append(
                nodes.definition_list_item(
                    '',
                    nodes.term('', '', nodes.strong(text=name)),
                    nodes.definition('', *my_def)
                )
            )

    return nodes.definition_list('', *items)

class ArgParseDirective(Directive):

    has_content = True

    option_spec = dict(module=unchanged, func=unchanged, ref=unchanged, prog=unchanged, path=unchanged, nodefault=flag)

    def run(self):

        if 'module' in self.options and 'func' in self.options:
            module_name = self.options['module']
            attr_name = self.options['func']
        elif 'ref' in self.options:
            _parts = self.options['ref'].split('.')
            module_name = '.'.join(_parts[0:-1])
            attr_name = _parts[-1]
        else:
            raise self.error(':module: and :func: should be specified, or :ref:')

        mod = __import__(module_name, globals(), locals(), [attr_name])
        if not hasattr(mod, attr_name):
            raise self.error('Module "%s" has no attribute "%s"\nIncorrect argparse :module: or :func: values?' % (module_name, attr_name))

        func = getattr(mod, attr_name)

        if isinstance(func, ArgumentParser):
            parser = func
        else:
            parser = func()

        if not 'path' in self.options:
            self.options['path'] = ''
        path = str(self.options['path'])

        parser.prog = self.options['prog']

        result = parse_parser(parser,
                              skip_default_values='nodefault' in self.options)
        result = parser_navigate(result, path)

        nested_content = nodes.paragraph()
        self.state.nested_parse(self.content, self.content_offset, nested_content)
        nested_content = nested_content.children

        items = []

        # add common content between
        for item in nested_content:
            if not isinstance(item, nodes.definition_list):
                items.append(item)

        if 'description' in result:
            items.append(nodes.paragraph(text=result['description']))

        items.append(nodes.literal_block(text=result['usage']))

        items.append(print_command_args_and_opts(
            print_arg_list(result, nested_content),
            print_opt_list(result, nested_content),
            print_subcommand_list(result, nested_content)
        ))

        if 'epilog' in result:
            items.append(nodes.paragraph(text=result['epilog']))

        return items

def setup(app):
    app.add_directive('argparse', ArgParseDirective)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""route-ctl application actions."""

from __future__ import (
    absolute_import,
    print_function,
    unicode_literals,
    with_statement,
)

import gettext
import json
import logging

from . import core
from .builder import RouteBuilder
from .parser import RouteParser

# logging
log = logging.getLogger(__name__)

# l18n
trans = gettext.translation(__name__, 'locale', fallback=True)
_ = trans.gettext

def write_routes(routes, route_file):
    builder = RouteBuilder()
    log.info(_('writing routes to file %r'), route_file)
    builder.write(routes, dest_filename=route_file)

def list_items(route_file, *args, **kwargs):
    parser = RouteParser(route_file)
    current_routes = parser.parse_all()
    log.info(_('parsing routes from route file'))
    result = {'routes': list(current_routes)}
    log.info(_('listing all items'))
    return json.dumps(result, indent=2)

def find_items(
        route_file,
        value,
        key,
        ignore_case,
        exact_match,
        *args,
        **kwargs):
    parser = RouteParser(route_file)
    current_routes = parser.parse_all()
    log.info(_('creating a route filter: %s=%r'), key, value)
    found_routes = core.find_routes(current_routes, value, key,
                                    ignore_case, exact_match)
    log.info(_('parsing routes from route file'))
    result = {'routes': list(found_routes)}
    return json.dumps(result, indent=2)

def validate_item(
        route_file,
        name,
        ensure=None,
        gateway=None,
        interface=None,
        network=None,
        netmask=None,
        options=None,
        *args,
        **kwargs):
    route = {'name': name}
    if ensure:
        route['ensure'] = ensure
    if gateway:
        route['gateway'] = gateway
    if interface:
        route['interface'] = interface
    if netmask:
        route['netmask'] = netmask
    if network:
        route['network'] = network
    if options:
        route['options'] = options
    log.info(_('parsing routes from route file'))
    parser = RouteParser(route_file)
    current_routes = parser.parse_all()
    log.info(_('validating item %r'), name)
    validated = core.validate_route(current_routes, route)
    result = {'input': route, 'output': validated}
    return json.dumps(result, indent=2)

def batch_validate_items(
        route_file,
        source_file,
        *args,
        **kwargs):
    log.info(_('loading routes from JSON'))
    src_routes = json.load(source_file)['routes']
    log.info(_('parsing routes from route file'))
    parser = RouteParser(route_file)
    current_routes = parser.parse_all()
    log.info(_('batch validating routes'))
    validated = core.validate_routes(current_routes, src_routes)
    result = {'routes': list(validated)}
    return json.dumps(result, indent=2)

def batch_replace_items(
        route_file,
        source_file,
        *args,
        **kwargs):
    log.info(_('loading routes from JSON'))
    src_routes = list(json.load(source_file)['routes'])
    log.info(_('batch replacing routes'))
    write_routes(src_routes, route_file)
    result = {'routes': src_routes}
    return json.dumps(result, indent=2)

def create_or_update_item(
        route_file,
        name,
        ensure,
        gateway,
        interface,
        network,
        netmask,
        options=None,
        *args,
        **kwargs):
    log.debug(_('called `create_or_update_item` with arguments: route_file=%r, '
                'name=%r, ensure=%r, gateway=%r, interface=%r, network=%r, '
                'netmask=%r, options=%r, %r, %r'),
              route_file, name, ensure, gateway, interface, network, netmask,
              options, args, kwargs)
    route = {
        'name': name,
        'ensure': ensure,
        'gateway': gateway,
        'interface': interface,
        'netmask': netmask,
        'network': network,
    }
    if options:
        route['options'] = options
    log.info(_('parsing routes from route file'))
    with RouteParser.open(route_file) as parser:
        current_routes = list(parser.parse_all())
    log.info(_('looking up if the route already exists'))
    existing_routes = list(core.find_existing(current_routes, route))
    if not existing_routes:
        log.info(_('creating a new route %s=%r'), 'name', name)
        current_routes.append(route)
    elif len(existing_routes) > 1:
        raise core.MultipleRoutesFoundError(
            _('Unable to update. More than one route with such name exists.'))
    else:
        existing_route = existing_routes[0]
        old_name = existing_route['name']
        log.info(_('updating existing route %s=%r'), 'name', old_name)
        existing_route.update(route)
    write_routes(current_routes, route_file)
    return json.dumps({'routes': current_routes}, indent=2)

def update_item(
        route_file,
        name,
        ensure,
        gateway,
        interface,
        network,
        netmask,
        options=None,
        *args,
        **kwargs):
    log.debug(_('called `update_item` with arguments: route_file=%r, '
                'name=%r, ensure=%r, gateway=%r, interface=%r, network=%r, '
                'netmask=%r, options=%r, %r, %r'),
              route_file, name, ensure, gateway, interface, network, netmask,
              options, args, kwargs)
    route = {
        'name': name,
        'ensure': ensure,
        'gateway': gateway,
        'interface': interface,
        'netmask': netmask,
        'network': network,
    }
    if options:
        route['options'] = options
    log.info(_('parsing routes from route file'))
    with RouteParser.open(route_file) as parser:
        current_routes = list(parser.parse_all())
    log.info(_('looking up if the route already exists'))
    existing_routes = list(core.find_existing(current_routes, route))
    if not existing_routes:
        raise core.RouteNotFoundError(
            _('Unable to update. No route matching criteria found.'))
    if len(existing_routes) > 1:
        raise core.MultipleRoutesFoundError(
            _('Unable to update. More than one route matching criteria exist.'))
    existing_route = existing_routes[0]
    old_name = existing_route['name']
    log.info(_('updating existing route %s=%r'), 'name', old_name)
    existing_route.update(route)
    write_routes(current_routes, route_file)
    return json.dumps({'routes': current_routes}, indent=2)

def delete_items(
        route_file,
        value,
        key,
        ignore_case,
        exact_match,
        *args,
        **kwargs):
    log.debug(_('called `delete_items` with arguments: route_file=%r, '
                'value=%r, key=%r, ignore_case=%r, exact_match=%r, %r, %r'),
              route_file, value, key, ignore_case, exact_match, args, kwargs)
    with RouteParser.open(route_file) as parser:
        log.info(_('parsing routes from route file'))
        current_routes = list(parser.parse_all())
        log.info(_('filtering out items matching filter: %s=%r'), key, value)
        new_routes = list(core.delete_routes(current_routes, value, key,
                                             ignore_case, exact_match))
    write_routes(new_routes, route_file)
    result = {'routes': new_routes}
    return json.dumps(result, indent=2)
<commit_msg>Cleanup imports<commit_after># -*- coding: utf-8 -*-
"""route-ctl application actions."""

from __future__ import (
    absolute_import,
    print_function,
    unicode_literals,
    with_statement,
)

import json
import logging
from gettext import translation

from . import core
from .builder import RouteBuilder
from .parser import RouteParser
from .manager import RouteManager

# logging
log = logging.getLogger(__name__)

# l18n
_ = translation(__name__, 'locale', fallback=True).gettext

def write_routes(routes, route_file):
    builder = RouteBuilder()
    log.info(_('writing routes to file %r'), route_file)
    builder.write(routes, dest_filename=route_file)

def list_items(route_file, *args, **kwargs):
    parser = RouteParser(route_file)
    current_routes = parser.parse_all()
    log.info(_('parsing routes from route file'))
    result = {'routes': list(current_routes)}
    log.info(_('listing all items'))
    return json.dumps(result, indent=2)

def find_items(
        route_file,
        value,
        key,
        ignore_case,
        exact_match,
        *args,
        **kwargs):
    parser = RouteParser(route_file)
    current_routes = parser.parse_all()
    log.info(_('creating a route filter: %s=%r'), key, value)
    found_routes = core.find_routes(current_routes, value, key,
                                    ignore_case, exact_match)
    log.info(_('parsing routes from route file'))
    result = {'routes': list(found_routes)}
    return json.dumps(result, indent=2)

def validate_item(
        route_file,
        name,
        ensure=None,
        gateway=None,
        interface=None,
        network=None,
        netmask=None,
        options=None,
        *args,
        **kwargs):
    route = {'name': name}
    if ensure:
        route['ensure'] = ensure
    if gateway:
        route['gateway'] = gateway
    if interface:
        route['interface'] = interface
    if netmask:
        route['netmask'] = netmask
    if network:
        route['network'] = network
    if options:
        route['options'] = options
    log.info(_('parsing routes from route file'))
    parser = RouteParser(route_file)
    current_routes = parser.parse_all()
    log.info(_('validating item %r'), name)
    validated = core.validate_route(current_routes, route)
    result = {'input': route, 'output': validated}
    return json.dumps(result, indent=2)

def batch_validate_items(
        route_file,
        source_file,
        *args,
        **kwargs):
    log.info(_('loading routes from JSON'))
    src_routes = json.load(source_file)['routes']
    log.info(_('parsing routes from route file'))
    parser = RouteParser(route_file)
    current_routes = parser.parse_all()
    log.info(_('batch validating routes'))
    validated = core.validate_routes(current_routes, src_routes)
    result = {'routes': list(validated)}
    return json.dumps(result, indent=2)

def batch_replace_items(
        route_file,
        source_file,
        *args,
        **kwargs):
    log.info(_('loading routes from JSON'))
    src_routes = list(json.load(source_file)['routes'])
    log.info(_('batch replacing routes'))
    write_routes(src_routes, route_file)
    result = {'routes': src_routes}
    return json.dumps(result, indent=2)

def create_or_update_item(
        route_file,
        name,
        ensure,
        gateway,
        interface,
        network,
        netmask,
        options=None,
        *args,
        **kwargs):
    log.debug(_('called `create_or_update_item` with arguments: route_file=%r, '
                'name=%r, ensure=%r, gateway=%r, interface=%r, network=%r, '
                'netmask=%r, options=%r, %r, %r'),
              route_file, name, ensure, gateway, interface, network, netmask,
              options, args, kwargs)
    route = {
        'name': name,
        'ensure': ensure,
        'gateway': gateway,
        'interface': interface,
        'netmask': netmask,
        'network': network,
    }
    if options:
        route['options'] = options
    log.info(_('parsing routes from route file'))
    with RouteParser.open(route_file) as parser:
        current_routes = list(parser.parse_all())
    log.info(_('looking up if the route already exists'))
    existing_routes = list(core.find_existing(current_routes, route))
    if not existing_routes:
        log.info(_('creating a new route %s=%r'), 'name', name)
        current_routes.append(route)
    elif len(existing_routes) > 1:
        raise core.MultipleRoutesFoundError(
            _('Unable to update. More than one route with such name exists.'))
    else:
        existing_route = existing_routes[0]
        old_name = existing_route['name']
        log.info(_('updating existing route %s=%r'), 'name', old_name)
        existing_route.update(route)
    write_routes(current_routes, route_file)
    return json.dumps({'routes': current_routes}, indent=2)

def update_item(
        route_file,
        name,
        ensure,
        gateway,
        interface,
        network,
        netmask,
        options=None,
        *args,
        **kwargs):
    log.debug(_('called `update_item` with arguments: route_file=%r, '
                'name=%r, ensure=%r, gateway=%r, interface=%r, network=%r, '
                'netmask=%r, options=%r, %r, %r'),
              route_file, name, ensure, gateway, interface, network, netmask,
              options, args, kwargs)
    route = {
        'name': name,
        'ensure': ensure,
        'gateway': gateway,
        'interface': interface,
        'netmask': netmask,
        'network': network,
    }
    if options:
        route['options'] = options
    log.info(_('parsing routes from route file'))
    with RouteParser.open(route_file) as parser:
        current_routes = list(parser.parse_all())
    log.info(_('looking up if the route already exists'))
    existing_routes = list(core.find_existing(current_routes, route))
    if not existing_routes:
        raise core.RouteNotFoundError(
            _('Unable to update. No route matching criteria found.'))
    if len(existing_routes) > 1:
        raise core.MultipleRoutesFoundError(
            _('Unable to update. More than one route matching criteria exist.'))
    existing_route = existing_routes[0]
    old_name = existing_route['name']
    log.info(_('updating existing route %s=%r'), 'name', old_name)
    existing_route.update(route)
    write_routes(current_routes, route_file)
    return json.dumps({'routes': current_routes}, indent=2)

def delete_items(
        route_file,
        value,
        key,
        ignore_case,
        exact_match,
        *args,
        **kwargs):
    log.debug(_('called `delete_items` with arguments: route_file=%r, '
                'value=%r, key=%r, ignore_case=%r, exact_match=%r, %r, %r'),
              route_file, value, key, ignore_case, exact_match, args, kwargs)
    with RouteParser.open(route_file) as parser:
        log.info(_('parsing routes from route file'))
        current_routes = list(parser.parse_all())
        log.info(_('filtering out items matching filter: %s=%r'), key, value)
        new_routes = list(core.delete_routes(current_routes, value, key,
                                             ignore_case, exact_match))
    write_routes(new_routes, route_file)
    result = {'routes': new_routes}
    return json.dumps(result, indent=2)
<|endoftext|>


<commit_before>from discord.ext import commands
from .utils import checks
import discord
import io

GUILD_ID = 81883016288276480
VOICE_ROOM_ID = 633466718035116052
GENERAL_VOICE_ID = 81883016309248000

class Funhouse(commands.Cog):
    def __init__(self, bot):
        self.bot = bot

    def is_outside_voice(self, state):
        return state.channel is None or state.channel.id != GENERAL_VOICE_ID

    def is_inside_voice(self, state):
        return state.channel is not None and state.channel.id == GENERAL_VOICE_ID

    @commands.Cog.listener()
    async def on_voice_state_update(self, member, before, after):
        if member.guild.id != GUILD_ID:
            return

        voice_room = member.guild.get_channel(VOICE_ROOM_ID)
        if self.is_outside_voice(before) and self.is_inside_voice(after):
            # joined a channel
            await voice_room.set_permissions(member, read_messages=True)
        elif self.is_outside_voice(after) and self.is_inside_voice(before):
            # left the channel
            await voice_room.set_permissions(member, read_messages=None)

    @commands.command(hidden=True)
    async def cat(self, ctx):
        """Gives you a random cat."""
        async with ctx.session.get('https://aws.random.cat/meow') as resp:
            if resp.status != 200:
                return await ctx.send('No cat found :(')
            js = await resp.json()
            await ctx.send(embed=discord.Embed(title='Random Cat').set_image(url=js['file']))

    @commands.command(hidden=True)
    async def dog(self, ctx):
        """Gives you a random dog."""
        async with ctx.session.get('https://random.dog/woof') as resp:
            if resp.status != 200:
                return await ctx.send('No dog found :(')

            filename = await resp.text()
            url = f'https://random.dog/{filename}'
            if filename.endswith(('.mp4', '.webm')):
                async with ctx.typing():
                    async with ctx.session.get(url) as other:
                        if other.status != 200:
                            return await ctx.send('Could not download dog video :(')

                        fp = io.BytesIO(await other.read())
                        await ctx.send(file=discord.File(fp, filename=filename))
            else:
                await ctx.send(embed=discord.Embed(title='Random Dog').set_image(url=url))

def setup(bot):
    bot.add_cog(Funhouse(bot))
<commit_msg>Check for filesize of dog videos before uploading them.<commit_after>from discord.ext import commands
from .utils import checks
import discord
import io

GUILD_ID = 81883016288276480
VOICE_ROOM_ID = 633466718035116052
GENERAL_VOICE_ID = 81883016309248000

class Funhouse(commands.Cog):
    def __init__(self, bot):
        self.bot = bot

    def is_outside_voice(self, state):
        return state.channel is None or state.channel.id != GENERAL_VOICE_ID

    def is_inside_voice(self, state):
        return state.channel is not None and state.channel.id == GENERAL_VOICE_ID

    @commands.Cog.listener()
    async def on_voice_state_update(self, member, before, after):
        if member.guild.id != GUILD_ID:
            return

        voice_room = member.guild.get_channel(VOICE_ROOM_ID)
        if self.is_outside_voice(before) and self.is_inside_voice(after):
            # joined a channel
            await voice_room.set_permissions(member, read_messages=True)
        elif self.is_outside_voice(after) and self.is_inside_voice(before):
            # left the channel
            await voice_room.set_permissions(member, read_messages=None)

    @commands.command(hidden=True)
    async def cat(self, ctx):
        """Gives you a random cat."""
        async with ctx.session.get('https://aws.random.cat/meow') as resp:
            if resp.status != 200:
                return await ctx.send('No cat found :(')
            js = await resp.json()
            await ctx.send(embed=discord.Embed(title='Random Cat').set_image(url=js['file']))

    @commands.command(hidden=True)
    async def dog(self, ctx):
        """Gives you a random dog."""
        async with ctx.session.get('https://random.dog/woof') as resp:
            if resp.status != 200:
                return await ctx.send('No dog found :(')

            filename = await resp.text()
            url = f'https://random.dog/{filename}'
            filesize = ctx.guild.filesize_limit if ctx.guild else 8388608
            if filename.endswith(('.mp4', '.webm')):
                async with ctx.typing():
                    async with ctx.session.get(url) as other:
                        if other.status != 200:
                            return await ctx.send('Could not download dog video :(')

                        if int(other.headers['Content-Length']) >= filesize:
                            return await ctx.send(f'Video was too big to upload... See it here: {url} instead.')

                        fp = io.BytesIO(await other.read())
                        await ctx.send(file=discord.File(fp, filename=filename))
            else:
                await ctx.send(embed=discord.Embed(title='Random Dog').set_image(url=url))

def setup(bot):
    bot.add_cog(Funhouse(bot))
<|endoftext|>


<commit_before>#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# vim: set et sw=4 ts=4 sts=4 ff=unix fenc=utf8:
# Author: Binux<i@binux.me>
#         http://binux.me
# Created on 2014-11-17 19:17:12

__version__ = '0.3.6'
<commit_msg>start of version 0.3.7<commit_after>#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# vim: set et sw=4 ts=4 sts=4 ff=unix fenc=utf8:
# Author: Binux<i@binux.me>
#         http://binux.me
# Created on 2014-11-17 19:17:12

__version__ = '0.3.7'
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
import logging
from datetime import datetime

import scrapy
from scrapy_redis.spiders import RedisSpider

from items import StoreItem

class StoreSpider(RedisSpider):
    name = "store"
    allowed_domains = ["aliexpress.com"]
    start_urls = (
        'http://www.aliexpress.com/',
    )

    prefix = ''

    def start_requests(self):
        StoreSpider.prefix = self.settings['prefix']
        self.redis_key = '{}:store'.format(StoreSpider.prefix)

        yield self.next_request()

    def parse(self, response):
        self.log('request store: {}'.format(response.url), logging.INFO)

        owner_member_id = response.css('.s-alitalk').xpath('a/@data-id1').extract()[0]
        evaluation_detail_url = 'http://feedback.aliexpress.com/display/evaluationDetail.htm?ownerMemberId={}'.format(owner_member_id)
        return scrapy.Request(url=evaluation_detail_url, callback=self.parse_evaluation_detail)

    def parse_evaluation_detail(self, response):
        summary_tb_tds = response.xpath('//div[@id="feedback-summary"]/div/table/tbody/tr/td')
        store_name = summary_tb_tds[0].xpath('a/text()').extract()[0]
        store_url = summary_tb_tds[0].xpath('a/@href').extract()[0]
        store_positive_feedback = summary_tb_tds[1].xpath('span/text()').extract()[0]
        store_positive_score = int(summary_tb_tds[2].xpath('span/text()').extract()[0].replace(',', ''))
        store_since_time = datetime.strptime(summary_tb_tds[3].xpath('text()').extract()[0].strip(), '%d %b %Y')

        history_tds = response.xpath('//div[@id="feedback-history"]/div/table/tbody/tr/td/a/text()').extract()
        one_month_feedback = [int(td.strip().replace(',', '').replace('-', '0')) for td in history_tds[::5]]
        three_month_feedback = [int(td.strip().replace(',', '').replace('-', '0')) for td in history_tds[1::5]]
        six_month_feedback = [int(td.strip().replace(',', '').replace('-', '0')) for td in history_tds[2::5]]
        twelve_month_feedback = [int(td.strip().replace(',', '').replace('-', '0')) for td in history_tds[3::5]]
        overall_feedback = [int(td.strip().replace(',', '').replace('-', '0')) for td in history_tds[4::5]]

        store_id = store_url[store_url.index('/') + 1:]

        item = StoreItem()
        item['prefix'] = StoreSpider.prefix
        item['_id'] = store_id
        item['url'] = store_url
        item['name'] = store_name
        item['positive_feedback'] = store_positive_feedback
        item['positive_score'] = store_positive_score
        item['since_time'] = store_since_time
        item['one_month_feedback'] = one_month_feedback
        item['three_month_feedback'] = three_month_feedback
        item['six_month_feedback'] = six_month_feedback
        item['twelve_month_feedback'] = twelve_month_feedback
        item['overall_feedback'] = overall_feedback

        all_product_url = 'http://www.aliexpress.com/store/all-wholesale-products/{}.html'.format(store_id)

        return scrapy.Request(all_product_url, meta={'item': item}, callback=self.parse_product_num)

    def parse_product_num(self, response):
        item = response.meta['item']

        product_num = int(response.xpath('//div[@id="result-info"]/strong/text()').extract()[0])
        item['product'] = product_num

        return item
<commit_msg>抓取店铺的产品数量，改正bug<commit_after># -*- coding: utf-8 -*-
import logging
from datetime import datetime

import scrapy
from scrapy_redis.spiders import RedisSpider

from items import StoreItem

class StoreSpider(RedisSpider):
    name = "store"
    allowed_domains = ["aliexpress.com"]
    start_urls = (
        'http://www.aliexpress.com/',
    )

    prefix = ''

    def start_requests(self):
        StoreSpider.prefix = self.settings['prefix']
        self.redis_key = '{}:store'.format(StoreSpider.prefix)

        yield self.next_request()

    def parse(self, response):
        self.log('request store: {}'.format(response.url), logging.INFO)

        owner_member_id = response.css('.s-alitalk').xpath('a/@data-id1').extract()[0]
        evaluation_detail_url = 'http://feedback.aliexpress.com/display/evaluationDetail.htm?ownerMemberId={}'.format(owner_member_id)
        return scrapy.Request(url=evaluation_detail_url, callback=self.parse_evaluation_detail)

    def parse_evaluation_detail(self, response):
        summary_tb_tds = response.xpath('//div[@id="feedback-summary"]/div/table/tbody/tr/td')
        store_name = summary_tb_tds[0].xpath('a/text()').extract()[0]
        store_url = summary_tb_tds[0].xpath('a/@href').extract()[0]
        store_positive_feedback = summary_tb_tds[1].xpath('span/text()').extract()[0]
        store_positive_score = int(summary_tb_tds[2].xpath('span/text()').extract()[0].replace(',', ''))
        store_since_time = datetime.strptime(summary_tb_tds[3].xpath('text()').extract()[0].strip(), '%d %b %Y')

        history_tds = response.xpath('//div[@id="feedback-history"]/div/table/tbody/tr/td/a/text()').extract()
        one_month_feedback = [int(td.strip().replace(',', '').replace('-', '0')) for td in history_tds[::5]]
        three_month_feedback = [int(td.strip().replace(',', '').replace('-', '0')) for td in history_tds[1::5]]
        six_month_feedback = [int(td.strip().replace(',', '').replace('-', '0')) for td in history_tds[2::5]]
        twelve_month_feedback = [int(td.strip().replace(',', '').replace('-', '0')) for td in history_tds[3::5]]
        overall_feedback = [int(td.strip().replace(',', '').replace('-', '0')) for td in history_tds[4::5]]

        store_id = store_url.split('/')[-1]

        item = StoreItem()
        item['prefix'] = StoreSpider.prefix
        item['_id'] = store_id
        item['url'] = store_url
        item['name'] = store_name
        item['positive_feedback'] = store_positive_feedback
        item['positive_score'] = store_positive_score
        item['since_time'] = store_since_time
        item['one_month_feedback'] = one_month_feedback
        item['three_month_feedback'] = three_month_feedback
        item['six_month_feedback'] = six_month_feedback
        item['twelve_month_feedback'] = twelve_month_feedback
        item['overall_feedback'] = overall_feedback

        all_product_url = 'http://www.aliexpress.com/store/all-wholesale-products/{}.html'.format(store_id)

        return scrapy.Request(all_product_url, meta={'item': item}, callback=self.parse_product_num)

    def parse_product_num(self, response):
        item = response.meta['item']

        product_num = int(response.xpath('//div[@id="result-info"]/strong/text()').extract()[0])
        item['product'] = product_num

        return item
<|endoftext|>


<commit_before>from OrderedSet import OrderedSet
from HashMap import HashMap

class MoleculeType(object):
    """An abstract container for molecules of one type
    """
    __slots__ = ['name', 'moleculeSet', 'bondForceSet', 'pairForceSet', 'angleForceSet',
            'dihedralForceSet', 'torsiontorsionForceSet', 'constraints', 'exclusions',
            'settles', 'nrexcl']
    def __init__(self, name):
        """Initialize the MoleculeType container

        Args:
            name (str): the name of the moleculetype to add
        """
        self.name = name
        self.moleculeSet = OrderedSet()
        self.bondForceSet = HashMap()
        self.pairForceSet = HashMap()
        self.angleForceSet = HashMap()
        self.dihedralForceSet = HashMap()
        self.torsiontorsionForceSet = HashMap()
        self.constraints = HashMap()
        self.exclusions = HashMap()
        self.settles = None
        self.nrexcl = None

    def addMolecule(self, molecule):
        """Add a molecule into the moleculetype container

        Args:
            molecule (Molecule): the molecule to append
        """
        self.moleculeSet.add(molecule)

    def removeMolecule(self, molecule):
        """Remove a molecule from the system.

        Args:
            molecule (Molecule): remove a molecule from the moleculeType
        """
        self.moleculeSet.remove(molecule)

    def getMolecule(self, molecule):
        """Get a molecule from the system

        Args:
            molecule (Molecule): retrieve an equivalent molecule from the moleculetype
        """
        return get_equivalent(self.moleculeSet, molecule, False)

    def addForce(self, force):
        """Add a force to the moleculeType

        Args:
            force (AbstractForce): Add a force or contraint to the moleculeType
        """
        self.forceSet.add(force)

    def removeForce(self, force):
        """Remove a force from the moleculeType

        Args:
            force (AbstractForce): Remove a force from the moleculeType
        """
        self.forceSet.remove(force)

    def getForce(self, force):
        """Get a force from the moleculeType

        Args:
            force (AbstractForce): Retrieve a force from the moleculeType
        """
        return get_equivalent(self.forceSet, force, False)

    def setNrexcl(self, nrexcl):
        """Set the nrexcl

        Args:
            nrexcl (int): the value for nrexcl
        """
        self.nrexcl = nrexcl

    def getNrexcl(self):
        """Gets the nrexcl
        """
        return self.nrexcl
<commit_msg>removed slots from MoleculeType - negligible memory improvement at the cost of some flexibility during development<commit_after>from OrderedSet import OrderedSet
from HashMap import HashMap

class MoleculeType(object):
    """An abstract container for molecules of one type
    """
    def __init__(self, name):
        """Initialize the MoleculeType container

        Args:
            name (str): the name of the moleculetype to add
        """
        self.name = name
        self.moleculeSet = OrderedSet()
        self.bondForceSet = HashMap()
        self.pairForceSet = HashMap()
        self.angleForceSet = HashMap()
        self.dihedralForceSet = HashMap()
        self.torsiontorsionForceSet = HashMap()
        self.constraints = HashMap()
        self.exclusions = HashMap()
        self.settles = None
        self.nrexcl = None

    def addMolecule(self, molecule):
        """Add a molecule into the moleculetype container

        Args:
            molecule (Molecule): the molecule to append
        """
        self.moleculeSet.add(molecule)

    def removeMolecule(self, molecule):
        """Remove a molecule from the system.

        Args:
            molecule (Molecule): remove a molecule from the moleculeType
        """
        self.moleculeSet.remove(molecule)

    def getMolecule(self, molecule):
        """Get a molecule from the system

        Args:
            molecule (Molecule): retrieve an equivalent molecule from the moleculetype
        """
        return get_equivalent(self.moleculeSet, molecule, False)

    def addForce(self, force):
        """Add a force to the moleculeType

        Args:
            force (AbstractForce): Add a force or contraint to the moleculeType
        """
        self.forceSet.add(force)

    def removeForce(self, force):
        """Remove a force from the moleculeType

        Args:
            force (AbstractForce): Remove a force from the moleculeType
        """
        self.forceSet.remove(force)

    def getForce(self, force):
        """Get a force from the moleculeType

        Args:
            force (AbstractForce): Retrieve a force from the moleculeType
        """
        return get_equivalent(self.forceSet, force, False)

    def setNrexcl(self, nrexcl):
        """Set the nrexcl

        Args:
            nrexcl (int): the value for nrexcl
        """
        self.nrexcl = nrexcl

    def getNrexcl(self):
        """Gets the nrexcl
        """
        return self.nrexcl
<|endoftext|>


<commit_before>from discord.ext import commands
from .utils import checks
import discord

GUILD_ID = 81883016288276480
VOICE_ROOM_ID = 633466718035116052
GENERAL_VOICE_ID = 81883016309248000

class Funhouse(commands.Cog):
    def __init__(self, bot):
        self.bot = bot

    def is_outside_voice(self, state):
        return state.channel is None or state.channel.id != GENERAL_VOICE_ID

    def is_inside_voice(self, state):
        return state.channel is not None and state.channel.id == GENERAL_VOICE_ID

    @commands.Cog.listener()
    async def on_voice_state_update(self, member, before, after):
        if member.guild.id != GUILD_ID:
            return

        voice_room = member.guild.get_channel(VOICE_ROOM_ID)
        if self.is_outside_voice(before) and self.is_inside_voice(after):
            # joined a channel
            await voice_room.set_permissions(member, read_messages=True)
        elif self.is_outside_voice(after) and self.is_inside_voice(before):
            # left the channel
            await voice_room.set_permissions(member, read_messages=False)

    @commands.command(hidden=True)
    async def cat(self, ctx):
        """Gives you a random cat."""
        async with ctx.session.get('https://aws.random.cat/meow') as resp:
            if resp.status != 200:
                return await ctx.send('No cat found :(')
            js = await resp.json()
            await ctx.send(embed=discord.Embed(title='Random Cat').set_image(url=js['file']))

def setup(bot):
    bot.add_cog(Funhouse(bot))
<commit_msg>Clear rather than deny permissions when leaving.<commit_after>from discord.ext import commands
from .utils import checks
import discord

GUILD_ID = 81883016288276480
VOICE_ROOM_ID = 633466718035116052
GENERAL_VOICE_ID = 81883016309248000

class Funhouse(commands.Cog):
    def __init__(self, bot):
        self.bot = bot

    def is_outside_voice(self, state):
        return state.channel is None or state.channel.id != GENERAL_VOICE_ID

    def is_inside_voice(self, state):
        return state.channel is not None and state.channel.id == GENERAL_VOICE_ID

    @commands.Cog.listener()
    async def on_voice_state_update(self, member, before, after):
        if member.guild.id != GUILD_ID:
            return

        voice_room = member.guild.get_channel(VOICE_ROOM_ID)
        if self.is_outside_voice(before) and self.is_inside_voice(after):
            # joined a channel
            await voice_room.set_permissions(member, read_messages=True)
        elif self.is_outside_voice(after) and self.is_inside_voice(before):
            # left the channel
            await voice_room.set_permissions(member, read_messages=None)

    @commands.command(hidden=True)
    async def cat(self, ctx):
        """Gives you a random cat."""
        async with ctx.session.get('https://aws.random.cat/meow') as resp:
            if resp.status != 200:
                return await ctx.send('No cat found :(')
            js = await resp.json()
            await ctx.send(embed=discord.Embed(title='Random Cat').set_image(url=js['file']))

def setup(bot):
    bot.add_cog(Funhouse(bot))
<|endoftext|>


<commit_before>'''classes to ease the creation of html documents'''

import xml.etree.ElementTree as ET

class TagBase(ET.Element):
    "base class for all tags"

    SELF_CLOSING = False

    def __init__(self, childs, attrs):
        "add childs and call parent constructor"
        clean_attrs = dict([(key.rstrip("_"), val)
            for (key, val) in attrs.iteritems()])
        tag = self.__class__.__name__.lower()

        ET.Element.__init__(self, tag, clean_attrs)

        for child in childs:
            self.append(child)

    def format(self, level=0, increment=1):
        "pretty print the object"
        one_indent = " " * increment
        indent = one_indent * level

        attrs = " ".join(['%s="%s"' % (name, val)
            for (name, val) in self.attrib.iteritems()])
        if attrs:
            attrs = " " + attrs

        if self.__class__.SELF_CLOSING:
            return "%s<%s%s />" % (indent, self.tag, attrs)
        else:

            def format_child(child):
                """transform childs to string representations"""
                if isinstance(child, TagBase):
                    return child.format(level + 1, increment)
                else:
                    return indent + one_indent + str(child)

            childs = "\n".join([format_child(child)
                for child in list(self)])

            if childs:
                childs = "\n" + childs + "\n"

            return "%s<%s%s>%s%s</%s>" % (indent, self.tag, attrs,
                    childs, indent, self.tag)

    def __str__(self):
        "return a string representation"
        return self.format(0)

TAGS = {
    "a": (False, "Defines a hyperlink"),
    "abbr": (False, "Defines an abbreviation"),
    "address": (False, "Defines an address element"),
    "area": (False, "Defines an area inside an image map"),
    "article": (False, "Defines an article"),
    "aside": (False, "Defines content aside from the page content"),
    "audio": (False, "Defines sound content"),
    "b": (False, "Defines bold text"),
    "base": (False, "Defines a base URL for all the links in a page"),
    "bdi": (False, "Defines text that is isolated from its surrounding"
        "text direction settings"),
    "bdo": (False, "Defines the direction of text display"),
    "blockquote": (False, "Defines a long quotation"),
    "body": (False, "Defines the body element"),
    "br": (True, "Inserts a single line break"),
    "button": (False, "Defines a push button"),
    "canvas": (False, "Defines graphics"),
    "caption": (False, "Defines a table caption"),
    "cite": (False, "Defines a citation"),
    "code": (False, "Defines computer code text"),
    "col": (False, "Defines attributes for table columns "),
    "colgroup": (False, "Defines groups of table columns"),
    "command": (False, "Defines a command button"),
    "datalist": (False, "Defines a list of options for an input field"),
    "dd": (False, "Defines a definition description"),
    "del": (False, "Defines deleted text"),
    "details": (False, "Defines details of an element"),
    "dfn": (False, "Defines a definition term"),
    "div": (False, "Defines a section in a document"),
    "dl": (False, "Defines a definition list"),
    "dt": (False, "Defines a definition term"),
    "em": (False, "Defines emphasized text "),
    "embed": (False, "Defines external interactive content or plugin"),
    "fieldset": (False, "Defines a fieldset"),
    "figcaption": (False, "Defines the caption of a figure element"),
    "figure": (False, "Defines a group of media content, and their caption"),
    "footer": (False, "Defines a footer for a section or page"),
    "form": (False, "Defines a form "),
    "h1": (False, "Defines header level 1"),
    "h2": (False, "Defines header level 2"),
    "h3": (False, "Defines header level 3"),
    "h4": (False, "Defines header level 4"),
    "h5": (False, "Defines header level 5"),
    "h6": (False, "Defines header level 6"),
    "head": (False, "Defines information about the document"),
    "header": (False, "Defines a header for a section or page"),
    "hgroup": (False, "Defines information about a section in a document"),
    "hr": (True, "Defines a horizontal rule"),
    "html": (False, "Defines an html document"),
    "i": (False, "Defines italic text"),
    "iframe": (False, "Defines an inline sub window (frame)"),
    "img": (True, "Defines an image"),
    "input": (True, "Defines an input field"),
    "ins": (False, "Defines inserted text"),
    "keygen": (False, "Defines a key pair generator field (for forms)"),
    "kbd": (False, "Defines keyboard text"),
    "label": (False, "Defines a label for a form control"),
    "legend": (False, "Defines a title in a fieldset"),
    "li": (False, "Defines a list item"),
    "link": (False, "Defines a resource reference"),
    "map": (False, "Defines an image map "),
    "mark": (False, "Defines marked text"),
    "menu": (False, "Defines a menu list"),
    "meta": (True, "Defines meta information"),
    "meter": (False, "Defines a scalar measurement within a known range"),
    "nav": (False, "Defines navigation links"),
    "noscript": (False, "Defines a noscript section"),
    "object": (False, "Defines an embedded object"),
    "ol": (False, "Defines an ordered list"),
    "optgroup": (False, "Defines an option group"),
    "option": (False, "Defines an option in a drop-down list"),
    "output": (False, "Defines the result of a calculation"),
    "p": (False, "Defines a paragraph"),
    "param": (False, "Defines a parameter for an object"),
    "pre": (False, "Defines preformatted text"),
    "progress": (False, "Represents the progress of a task"),
    "q": (False, "Defines a short quotation"),
    "rp": (False, "Used in ruby annotations to define what to show if a "
            "browser does not support the ruby element"),
    "rt": (False, "Defines explanation to ruby annotations"),
    "ruby": (False, "Defines ruby annotations"),
    "s": (False, "Defines text that is no longer correct"),
    "samp": (False, "Defines sample computer code"),
    "script": (False, "Defines a script"),
    "section": (False, "Defines a section"),
    "select": (False, "Defines a selectable list"),
    "small": (False, "Defines smaller text"),
    "source": (False, "Defines multiple media resources for media elements, "
            "such as audio and video"),
    "span": (False, "Defines a section in a document"),
    "strong": (False, "Defines strong text"),
    "style": (False, "Defines a style definition"),
    "sub": (False, "Defines subscripted text"),
    "summary": (False, "Defines the header of a 'detail' element"),
    "sup": (False, "Defines superscripted text"),
    "table": (False, "Defines a table"),
    "tbody": (False, "Defines a table body"),
    "td": (False, "Defines a table cell"),
    "textarea": (False, "Defines a text area"),
    "tfoot": (False, "Defines a table footer"),
    "th": (False, "Defines a table header"),
    "thead": (False, "Defines a table header"),
    "time": (False, "Defines a date/time"),
    "title": (False, "Defines the document title"),
    "tr": (False, "Defines a table row"),
    "track": (False, "Defines text tracks used in mediaplayers"),
    "ul": (False, "Defines an unordered list"),
    "var": (False, "Defines a variable"),
    "video": (False, "Defines a video or movie"),
    "wbr": (False, "Defines a possible line-break")
}

def create_tags(ctx):
    "create all classes and put them in ctx"

    for (tag, info) in TAGS.iteritems():
        class_name = tag.title()
        self_closing, docs = info

        def __init__(self, *childs, **attrs):
            TagBase.__init__(self, childs, attrs)

        cls = type(class_name, (TagBase,), {
            "__doc__": docs,
            "__init__": __init__
        })

        cls.SELF_CLOSING = self_closing

        ctx[class_name] = cls

create_tags(globals())
<commit_msg>quote string content<commit_after>'''classes to ease the creation of html documents'''

import xml.etree.ElementTree as ET

def quote(text):
    """encode html entities"""
    text = unicode(text)
    return text.translate({
        ord('&'): u'&',
        ord('<'): u'<',
        ord('"'): u'"',
        ord('>'): u'>',
        ord('@'): u'@',
        0xa0: u' '})

class TagBase(ET.Element):
    "base class for all tags"

    SELF_CLOSING = False

    def __init__(self, childs, attrs):
        "add childs and call parent constructor"
        clean_attrs = dict([(key.rstrip("_"), val)
            for (key, val) in attrs.iteritems()])
        tag = self.__class__.__name__.lower()

        ET.Element.__init__(self, tag, clean_attrs)

        for child in childs:
            self.append(child)

    def format(self, level=0, increment=1):
        "pretty print the object"
        one_indent = " " * increment
        indent = one_indent * level

        attrs = " ".join(['%s="%s"' % (name, val)
            for (name, val) in self.attrib.iteritems()])
        if attrs:
            attrs = " " + attrs

        if self.__class__.SELF_CLOSING:
            return "%s<%s%s />" % (indent, self.tag, attrs)
        else:

            def format_child(child):
                """transform childs to string representations"""
                if isinstance(child, TagBase):
                    return child.format(level + 1, increment)
                else:
                    return indent + one_indent + quote(str(child))

            childs = "\n".join([format_child(child)
                for child in list(self)])

            if childs:
                childs = "\n" + childs + "\n"

            return "%s<%s%s>%s%s</%s>" % (indent, self.tag, attrs,
                    childs, indent, self.tag)

    def __str__(self):
        "return a string representation"
        return self.format(0)

TAGS = {
    "a": (False, "Defines a hyperlink"),
    "abbr": (False, "Defines an abbreviation"),
    "address": (False, "Defines an address element"),
    "area": (False, "Defines an area inside an image map"),
    "article": (False, "Defines an article"),
    "aside": (False, "Defines content aside from the page content"),
    "audio": (False, "Defines sound content"),
    "b": (False, "Defines bold text"),
    "base": (False, "Defines a base URL for all the links in a page"),
    "bdi": (False, "Defines text that is isolated from its surrounding"
        "text direction settings"),
    "bdo": (False, "Defines the direction of text display"),
    "blockquote": (False, "Defines a long quotation"),
    "body": (False, "Defines the body element"),
    "br": (True, "Inserts a single line break"),
    "button": (False, "Defines a push button"),
    "canvas": (False, "Defines graphics"),
    "caption": (False, "Defines a table caption"),
    "cite": (False, "Defines a citation"),
    "code": (False, "Defines computer code text"),
    "col": (False, "Defines attributes for table columns "),
    "colgroup": (False, "Defines groups of table columns"),
    "command": (False, "Defines a command button"),
    "datalist": (False, "Defines a list of options for an input field"),
    "dd": (False, "Defines a definition description"),
    "del": (False, "Defines deleted text"),
    "details": (False, "Defines details of an element"),
    "dfn": (False, "Defines a definition term"),
    "div": (False, "Defines a section in a document"),
    "dl": (False, "Defines a definition list"),
    "dt": (False, "Defines a definition term"),
    "em": (False, "Defines emphasized text "),
    "embed": (False, "Defines external interactive content or plugin"),
    "fieldset": (False, "Defines a fieldset"),
    "figcaption": (False, "Defines the caption of a figure element"),
    "figure": (False, "Defines a group of media content, and their caption"),
    "footer": (False, "Defines a footer for a section or page"),
    "form": (False, "Defines a form "),
    "h1": (False, "Defines header level 1"),
    "h2": (False, "Defines header level 2"),
    "h3": (False, "Defines header level 3"),
    "h4": (False, "Defines header level 4"),
    "h5": (False, "Defines header level 5"),
    "h6": (False, "Defines header level 6"),
    "head": (False, "Defines information about the document"),
    "header": (False, "Defines a header for a section or page"),
    "hgroup": (False, "Defines information about a section in a document"),
    "hr": (True, "Defines a horizontal rule"),
    "html": (False, "Defines an html document"),
    "i": (False, "Defines italic text"),
    "iframe": (False, "Defines an inline sub window (frame)"),
    "img": (True, "Defines an image"),
    "input": (True, "Defines an input field"),
    "ins": (False, "Defines inserted text"),
    "keygen": (False, "Defines a key pair generator field (for forms)"),
    "kbd": (False, "Defines keyboard text"),
    "label": (False, "Defines a label for a form control"),
    "legend": (False, "Defines a title in a fieldset"),
    "li": (False, "Defines a list item"),
    "link": (False, "Defines a resource reference"),
    "map": (False, "Defines an image map "),
    "mark": (False, "Defines marked text"),
    "menu": (False, "Defines a menu list"),
    "meta": (True, "Defines meta information"),
    "meter": (False, "Defines a scalar measurement within a known range"),
    "nav": (False, "Defines navigation links"),
    "noscript": (False, "Defines a noscript section"),
    "object": (False, "Defines an embedded object"),
    "ol": (False, "Defines an ordered list"),
    "optgroup": (False, "Defines an option group"),
    "option": (False, "Defines an option in a drop-down list"),
    "output": (False, "Defines the result of a calculation"),
    "p": (False, "Defines a paragraph"),
    "param": (False, "Defines a parameter for an object"),
    "pre": (False, "Defines preformatted text"),
    "progress": (False, "Represents the progress of a task"),
    "q": (False, "Defines a short quotation"),
    "rp": (False, "Used in ruby annotations to define what to show if a "
            "browser does not support the ruby element"),
    "rt": (False, "Defines explanation to ruby annotations"),
    "ruby": (False, "Defines ruby annotations"),
    "s": (False, "Defines text that is no longer correct"),
    "samp": (False, "Defines sample computer code"),
    "script": (False, "Defines a script"),
    "section": (False, "Defines a section"),
    "select": (False, "Defines a selectable list"),
    "small": (False, "Defines smaller text"),
    "source": (False, "Defines multiple media resources for media elements, "
            "such as audio and video"),
    "span": (False, "Defines a section in a document"),
    "strong": (False, "Defines strong text"),
    "style": (False, "Defines a style definition"),
    "sub": (False, "Defines subscripted text"),
    "summary": (False, "Defines the header of a 'detail' element"),
    "sup": (False, "Defines superscripted text"),
    "table": (False, "Defines a table"),
    "tbody": (False, "Defines a table body"),
    "td": (False, "Defines a table cell"),
    "textarea": (False, "Defines a text area"),
    "tfoot": (False, "Defines a table footer"),
    "th": (False, "Defines a table header"),
    "thead": (False, "Defines a table header"),
    "time": (False, "Defines a date/time"),
    "title": (False, "Defines the document title"),
    "tr": (False, "Defines a table row"),
    "track": (False, "Defines text tracks used in mediaplayers"),
    "ul": (False, "Defines an unordered list"),
    "var": (False, "Defines a variable"),
    "video": (False, "Defines a video or movie"),
    "wbr": (False, "Defines a possible line-break")
}

def create_tags(ctx):
    "create all classes and put them in ctx"

    for (tag, info) in TAGS.iteritems():
        class_name = tag.title()
        self_closing, docs = info

        def __init__(self, *childs, **attrs):
            TagBase.__init__(self, childs, attrs)

        cls = type(class_name, (TagBase,), {
            "__doc__": docs,
            "__init__": __init__
        })

        cls.SELF_CLOSING = self_closing

        ctx[class_name] = cls

create_tags(globals())
<|endoftext|>


<commit_before># Copyright 2019 The resource-policy-evaluation-library Authors. All rights reserved.
#
# 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.

from .base import Resource
from rpe.exceptions import is_retryable_exception
import tenacity
from googleapiclienthelpers.discovery import build_subresource
from googleapiclienthelpers.waiter import Waiter

class GoogleAPIResource(Resource):

    # Names of the get and update methods. Most are the same but override in
    # the Resource if necessary
    resource_property = None
    get_method = "get"
    update_method = "update"

    # If a resource is not in a ready state, we can't update it. If we retrieve
    # it, and the state changes, updates will be rejected because the ETAG will
    # have changed. If a resource defines readiness criteria, the get() call
    # will wait until the resource is in a ready state to return
    #
    # Key/Value to check to see if a resource is ready
    readiness_key = None
    readiness_value = None

    def __init__(self, resource_data, **kwargs):
        full_resource_path = "{}.{}".format(
            self.service_name,
            self.resource_path
        )

        self.service = build_subresource(
            full_resource_path,
            self.version,
            **kwargs
        )

        # If we are a property of a resource, also get the resource we're
        # associated with
        if self.is_property():
            # Build resource data for the parent
            parent_data = resource_data.copy()
            parent_type = resource_data['resource_type'].rsplit('.', 1)[0]
            parent_data['resource_type'] = parent_type

            self.parent_resource = GoogleAPIResource.factory(
                parent_data,
                **kwargs
            )

        self.resource_data = resource_data

    def is_property(self):
        return self.resource_property is not None

    @staticmethod
    def factory(resource_data, **kargs):
        resource_type_map = {
            'bigquery.datasets': GcpBigqueryDataset,
            'compute.instances': GcpComputeInstance,
            'cloudresourcemanager.projects': GcpProject,
            'cloudresourcemanager.projects.iam': GcpProjectIam,
            'pubsub.projects.subscriptions': GcpPubsubSubscription,
            'pubsub.projects.subscriptions.iam': GcpPubsubSubscriptionIam,
            'pubsub.projects.topics': GcpPubsubTopic,
            'pubsub.projects.topics.iam': GcpPubsubTopicIam,
            'sqladmin.instances': GcpSqlInstance,
            'storage.buckets': GcpStorageBucket,
            'storage.buckets.iam': GcpStorageBucketIamPolicy
        }

        resource_type = resource_data.get('resource_type')
        if not resource_type:
            assert 0, 'Unrecognized resource'

        if resource_type not in resource_type_map:
            assert 0, 'Unrecognized resource'

        cls = resource_type_map.get(resource_type)
        return cls(resource_data, **kargs)

    def type(self):
        type_components = ["gcp", self.service_name, self.resource_path]

        # Things like IAM policy are not separate resources, but rather
        # properties of a resource. We may want to evaluate policy on these
        # properties, so we represent them as resources and need to distinguish
        # them in the resource type.
        if self.is_property():
            type_components.append(self.resource_property)

        return ".".join(type_components)

    def get(self):
        method = getattr(self.service, self.get_method)

        # If the resource has readiness criteria, wait for it
        if self.readiness_key and self.readiness_value:
            waiter = Waiter(method, **self._get_request_args())
            asset = waiter.wait(
                self.readiness_key,
                self.readiness_value,
                interval=7,
                retries=60
            )
        else:
            asset = method(**self._get_request_args()).execute()

        # if this asset is a property, inject its parent
        if self.is_property():
            parent = self.parent_resource.get()
            asset['_resource'] = parent
        return asset

    @tenacity.retry(
        retry=tenacity.retry_if_exception(is_retryable_exception),
        wait=tenacity.wait_random_exponential(multiplier=1, max=10),
        stop=tenacity.stop_after_attempt(10)
    )
    def update(self, body):

        # remove injected data before attempting update
        if self.is_property() and '_resource' in body:
            del body['_resource']

        method = getattr(self.service, self.update_method)
        return method(**self._update_request_args(body)).execute()

class GcpBigqueryDataset(GoogleAPIResource):

    service_name = "bigquery"
    resource_path = "datasets"
    version = "v2"

    def _get_request_args(self):
        return {
            'datasetId': self.resource_data['resource_name'],
            'projectId': self.resource_data['project_id']
        }

    def _update_request_args(self, body):
        return {
            'datasetId': self.resource_data['resource_name'],
            'projectId': self.resource_data['project_id'],
            'body': body
        }

class GcpComputeInstance(GoogleAPIResource):

    service_name = "compute"
    resource_path = "instances"
    version = "v1"

    def _get_request_args(self):
        return {
            'instance': self.resource_data['resource_name'],
            'zone': self.resource_data['resource_location'],
            'project': self.resource_data['project_id']
        }

    def _update_request_args(self, body):
        return {
            'instance': self.resource_data['resource_name'],
            'zone': self.resource_data['resource_location'],
            'project': self.resource_data['project_id']
        }

class GcpPubsubSubscription(GoogleAPIResource):

    service_name = "pubsub"
    resource_path = "projects.subscriptions"
    version = "v1"
    update_method = "patch"

    def _get_request_args(self):
        return {
            'subscription': 'projects/{}/subscriptions/{}'.format(
                self.resource_data['project_id'],
                self.resource_data['resource_name']
            )
        }

    def _update_request_args(self, body):
        return {
            'name': 'projects/{}/subscriptions/{}'.format(
                self.resource_data['project_id'],
                self.resource_data['resource_name']
            ),
            'body': {
                'subscription': body,
                'updateMask': 'labels,ack_deadline_seconds,push_config,message_retention_duration,retain_acked_messages,expiration_policy'
            }
        }

class GcpPubsubSubscriptionIam(GcpPubsubSubscription):

    resource_property = 'iam'
    get_method = "getIamPolicy"
    update_method = "setIamPolicy"

    def _get_request_args(self):
        return {
            'resource': 'projects/{}/subscriptions/{}'.format(
                self.resource_data['project_id'],
                self.resource_data['resource_name']
            )
        }

    def _update_request_args(self, body):
        return {
            'resource': 'projects/{}/subscriptions/{}'.format(
                self.resource_data['project_id'],
                self.resource_data['resource_name']
            ),
            'body': {
                'policy': body
            }
        }

class GcpPubsubTopic(GoogleAPIResource):

    service_name = "pubsub"
    resource_path = "projects.topics"
    version = "v1"
    update_method = "patch"

    def _get_request_args(self):
        return {
            'topic': 'projects/{}/topics/{}'.format(
                self.resource_data['project_id'],
                self.resource_data['resource_name']
            )
        }

    def _update_request_args(self, body):
        return {
            'name': 'projects/{}/topics/{}'.format(
                self.resource_data['project_id'],
                self.resource_data['resource_name']
            ),
            'body': {
                'topic': body,
                # the name field is immutable
                'updateMask': 'labels'
            }
        }

class GcpPubsubTopicIam(GcpPubsubTopic):

    resource_property = "iam"
    get_method = "getIamPolicy"
    update_method = "setIamPolicy"

    def _get_request_args(self):
        return {
            'resource': 'projects/{}/topics/{}'.format(
                self.resource_data['project_id'],
                self.resource_data['resource_name']
            )
        }

    def _update_request_args(self, body):
        return {
            'resource': 'projects/{}/topics/{}'.format(
                self.resource_data['project_id'],
                self.resource_data['resource_name']
            ),
            'body': {
                'policy': body
            }
        }

class GcpStorageBucket(GoogleAPIResource):

    service_name = "storage"
    resource_path = "buckets"
    version = "v1"

    def _get_request_args(self):
        return {
            'bucket': self.resource_data['resource_name'],
            'userProject': self.resource_data['project_id']
        }

    def _update_request_args(self, body):
        return {
            'bucket': self.resource_data['resource_name'],
            'userProject': self.resource_data['project_id'],
            'body': body
        }

class GcpStorageBucketIamPolicy(GcpStorageBucket):

    resource_property = "iam"
    get_method = "getIamPolicy"
    update_method = "setIamPolicy"

class GcpSqlInstance(GoogleAPIResource):

    service_name = "sqladmin"
    resource_path = "instances"
    version = "v1beta4"
    readiness_key = 'state'
    readiness_value = 'RUNNABLE'

    def _get_request_args(self):
        return {
            'instance': self.resource_data['resource_name'],
            'project': self.resource_data['project_id']
        }

    def _update_request_args(self, body):
        return {
            'instance': self.resource_data['resource_name'],
            'project': self.resource_data['project_id'],
            'body': body
        }

class GcpProject(GoogleAPIResource):

    service_name = "cloudresourcemanager"
    resource_path = "projects"
    version = "v1"

    def _get_request_args(self):
        return {
            'projectId': self.resource_data['resource_name']
        }

    def _update_request_args(self, body):
        return {
            'projectId': self.resource_data['resource_name'],
            'body': body
        }

class GcpProjectIam(GcpProject):

    resource_property = "iam"
    get_method = "getIamPolicy"
    update_method = "setIamPolicy"

    def _get_request_args(self):
        return {
            'resource': self.resource_data['resource_name']
        }

    def _update_request_args(self, body):
        return {
            'resource': self.resource_data['resource_name'],
            'body': {
                'policy': body,
                'updateMask': "bindings,etag,auditConfigs"
            }
        }
<commit_msg>Add method for generating the full resource name of gcp resources<commit_after># Copyright 2019 The resource-policy-evaluation-library Authors. All rights reserved.
#
# 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.

from urllib.parse import urlparse
from .base import Resource
from rpe.exceptions import is_retryable_exception
import tenacity
from googleapiclienthelpers.discovery import build_subresource
from googleapiclienthelpers.waiter import Waiter

class GoogleAPIResource(Resource):

    # Names of the get and update methods. Most are the same but override in
    # the Resource if necessary
    resource_property = None
    get_method = "get"
    update_method = "update"

    # If a resource is not in a ready state, we can't update it. If we retrieve
    # it, and the state changes, updates will be rejected because the ETAG will
    # have changed. If a resource defines readiness criteria, the get() call
    # will wait until the resource is in a ready state to return
    #
    # Key/Value to check to see if a resource is ready
    readiness_key = None
    readiness_value = None

    def __init__(self, resource_data, **kwargs):
        full_resource_path = "{}.{}".format(
            self.service_name,
            self.resource_path
        )

        self.service = build_subresource(
            full_resource_path,
            self.version,
            **kwargs
        )

        # If we are a property of a resource, also get the resource we're
        # associated with
        if self.is_property():
            # Build resource data for the parent
            parent_data = resource_data.copy()
            parent_type = resource_data['resource_type'].rsplit('.', 1)[0]
            parent_data['resource_type'] = parent_type

            self.parent_resource = GoogleAPIResource.factory(
                parent_data,
                **kwargs
            )

        self.resource_data = resource_data

    def is_property(self):
        return self.resource_property is not None

    @staticmethod
    def factory(resource_data, **kargs):
        resource_type_map = {
            'bigquery.datasets': GcpBigqueryDataset,
            'compute.instances': GcpComputeInstance,
            'cloudresourcemanager.projects': GcpProject,
            'cloudresourcemanager.projects.iam': GcpProjectIam,
            'pubsub.projects.subscriptions': GcpPubsubSubscription,
            'pubsub.projects.subscriptions.iam': GcpPubsubSubscriptionIam,
            'pubsub.projects.topics': GcpPubsubTopic,
            'pubsub.projects.topics.iam': GcpPubsubTopicIam,
            'sqladmin.instances': GcpSqlInstance,
            'storage.buckets': GcpStorageBucket,
            'storage.buckets.iam': GcpStorageBucketIamPolicy
        }

        resource_type = resource_data.get('resource_type')
        if not resource_type:
            assert 0, 'Unrecognized resource'

        if resource_type not in resource_type_map:
            assert 0, 'Unrecognized resource'

        cls = resource_type_map.get(resource_type)
        return cls(resource_data, **kargs)

    def type(self):
        type_components = ["gcp", self.service_name, self.resource_path]

        # Things like IAM policy are not separate resources, but rather
        # properties of a resource. We may want to evaluate policy on these
        # properties, so we represent them as resources and need to distinguish
        # them in the resource type.
        if self.is_property():
            type_components.append(self.resource_property)

        return ".".join(type_components)

    # Google's documentation describes what it calls a 'full resource name' for
    # resources. None of the API's seem to implement it (except Cloud Asset
    # Inventory). This attempts to generate it from the discovery-based api
    # client's generated http request url.
    #
    # If we inject it into the resource, we can use it in policy evaluation to
    # simplify the structure of our policies
    def full_resource_name(self):

        # If this is a resource property, return the resource's frn instead
        if self.is_property():
            return self.parent_resource.full_resource_name()

        method = getattr(self.service, self.get_method)
        uri = method(**self._get_request_args()).uri

        uri_parsed = urlparse(uri)
        domain = uri_parsed.netloc
        path_segments = uri_parsed.path[1:].split('/')

        # First we need the name of the api
        if domain.startswith("www."):
            # we need to get the api name from the path
            api_name = path_segments.pop(0)
        else:
            # the api name is the first segment of the domain
            api_name = domain.split('.')[0]

        # Remove the version from the path
        path_segments.pop(0)

        # Remove method from the last path segment
        if ":" in path_segments[-1]:
            path_segments[-1] = path_segments[-1].split(":")[0]

        # Annoying resource-specific fixes
        if api_name == 'storage' and path_segments[0] == 'b':
            path_segments[0] = "buckets"

        resource_path = "/".join(path_segments)

        return "//{}.googleapis.com/{}".format(api_name, resource_path)

    def get(self):
        method = getattr(self.service, self.get_method)

        # If the resource has readiness criteria, wait for it
        if self.readiness_key and self.readiness_value:
            waiter = Waiter(method, **self._get_request_args())
            asset = waiter.wait(
                self.readiness_key,
                self.readiness_value,
                interval=7,
                retries=60
            )
        else:
            asset = method(**self._get_request_args()).execute()

        # if this asset is a property, inject its parent
        if self.is_property():
            parent = self.parent_resource.get()
            asset['_resource'] = parent
        return asset

    @tenacity.retry(
        retry=tenacity.retry_if_exception(is_retryable_exception),
        wait=tenacity.wait_random_exponential(multiplier=1, max=10),
        stop=tenacity.stop_after_attempt(10)
    )
    def update(self, body):

        # remove injected data before attempting update
        if self.is_property() and '_resource' in body:
            del body['_resource']

        method = getattr(self.service, self.update_method)
        return method(**self._update_request_args(body)).execute()

class GcpBigqueryDataset(GoogleAPIResource):

    service_name = "bigquery"
    resource_path = "datasets"
    version = "v2"

    def _get_request_args(self):
        return {
            'datasetId': self.resource_data['resource_name'],
            'projectId': self.resource_data['project_id']
        }

    def _update_request_args(self, body):
        return {
            'datasetId': self.resource_data['resource_name'],
            'projectId': self.resource_data['project_id'],
            'body': body
        }

class GcpComputeInstance(GoogleAPIResource):

    service_name = "compute"
    resource_path = "instances"
    version = "v1"

    def _get_request_args(self):
        return {
            'instance': self.resource_data['resource_name'],
            'zone': self.resource_data['resource_location'],
            'project': self.resource_data['project_id']
        }

    def _update_request_args(self, body):
        return {
            'instance': self.resource_data['resource_name'],
            'zone': self.resource_data['resource_location'],
            'project': self.resource_data['project_id']
        }

class GcpPubsubSubscription(GoogleAPIResource):

    service_name = "pubsub"
    resource_path = "projects.subscriptions"
    version = "v1"
    update_method = "patch"

    def _get_request_args(self):
        return {
            'subscription': 'projects/{}/subscriptions/{}'.format(
                self.resource_data['project_id'],
                self.resource_data['resource_name']
            )
        }

    def _update_request_args(self, body):
        return {
            'name': 'projects/{}/subscriptions/{}'.format(
                self.resource_data['project_id'],
                self.resource_data['resource_name']
            ),
            'body': {
                'subscription': body,
                'updateMask': 'labels,ack_deadline_seconds,push_config,message_retention_duration,retain_acked_messages,expiration_policy'
            }
        }

class GcpPubsubSubscriptionIam(GcpPubsubSubscription):

    resource_property = 'iam'
    get_method = "getIamPolicy"
    update_method = "setIamPolicy"

    def _get_request_args(self):
        return {
            'resource': 'projects/{}/subscriptions/{}'.format(
                self.resource_data['project_id'],
                self.resource_data['resource_name']
            )
        }

    def _update_request_args(self, body):
        return {
            'resource': 'projects/{}/subscriptions/{}'.format(
                self.resource_data['project_id'],
                self.resource_data['resource_name']
            ),
            'body': {
                'policy': body
            }
        }

class GcpPubsubTopic(GoogleAPIResource):

    service_name = "pubsub"
    resource_path = "projects.topics"
    version = "v1"
    update_method = "patch"

    def _get_request_args(self):
        return {
            'topic': 'projects/{}/topics/{}'.format(
                self.resource_data['project_id'],
                self.resource_data['resource_name']
            )
        }

    def _update_request_args(self, body):
        return {
            'name': 'projects/{}/topics/{}'.format(
                self.resource_data['project_id'],
                self.resource_data['resource_name']
            ),
            'body': {
                'topic': body,
                # the name field is immutable
                'updateMask': 'labels'
            }
        }

class GcpPubsubTopicIam(GcpPubsubTopic):

    resource_property = "iam"
    get_method = "getIamPolicy"
    update_method = "setIamPolicy"

    def _get_request_args(self):
        return {
            'resource': 'projects/{}/topics/{}'.format(
                self.resource_data['project_id'],
                self.resource_data['resource_name']
            )
        }

    def _update_request_args(self, body):
        return {
            'resource': 'projects/{}/topics/{}'.format(
                self.resource_data['project_id'],
                self.resource_data['resource_name']
            ),
            'body': {
                'policy': body
            }
        }

class GcpStorageBucket(GoogleAPIResource):

    service_name = "storage"
    resource_path = "buckets"
    version = "v1"

    def _get_request_args(self):
        return {
            'bucket': self.resource_data['resource_name'],
            'userProject': self.resource_data['project_id']
        }

    def _update_request_args(self, body):
        return {
            'bucket': self.resource_data['resource_name'],
            'userProject': self.resource_data['project_id'],
            'body': body
        }

class GcpStorageBucketIamPolicy(GcpStorageBucket):

    resource_property = "iam"
    get_method = "getIamPolicy"
    update_method = "setIamPolicy"

class GcpSqlInstance(GoogleAPIResource):

    service_name = "sqladmin"
    resource_path = "instances"
    version = "v1beta4"
    readiness_key = 'state'
    readiness_value = 'RUNNABLE'

    def _get_request_args(self):
        return {
            'instance': self.resource_data['resource_name'],
            'project': self.resource_data['project_id']
        }

    def _update_request_args(self, body):
        return {
            'instance': self.resource_data['resource_name'],
            'project': self.resource_data['project_id'],
            'body': body
        }

class GcpProject(GoogleAPIResource):

    service_name = "cloudresourcemanager"
    resource_path = "projects"
    version = "v1"

    def _get_request_args(self):
        return {
            'projectId': self.resource_data['resource_name']
        }

    def _update_request_args(self, body):
        return {
            'projectId': self.resource_data['resource_name'],
            'body': body
        }

class GcpProjectIam(GcpProject):

    resource_property = "iam"
    get_method = "getIamPolicy"
    update_method = "setIamPolicy"

    def _get_request_args(self):
        return {
            'resource': self.resource_data['resource_name']
        }

    def _update_request_args(self, body):
        return {
            'resource': self.resource_data['resource_name'],
            'body': {
                'policy': body,
                'updateMask': "bindings,etag,auditConfigs"
            }
        }
<|endoftext|>


<commit_before><commit_msg>Added copyTable functionality<commit_after><|endoftext|>