2017-10-23 8 views
1

私はPythonでclickパッケージを使用してCLIを構築しようとしています。クリックライブラリ付きのPython 3.6でモジュールエラーなし

import os 
import click 

cmd_folder = os.path.join(os.path.dirname(__file__), 'commands') 


class IAMCLI(click.MultiCommand): 

    def list_commands(self, ctx): 
     rv = [] 
     for filename in os.listdir(cmd_folder): 
      if filename.endswith('.py') and \ 
        filename.startswith('cmd_'): 
       rv.append(filename[4:-3]) 
     rv.sort() 
     return rv 

    def get_command(self, ctx, cmd_name): 
     ns = {} 
     fn = os.path.join(cmd_folder, 'cmd_{}.py'.format(cmd_name)) 
     with open(fn) as f: 
      code = compile(f.read(), fn, 'exec') 
      eval(code, ns, ns) 
     return ns['cli'] 


@click.command(cls=IAMCLI) 
@click.option('--env', default='dev', type=click.Choice(['dev', 'staging', 'production']), 
       help='AWS Environment') 
@click.pass_context 
def cli(): 
    """AWS IAM roles and policies management CLI.""" 
    pass 


if __name__ == '__main__': 
    cli() 

と、これは木である:

├── cli 
│   ├── __init__.py 
│   ├── aws 
│   │   ├── __init__.py 
│   │   ├── policy.py 
│   │   └── role.py 
│   ├── cli.py 
│   └── commands 
│    ├── __init__.py 
│    └── cmd_dump.py 

cmd_dump.pyは次のようになります。

私が使用しているPythonのバージョンは 3.6

これは私のアプリケーションのメインであります

import click 

from cli.aws.role import fetch_roles 


@click.command('dump', short_help='Dump IAM resources') 
@click.pass_context 
def cli(): 
    pass 

問題は、実行しようとするときですpython cli/cli.py --helpこれは私が得るものです:

File "cli/commands/cmd_dump.py", line 3, in <module> 
    from cli.aws.role import fetch_roles 
ModuleNotFoundError: No module named 'cli.aws'; 'cli' is not a package 

それについては何か考えてください。

+0

'cli.py'の親ディレクトリは' sys.path'の前に置かれていますので、 'aws.role.fetch_roles'をcli.pyスクリプトからインポートしようとしています。 – vaultah

+0

ここでは、 'os.path.join(cmd_folder、 'cmd _ {}。py'.format(cmd_name))'と書いてありますか? – Mazzy

+0

修正方法が不明です。何か案が? – Mazzy

答えて

2

私は新しいPythonプロジェクトの開発を開始するときに私のアプローチに基づいて別の答えを与えることをしようとします。あなたはあなたのプロジェクトを配布する予定ですか、あるいは誰かと共有しているだけでしょうか?もしあなたがそう思うなら、あなたは何を考えていますか?このコマンドを覚えておく必要がある人は、このコマンドを覚えておく必要がありますか?コマンドを覚えるのが彼にとってより簡単ではないでしょうか

$ cli --help 

代わりに、

私はあなたがすぐにあなたのプロジェクトのパッケージングを開始することをお勧め - 最小限のセットアップスクリプトを記述します。

from setuptools import setup, find_packages 

setup(
    name='mypkg', 
    version='0.1', 
    packages=find_packages(), 
    install_requires=['click'], 
    entry_points={ 
     'console_scripts': ['cli=cli.cli:cli'], 
    }, 
) 

新しい要件が出てくるとき、あなたは常にあなたのセットアップスクリプトを高めることができます。あなたのコードベースのルートディレクトリにセットアップスクリプトを配置します。

├── setup.py 
├── cli 
│   ├── __init__.py 
│   ├── aws 
... 

は今(setup.pyスクリプトがある)コードベースのルートディレクトリから、さらに良いpip install --editable=.python setup.py developを実行しますか。あなたは、開発モードでプロジェクトをインストールしているし、今(あなたの問題を解決することになる)、全ての輸入が正しく解決された状態で

$ cli --help 

を呼び出すことができます。しかし、それ以外にも多くの利点があります。ターゲットユーザーに配布する準備ができているようにプロジェクトをパッケージ化する方法と、ユーザーが以前と同じように呼び出すクリーンなコマンドラインインターフェイスが得られます。

