Skip to content

Commit

Permalink
Merge branch 'cli-fixes'
Browse files Browse the repository at this point in the history
  • Loading branch information
rienafairefr committed Sep 17, 2018
2 parents 8858c5e + 46179db commit 3db13da
Show file tree
Hide file tree
Showing 137 changed files with 2,669 additions and 1,186 deletions.
Empty file added cli/__init__.py
Empty file.
657 changes: 0 additions & 657 deletions cli/rebrickable_cli/cli.py

This file was deleted.

Empty file.
72 changes: 72 additions & 0 deletions cli/rebrickable_cli/cli/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
from functools import wraps

import click
import decorator
from click import get_current_context, Group


class State(object):
def __init__(self, format=None, client=None, user_token=None,
list_id=None, part_num=None, color_id=None, set_num=None, api=None):
self.format = format
self.client = client

self.api = api
self.user_token = user_token
self.list_id = list_id
self.part_num = part_num
self.color_id = color_id
self.set_num = set_num


def oprint(obj):
# print an object using the current configured output (json/yaml/py)
get_current_context().find_object(State).format.output(obj)


def get_or_push(fun):
@pass_state
@click.pass_context
@wraps(fun)
def decorated(click_context, state, *args, **kwargs):
for attr in kwargs:
setattr(state, attr, kwargs[attr])
try:
current_obj = fun(*args, **kwargs)
except:
current_obj = fun(state, *args, **kwargs)
if click_context.invoked_subcommand is None:
oprint(current_obj)
else:
click_context.obj = current_obj

return decorated


def object_print(fun):
def object_print_decorator(fun, *args, **kwargs):
obj = fun(*args, **kwargs)
oprint(obj)

return decorator.decorate(fun, object_print_decorator)


def add_typed_subcommands(type_):
def add_fun(fun, attribute_name):
@fun.command(name=attribute_name)
@click.make_pass_decorator(type_)
def type_cmd(obj):
print(getattr(obj, attribute_name))

def decorator(fun):
for attribute_name, attribute_type in type_.openapi_types.items():
add_fun(fun, attribute_name)

setattr(fun, 'invoke_without_command', True)

return fun

return decorator


pass_state = click.make_pass_decorator(State)
159 changes: 159 additions & 0 deletions cli/rebrickable_cli/cli/lego.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import click

from rebrickable_api import LegoApi, Part, PartColorsElement, Color, Element, Moc, PartCategory, Set, Theme
from rebrickable_cli.cli.common import add_typed_subcommands, pass_state, \
object_print, oprint, get_or_push


@click.group(help='LEGO data (parts, sets, themes, etc.)')
@pass_state
def lego(state):
state.api = LegoApi(state.client)


@lego.command('parts')
@pass_state
@object_print
def lego_parts(state):
return state.api.lego_parts_list()


@add_typed_subcommands(Part)
@lego.group('part')
@pass_state
@get_or_push
@click.argument('part_num')
def lego_part(state, part_num):
return state.api.lego_parts_read(part_num=state.part_num)


@lego_part.command('colors')
@pass_state
@object_print
def lego_part_colors(state):
return state.api.lego_parts_colors_list(part_num=state.part_num)


@add_typed_subcommands(PartColorsElement)
@lego_part.group('color')
@pass_state
@get_or_push
@click.argument('color_id', type=int)
def lego_part_color(state, color_id):
return state.api.lego_parts_colors_read(color_id=color_id, part_num=state.part_num)


@lego_part_color.command('sets')
@pass_state
@object_print
def lego_part_color_sets(state):
return state.api.lego_parts_colors_sets_list(color_id=state.color_id, part_num=state.part_num)


@lego.command('colors')
@pass_state
@object_print
def lego_colors(state):
return state.api.lego_colors_list()


@add_typed_subcommands(Color)
@lego.group('color')
@pass_state
@get_or_push
@click.argument('color_id', type=int)
def lego_color(state, color_id):
return state.api.lego_colors_read(id=color_id)


@add_typed_subcommands(Element)
@lego.group('element')
@pass_state
@get_or_push
@click.argument('element_id')
def lego_element(state, element_id):
return state.api.lego_elements_read(element_id=element_id)


@add_typed_subcommands(Moc)
@lego.group('moc')
@pass_state
@get_or_push
@click.argument('set_num')
def lego_moc(state, set_num):
return state.api.lego_mocs_read(set_num=set_num)


@lego_moc.command('parts')
@pass_state
@object_print
def lego_moc_parts(state):
return state.api.lego_mocs_parts_list(set_num=state.set_num)


@lego.command('part_categories')
@pass_state
@object_print
def lego_part_categories(state):
return state.api.lego_part_categories_list()


@add_typed_subcommands(PartCategory)
@lego.group('part_category')
@pass_state
@get_or_push
@click.argument('id', type=int)
def lego_part_category(state, id):
return state.api.lego_part_categories_read(id=id)


@lego.command('sets')
@pass_state
def lego_sets(state):
oprint(state.api.lego_sets_list())


@add_typed_subcommands(Set)
@lego.group('set')
@pass_state
@get_or_push
@click.argument('set_num')
def lego_set(state, set_num):
return state.api.lego_sets_read(set_num=set_num)


@lego_set.command('parts')
@pass_state
@object_print
def lego_set_parts(state):
return state.api.lego_sets_parts_list(set_num=state.set_num)


@lego_set.command('alternates')
@pass_state
@object_print
def lego_set_alternates(state):
return state.api.lego_sets_alternates_list(set_num=state.set_num)


@lego_set.command('sets')
@pass_state
@object_print
def lego_set_sets(state):
return state.api.lego_sets_sets_list(set_num=state.set_num)


@lego.command('themes')
@pass_state
@object_print
def lego_themes(state):
return state.api.lego_themes_list()


@add_typed_subcommands(Theme)
@lego.group('theme')
@pass_state
@get_or_push
@click.argument('theme_id')
def lego_theme(state, theme_id):
return state.api.lego_themes_read(id=theme_id)
86 changes: 86 additions & 0 deletions cli/rebrickable_cli/cli/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# -*- coding: utf-8 -*-

"""Console script for pyrebrickable."""
from __future__ import print_function
import json
from collections import namedtuple
from getpass import getpass

import click
import yaml
from click import get_current_context

from rebrickable_api import ApiClient, Configuration
from rebrickable_cli.cli.common import State

from rebrickable_cli.cli.lego import lego
from rebrickable_cli.cli.user import user
from rebrickable_cli.cli.users import users

try:
from enum import Enum, EnumMeta # pragma: no cover
except ImportError: # pragma: no cover
from enum34 import Enum, EnumMeta # pragma: no cover

from rebrickable_cli.utils import get_data, DATA_PATH, EnumType, write_data


class OutputFormat(Enum):
json = 0 # json.dumps
yaml = 1 # yaml.dump
py = 2 # print


OutputFormatter = namedtuple('OutputFormatter', ['output'])


def get_api_client():
configuration = Configuration()
data = get_data()
api_key = data['api_key']
configuration.api_key['Authorization'] = api_key
configuration.api_key_prefix['Authorization'] = 'key'
return ApiClient(configuration)


@click.group(help="Rebrickable CLI implemented in Python")
@click.pass_context
@click.option('--output', '-o', help='output printer (json, yaml, py --regular print Python function--)',type=EnumType(OutputFormat, casesensitive=False), default="py")
def main(click_context, output):
"""Console script for pyrebrickable."""

format = None
if output == OutputFormat.json:
format = OutputFormatter(output=lambda o: print(json.dumps(o.to_dict(), default=str)))
elif output == OutputFormat.yaml:
format = OutputFormatter(output=lambda o: print(yaml.dump(o.to_dict(), default_flow_style=False)))
elif output == OutputFormat.py:
format = OutputFormatter(output=lambda o: print(o))

try:
click_context.obj = State(format=format, client=get_api_client())
except (IOError, KeyError, ValueError):
if click_context.invoked_subcommand != 'register':
print('please register your API key using: \nrebrickable register')
raise click.Abort()


def get_api_key():
return getpass(prompt='Please enter your API key:')


@main.command(help='registers an API key with the CLI')
def register():
key = get_api_key()
data = get_data()
data['api_key'] = key
write_data(data)
print('OK, registered API key in %s' % DATA_PATH)


main.add_command(lego)
main.add_command(users)
main.add_command(user)

if __name__ == "__main__":
main()
Loading

0 comments on commit 3db13da

Please sign in to comment.