Cannot use **default** as argument #294

Closed
harshadyeola opened this Issue Feb 12, 2015 · 6 comments

Comments

Projects
None yet
2 participants
@harshadyeola

Hi There..
Cannot use default as argument!!

class MyappDeleteController(CementBaseController):
    class Meta:
        label = 'delete'
        stacked_on = 'site'
        stacked_type = 'nested'
        description = 'delete an existing website'
        arguments = [
            (['arg1'],
                dict(help='domain name to be deleted')),
            (['--no-prompt'],
                dict(help="doesnt ask permission for delete",
            ]
    @expose(hide=True)
    def default(self):
        Log.info(self, "in default")
        # Delete code here

With above code i have used default as argument, but its not taking default as argument but as command.

root@ubuntu-web:~# myapp site delete default 
usage: myapp (sub-commands ...) [options ...] {arguments ...}
myapp: error: the following arguments are required: arg1
@derks

This comment has been minimized.

Show comment
Hide comment
@derks

derks Feb 12, 2015

Member

This is an interesting situation, because the default command is executed if no sub-command is passed, however in your use case you need to pass an argument of default (which in turn is read as explicitly calling the default sub-command).

I would suggest trying the following. Currently, you are stacking this controller on top of the site controller as nested. Meaning you would call this as:

$ yourapp site delete [arg1]

Would it be possible to instead make this an embedded controller, with a delete sub-command? For example:

class MyappDeleteController(CementBaseController):
    class Meta:
        label = 'delete'
        stacked_on = 'site'
        stacked_type = 'embedded'
        description = 'delete an existing website'
        arguments = [
            (['arg1'],
                dict(help='domain name to be deleted')),
            (['--no-prompt'],
                dict(help="doesnt ask permission for delete",
            ]
    @expose()
    def delete(self):
        Log.info(self, "in delete")
        # Delete code here

The delete sub-command and arguments would then be available under the site namespace. That is one option. Alternatively, you could make arg1 optional, and then handle the requirement in your code. This is kind of messy, but it could work... basically, check that default was explicitly passed via command line:

import sys

class MyappDeleteController(CementBaseController):
    class Meta:
        label = 'delete'
        stacked_on = 'site'
        stacked_type = 'nested'
        description = 'delete an existing website'
        arguments = [
            (['arg1'],
                dict(help='domain name to be deleted', nargs='?')),
            (['--no-prompt'],
                dict(help="doesnt ask permission for delete",
            ]

    @expose(hide=True)
    def default(self):
        try:
            # change the sys.argv index number to match your app layout
            if sys.argv[2] == 'default':
                self.app.pargs.arg1 = 'default'
        except IndexError as e:
            # not that many arguments were passed
            pass

        # continue normal operations
        assert self.app.pargs.arg1, "Argument required"

        print "Inside MySecondController.default()"
        print "Arg1 => %s" % self.app.pargs.arg1

In the above example.. the following would throw an Argument required error:

$ yourapp site delete

But this would give the end result of arg1 producing default:

$ yourapp site delete default

The important thing to note in the above is the use of nargs='?' on the arg1 argument... which makes it optional, otherwise it would throw a 'missing arguments' error.

I know that is probably confusing... and not ideal, but does that help at all?

Member

derks commented Feb 12, 2015

This is an interesting situation, because the default command is executed if no sub-command is passed, however in your use case you need to pass an argument of default (which in turn is read as explicitly calling the default sub-command).

I would suggest trying the following. Currently, you are stacking this controller on top of the site controller as nested. Meaning you would call this as:

$ yourapp site delete [arg1]

Would it be possible to instead make this an embedded controller, with a delete sub-command? For example:

class MyappDeleteController(CementBaseController):
    class Meta:
        label = 'delete'
        stacked_on = 'site'
        stacked_type = 'embedded'
        description = 'delete an existing website'
        arguments = [
            (['arg1'],
                dict(help='domain name to be deleted')),
            (['--no-prompt'],
                dict(help="doesnt ask permission for delete",
            ]
    @expose()
    def delete(self):
        Log.info(self, "in delete")
        # Delete code here

The delete sub-command and arguments would then be available under the site namespace. That is one option. Alternatively, you could make arg1 optional, and then handle the requirement in your code. This is kind of messy, but it could work... basically, check that default was explicitly passed via command line:

import sys

class MyappDeleteController(CementBaseController):
    class Meta:
        label = 'delete'
        stacked_on = 'site'
        stacked_type = 'nested'
        description = 'delete an existing website'
        arguments = [
            (['arg1'],
                dict(help='domain name to be deleted', nargs='?')),
            (['--no-prompt'],
                dict(help="doesnt ask permission for delete",
            ]

    @expose(hide=True)
    def default(self):
        try:
            # change the sys.argv index number to match your app layout
            if sys.argv[2] == 'default':
                self.app.pargs.arg1 = 'default'
        except IndexError as e:
            # not that many arguments were passed
            pass

        # continue normal operations
        assert self.app.pargs.arg1, "Argument required"

        print "Inside MySecondController.default()"
        print "Arg1 => %s" % self.app.pargs.arg1

In the above example.. the following would throw an Argument required error:

$ yourapp site delete

But this would give the end result of arg1 producing default:

$ yourapp site delete default

The important thing to note in the above is the use of nargs='?' on the arg1 argument... which makes it optional, otherwise it would throw a 'missing arguments' error.

I know that is probably confusing... and not ideal, but does that help at all?

@harshadyeola

This comment has been minimized.

Show comment
Hide comment
@harshadyeola

harshadyeola Feb 13, 2015

@derks
Thanks for quick solution, I will try this this and back to you soon.

@derks
Thanks for quick solution, I will try this this and back to you soon.

@harshadyeola

This comment has been minimized.

Show comment
Hide comment
@harshadyeola

harshadyeola Feb 16, 2015

@derks
I can't use MyappDeleteController with stacked_type as embedded. as i have different optional arguments.

@derks
I can't use MyappDeleteController with stacked_type as embedded. as i have different optional arguments.

@derks

This comment has been minimized.

Show comment
Hide comment
@derks

derks May 6, 2015

Member

@harshadyeola I am sorry I haven't not gotten back to this. Have you found any alternative solutions?

Member

derks commented May 6, 2015

@harshadyeola I am sorry I haven't not gotten back to this. Have you found any alternative solutions?

derks added a commit that referenced this issue May 6, 2015

@derks

This comment has been minimized.

Show comment
Hide comment
@derks

derks May 6, 2015

Member

I've committed a work-around for this issue to cement/master, which will be available in Cement 2.6 Stable. The following is a working example:

from cement.core import handler
from cement.core.foundation import CementApp
from cement.core.controller import CementBaseController, expose

class BaseController(CementBaseController):
    class Meta:
        label = 'base'

    @expose(hide=True)
    def default(self):
        print('Inside MyController.default()')

class DeleteController(CementBaseController):
    class Meta:
        label = 'delete'
        stacked_on = 'base'
        stacked_type = 'nested'
        arguments = [
            (['arg1'],
             dict(help='domain name to be deleted', nargs='?')),
            ]
        default_func = 'default_command'

    @expose(hide=True)
    def default_command(self):
        print(self.app.argv)
        print('Inside DeleteController.default_command()')
        print('arg1 => %s' % self.app.pargs.arg1)


class MyApp(CementApp):
    class Meta:
        label = 'myapp'
        base_controller = BaseController

with MyApp() as app:
    handler.register(DeleteController)

    try:
        app.run()
    except Exception as e:
        print e

In action:

$ python myapp.py delete default
Inside DeleteController.default_command()
arg1 => default

The above uses the new CementBaseController.Meta.default_func meta-data option to change the default function name. Additionally, the default() function is renamed to default_command but it can be called anything (can not start with an _ however).

Please let me know if this doesn't work for you.

Member

derks commented May 6, 2015

I've committed a work-around for this issue to cement/master, which will be available in Cement 2.6 Stable. The following is a working example:

from cement.core import handler
from cement.core.foundation import CementApp
from cement.core.controller import CementBaseController, expose

class BaseController(CementBaseController):
    class Meta:
        label = 'base'

    @expose(hide=True)
    def default(self):
        print('Inside MyController.default()')

class DeleteController(CementBaseController):
    class Meta:
        label = 'delete'
        stacked_on = 'base'
        stacked_type = 'nested'
        arguments = [
            (['arg1'],
             dict(help='domain name to be deleted', nargs='?')),
            ]
        default_func = 'default_command'

    @expose(hide=True)
    def default_command(self):
        print(self.app.argv)
        print('Inside DeleteController.default_command()')
        print('arg1 => %s' % self.app.pargs.arg1)


class MyApp(CementApp):
    class Meta:
        label = 'myapp'
        base_controller = BaseController

with MyApp() as app:
    handler.register(DeleteController)

    try:
        app.run()
    except Exception as e:
        print e

In action:

$ python myapp.py delete default
Inside DeleteController.default_command()
arg1 => default

The above uses the new CementBaseController.Meta.default_func meta-data option to change the default function name. Additionally, the default() function is renamed to default_command but it can be called anything (can not start with an _ however).

Please let me know if this doesn't work for you.

@derks derks closed this May 6, 2015

@harshadyeola

This comment has been minimized.

Show comment
Hide comment
@harshadyeola

harshadyeola May 7, 2015

@derks
Thanks for the heads up. I will try your solution and let you know.

@derks
Thanks for the heads up. I will try your solution and let you know.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment