Invoke: Example from 芦 Using Invoke as a library 禄 gives 芦 AttributeError 禄

Created on 3 Nov 2015  路  9Comments  路  Source: pyinvoke/invoke

Here's the code: https://github.com/zaiste/invoke-example-bug
I followed this tutorial: http://invoke.readthedocs.org/en/latest/concepts/library.html#reusing-as-a-binary

  1. Install the package with python setup.py install
  2. Execute tester

it returns:

Traceback (most recent call last):
  File "/usr/local/bin/tester", line 9, in <module>
    load_entry_point('tester==0.1.0', 'console_scripts', 'tester')()
  File "/usr/local/lib/python2.7/site-packages/invoke/program.py", line 246, in run
    self._parse(argv)
  File "/usr/local/lib/python2.7/site-packages/invoke/program.py", line 301, in _parse
    self.parse_tasks()
  File "/usr/local/lib/python2.7/site-packages/invoke/program.py", line 458, in parse_tasks
    self.parser = Parser(contexts=self.collection.to_contexts())
AttributeError: 'module' object has no attribute 'to_contexts'
Needs investigation Support

Most helpful comment

Hi,

I am also seeing this issue; after poking around with the source, I figured it is caused by not including the task_args into the initial context when the namespace is not None.

In other words, I can get the example to work when this diff applied:

diff --git a/invoke/program.py b/invoke/program.py
index 078f9fc..ac799a6 100644
--- a/invoke/program.py
+++ b/invoke/program.py
@@ -429,8 +429,7 @@ class Program(object):
         whether a bundled namespace was specified in `.__init__`.
         """
         args = self.core_args()
-        if self.namespace is None:
-            args += self.task_args()
+        args += self.task_args()
         return ParserContext(args=args)

     def print_version(self):

... and setting up the Program like this:

program = Program(namespace=Collection.from_module(tasks), version= '0.1.0')

However, I don't see why the task_args are omitted with a namespace, so I suspect something else will possibly break with my patch. Maybe it helps tracking down the issue, though :)

All 9 comments

Thanks, will try to replicate on my end but sounds plausible :) may simply need a Collection() wrapped around the module object (so, namespace=Collection(tasks)), though that seems like something odd for me to have overlooked.

I've tried namespace=Collection(tasks) and Collection.from_module(tasks), but it still doesn't work. Now, I'm getting AttributeError: list. I'm not sure how to debug this...

Yep, I'm having the same problem.
The Collection.from_module() worked https://github.com/pyinvoke/invoke/pull/285

But the subcommands can't be executed :(

Hi,

the same for me with Invoke 0.12.0. Code is here

best, Luca

Having the same problem with 0.12.2

Hi,

I am also seeing this issue; after poking around with the source, I figured it is caused by not including the task_args into the initial context when the namespace is not None.

In other words, I can get the example to work when this diff applied:

diff --git a/invoke/program.py b/invoke/program.py
index 078f9fc..ac799a6 100644
--- a/invoke/program.py
+++ b/invoke/program.py
@@ -429,8 +429,7 @@ class Program(object):
         whether a bundled namespace was specified in `.__init__`.
         """
         args = self.core_args()
-        if self.namespace is None:
-            args += self.task_args()
+        args += self.task_args()
         return ParserContext(args=args)

     def print_version(self):

... and setting up the Program like this:

program = Program(namespace=Collection.from_module(tasks), version= '0.1.0')

However, I don't see why the task_args are omitted with a namespace, so I suspect something else will possibly break with my patch. Maybe it helps tracking down the issue, though :)

Based on the comment by @kiesel, this seems to work for me:

def pyinvoke_issue_283():
    from invoke.parser import ParserContext
    def initial_context(self):
        args = self.core_args()
        args += self.task_args()
        return ParserContext(args=args)
    return property(initial_context)

Program.initial_context = pyinvoke_issue_283()

FTR, this is really two issues:

  • Initially, as I noted in the past, the problem was a documentation one: currently Program(namespace=xxx) is supposed to take a Collection object, and the doc example doesn't properly reflect that. Rubbing Collection.from_module on it fixes the issue.
  • However, you'll then hit issue #288, which is also an AttributeError symptom, but with a different attribute/message. Easy to overlook as @zaiste probably did.

I have merged #288 and fixed the doc re: this issue, and my real world code that uses namespace=xxx is now working.

For anyone getting this error using flit you can just wrap you entry point with a method.

program = Program(...)

def main():
   program.run()
[tool.flit.scripts]
program = "program:main"
Was this page helpful?
0 / 5 - 0 ratings