ここでプロジェクトの開発を続けます。 cliコマンドのコードを変更すると、それは即時に適用されるため、何か変更するたびにプロジェクトを再インストールする必要はありません。プロジェクトの開発と準備ができていると、問題のユーザーに配信したいたら

:これはインストール可能wheelファイルにプロジェクトをパッケージ化します

$ python setup.py bdist_wheel 

(あなたがするwheelパッケージをインストールする必要があります。コマンド:pip install wheel --user)を呼び出すことができます。通常は、コードベースルートディレクトリのサブディレクトリdistに存在します。このファイルをユーザーに与えます。ファイルをインストールするには、彼は

$ pip install Downloads/mypkg-0.1-py3-none.whl --user 

を発行し、TPを開始すぐにツールを使用することができます。

$ cli --help 

これは非常に単純化記述で、学ぶべきものがたくさんあるが、プロセスを通してあなたを導く役に立つ資料がたくさんあります。

トピックの詳細については、クイックスタートリファレンスとして、PyPA packaging guideをお勧めします。パッケージclickコマンドの場合、their own docs are more than sufficient


  1. 私が分布し、それがそのための標準的なツールであるとして、該当するパッケージの開発にpipを使用することをお勧めします。
+0

素敵な答えをありがとう。 'python path/to/project/codebase/cli/cli.py --help'コマンドが正しくモジュールを解決しない点 – Mazzy

+0

あなたはインタプリタを介してプレーンスクリプトとして呼び出しているので、そうです。私が記述したアプローチは、コマンドラインユーティリティを呼び出すことを意図しているので、コマンドラインユーティリティを呼び出して、Pythonスクリプトとして扱う際の頭痛を避けることです。 – hoefling

+0

プロジェクトの構造を変更する必要は全くありませんでした。名前の変更やファイルの移動はありません。 – hoefling

0

私はこれを100回行った。パッケージを複数のレベルで同じ名前にすることは魅力ですが、抵抗してください!最近私のパッケージには__main__.pyが付いている傾向があり、これがあなたの持つ問題を解決するのに役立ちます。

名前空間の問題があると思われます。あなたのパッケージまたはcli.pyと呼ばれる内部ファイルの名前を変更してみてください。これらを使用しようとしているすべてのインポートを必ずリファクタリングしてください。

また、関数名をcli以外に変更してください。同じ問題。

├── cli     <- rename this to 'my_app' or something else 
│ ├── __init__.py 
│ ├── aws 
│ │ ├── __init__.py 
│ │ ├── policy.py 
│ │ └── role.py 
│ ├── cli.py    <- or maybe it is easier to rename this 
│ └── commands 
│  ├── __init__.py 
│  └── cmd_dump.py 
+0

これはなぜですか?名前を変更して何を達成する必要がありますか? – Mazzy

+1

「ModuleNotFoundError: 'cli.aws'という名前のモジュールがありません。 'cli'はパッケージではありません。実際には 'cli.py'の内部を見ていて、' cli'というディレクトリではないと思います。それがモジュールが見つからない理由です...それはモジュールを見ていません!奇妙なことは、cpythonがそれらを作成する順序によっては、時にはうまくいくことがあり、時にはうまくいかないことがあります。ボード全体で同じ名前を付けないでください。 – slightlynybbled

+0

私はaws_iam_cliフォルダの名前を変更しましたが、まだ動作しません。 – Mazzy

1

パッケージ内でスクリプトを実行しないでください。パッケージはコード内でインポートされますが、スクリプトはスクリプト内で実行されません。残りはインポートエラーとは関係ありません。例えば

├── cli # package 
│ ├── __init__.py 
│ ├── aws 
│ │ ├── __init__.py 
│ │ ├── policy.py 
│ │ └── role.py 
│ ├── cli.py 
│ │ └── commands 
│ │  ├── __init__.py 
│ │  └── cmd_dump.py 
├── run_this_module.py 

モジュールrun_this_module.pyを実行する:

import cli 

"""Do your code here""" 
+0

このように「クリック」を実行するポイントは、実行を中断するということです。 'python main.py dump'を実行すると次のようなエラーが出ます:: TypeError:cli()は0の位置引数をとりますが、1が与えられました' – Mazzy

+0

私はこれらの単純なルール(および他の多くのルール)を使用して、 。 Pythonは、私が生産用ではなくソフトウェアを設計するために使用する言語です。純粋なC言語で翻訳するにはコピー&ペーストのコマンドが必要です。 –