LoginSignup
0
0

More than 5 years have passed since last update.

cloudmonkey CLI をpython3化してみる −1

Last updated at Posted at 2016-09-01

AWSやCloudnをAPIで利用できるようになるCLIツールCloudStack cloudmonkey CLIでも残念ながらPython2。そこで、勉強のためにPython3で動くようにしてみる。 

まだ成功していません、メモとして利用中。

Python3化

ちょっと調べたところ一般的には、2パターンあり、引き続きpython2でも動くようにする、「Polyglot」と、それ以降はPython2をサポートしない「Port」があるらしい。

方向性
  python2は使っていないのでPortすることにする。

Python3化に関するドキュメント

diveintopython

Python3化ツール

2から3に移行するのはよくあるみたいで、すぐに幾つかツールが見つかった。
2to3
Futurize
Six

作業履歴

作業用のディレクトリを作成してそこに移動

$ mkdir porting
$ cd porting

作業用の環境を準備して

$ pyvenv ven
$ source ven/bin/activate

ドキュメントには次のパッケージが必要だったのでインストール

readline
requests
Pygments
prettytable

argcomplete
$ pip install --upgrade readline requests Pygments prettytable argcomplete

$ pip freeze

argcomplete==1.4.1
prettytable==0.7.2
Pygments==2.1.3
readline==6.2.4.1
requests==2.11.1
```

それらに追加してポートするのにfutureをインストール
$ pip install future

コードをクローンしてそこに移動

$ git clone https://github.com/apache/cloudstack-cloudmonkey.git
cd cloudstack-cloudmonkey

中身はこんな感じ

$ ls

CHANGES.md        LICENSE           NOTICE            cloudmonkey       docs              setup.cfg
Dockerfile        Makefile          README.md         config.docker     performrelease.sh setup.py

早速インストールしてみる

$ python setup.py develop
  File "setup.py", line 50
    print "If you're upgrading, run the following to enable parameter completion:"
                                                                                 ^
SyntaxError: Missing parentheses in call to 'print'

まあ、失敗しますね。
ドキュメンをは後で読み直すとして 、futurize --stage1 futurize --stage2って実行するとpython3 のコードに書き換えてくれるみたいなので。まずインストールできるようにsetup.pyをpytho3のコードに変換

$ futurize --stage1 -w setup.py

RefactoringTool: Skipping optional fixer: idioms
RefactoringTool: Skipping optional fixer: ws_comma
RefactoringTool: Refactored setup.py
--- setup.py    (original)
+++ setup.py    (refactored)
@@ -1,3 +1,4 @@
+from __future__ import print_function
 # Licensed to the Apache Software Foundation (ASF) under one
 # or more contributor license agreements.  See the NOTICE file
 # distributed with this work for additional information
@@ -47,10 +48,10 @@
         requires.append('readline')

 # Upgrade notes for 5.3.0
-print "If you're upgrading, run the following to enable parameter completion:"
-print "  cloudmonkey sync"
-print "  cloudmonkey set paramcompletion true"
-print "Parameter completion may fail, if the above is not run!"
+print("If you're upgrading, run the following to enable parameter completion:")
+print("  cloudmonkey sync")
+print("  cloudmonkey set paramcompletion true")
+print("Parameter completion may fail, if the above is not run!")

 setup(
     name = 'cloudmonkey',
RefactoringTool: Files that were modified:
RefactoringTool: setup.py

$ futurize --stage2 -w setup.py

RefactoringTool: Refactored setup.py
--- setup.py    (original)
+++ setup.py    (refactored)
@@ -1,4 +1,5 @@
 from __future__ import print_function
+from builtins import str
 # Licensed to the Apache Software Foundation (ASF) under one
 # or more contributor license agreements.  See the NOTICE file
 # distributed with this work for additional information
RefactoringTool: Files that were modified:
RefactoringTool: setup.py

作業が終わったので、インスールを実行したが
$ python setup.py develop

Traceback (most recent call last):
  File "setup.py", line 29, in <module>
    from cloudmonkey import __version__, __description__
  File "/Users/kentaro/porting/cloudstack-cloudmonkey/cloudmonkey/__init__.py", line 22
    except ImportError, e:
                      ^
SyntaxError: invalid syntax

init.pyがエラーになり、終了、今度はinit.pyを変換してみる。
$ futurize --stage1 -w cloudmonkey/init.py

RefactoringTool: Skipping optional fixer: idioms
RefactoringTool: Skipping optional fixer: ws_comma
RefactoringTool: Refactored cloudmonkey/__init__.py
--- cloudmonkey/__init__.py     (original)
+++ cloudmonkey/__init__.py     (refactored)
@@ -1,3 +1,5 @@
+from __future__ import print_function
+from __future__ import absolute_import
 # Licensed to the Apache Software Foundation (ASF) under one
 # or more contributor license agreements.  See the NOTICE file
 # distributed with this work for additional information
@@ -16,8 +18,8 @@
 # under the License.

 try:
-    from config import __version__, __description__
-    from config import __maintainer__, __maintaineremail__
-    from config import __project__, __projecturl__, __projectemail__
-except ImportError, e:
-    print e
+    from .config import __version__, __description__
+    from .config import __maintainer__, __maintaineremail__
+    from .config import __project__, __projecturl__, __projectemail__
+except ImportError as e:
+    print(e)
RefactoringTool: Files that were modified:
RefactoringTool: cloudmonkey/__init__.py

$ futurize --stage2 -w cloudmonkey/init.py

RefactoringTool: No files need to be modified.

再度インストールしてみると。
$ python setup.py develop

Traceback (most recent call last):
  File "setup.py", line 29, in <module>
    from cloudmonkey import __version__, __description__
  File "/Users/kentaro/porting/cloudstack-cloudmonkey/cloudmonkey/__init__.py", line 21, in <module>
    from .config import __version__, __description__
  File "/Users/kentaro/porting/cloudstack-cloudmonkey/cloudmonkey/config.py", line 33
    except ImportError, e:
                      ^
SyntaxError: invalid syntax

今度はconfig.pyがエラーになり、終了、config.pyを変換してみる。
$ futurize --stage1 -w cloudmonkey/config.py

RefactoringTool: Skipping optional fixer: idioms
RefactoringTool: Skipping optional fixer: ws_comma
RefactoringTool: Refactored cloudmonkey/config.py
--- cloudmonkey/config.py       (original)
+++ cloudmonkey/config.py       (refactored)
@@ -16,6 +16,8 @@
 # specific language governing permissions and limitations
 # under the License.

+from __future__ import print_function
+from functools import reduce
 __version__ = "5.3.3"
 __description__ = "Command Line Interface for Apache CloudStack"
 __maintainer__ = "The Apache CloudStack Team"
@@ -30,8 +32,8 @@

     from ConfigParser import ConfigParser
     from os.path import expanduser
-except ImportError, e:
-    print "ImportError", e
+except ImportError as e:
+    print("ImportError", e)

 param_type = ['boolean', 'date', 'float', 'integer', 'short', 'list',
               'long', 'object', 'map', 'string', 'tzdate', 'uuid']
@@ -84,18 +86,18 @@
         try:
             with open(config_file, 'r') as cfg:
                 config.readfp(cfg)
-        except IOError, e:
-            print "Error: config_file not found", e
+        except IOError as e:
+            print("Error: config_file not found", e)

     profile = None
     try:
         profile = get_attr('profile')
-    except AttributeError, e:
+    except AttributeError as e:
         pass
     if profile is None or profile == '':
         profile = default_profile_name
     if profile in mandatory_sections:
-        print "Server profile name cannot be '%s'" % profile
+        print("Server profile name cannot be '%s'" % profile)
         sys.exit(1)

     has_profile_changed = False
@@ -117,8 +119,8 @@
                 else:
                     for key in config_fields[section].keys():
                         config.set(section, key, config_fields[section][key])
-            except ValueError, e:
-                print "Server profile name cannot be", profile
+            except ValueError as e:
+                print("Server profile name cannot be", profile)
                 sys.exit(1)
         if section in mandatory_sections:
             section_keys = config_fields[section].keys()
@@ -150,13 +152,13 @@
         try:
             with open(config_file, 'r') as cfg:
                 config.readfp(cfg)
-        except IOError, e:
-            print "Error: config_file not found", e
+        except IOError as e:
+            print("Error: config_file not found", e)
     else:
         config = write_config(get_attr, config_file)
-        print "Welcome! Use the `set` command to configure options"
-        print "Config file:", config_file
-        print "After setting up, run the `sync` command to sync apis\n"
+        print("Welcome! Use the `set` command to configure options")
+        print("Config file:", config_file)
+        print("After setting up, run the `sync` command to sync apis\n")

     missing_keys = []
     if config.has_option('core', 'profile'):
@@ -166,19 +168,19 @@
         profile = default_profile_name

     if profile is None or profile == '' or profile in mandatory_sections:
-        print "Server profile cannot be", profile
+        print("Server profile cannot be", profile)
         sys.exit(1)

     set_attr("profile_names", filter(lambda x: x != "core" and x != "ui",
                                      config.sections()))

     if not config.has_section(profile):
-        print ("Selected profile (%s) does not exist," +
-               " using default values") % profile
+        print(("Selected profile (%s) does not exist," +
+               " using default values") % profile)
         try:
             config.add_section(profile)
-        except ValueError, e:
-            print "Server profile name cannot be", profile
+        except ValueError as e:
+            print("Server profile name cannot be", profile)
             sys.exit(1)
         for key in default_profile.keys():
             config.set(profile, key, default_profile[key])
@@ -191,7 +193,7 @@
         for key in section_keys:
             try:
                 set_attr(key, config.get(section, key))
-            except Exception, e:
+            except Exception as e:
                 if section in mandatory_sections:
                     set_attr(key, config_fields[section][key])
                 else:
@@ -202,8 +204,8 @@
                 set_attr(key, get_attr('prompt').strip() + " ")

     if len(missing_keys) > 0:
-        print "Missing configuration was set using default values for keys:"
-        print "`%s` in %s" % (', '.join(missing_keys), config_file)
+        print("Missing configuration was set using default values for keys:")
+        print("`%s` in %s" % (', '.join(missing_keys), config_file))
         write_config(get_attr, config_file)

     return config_options
RefactoringTool: Files that were modified:
RefactoringTool: cloudmonkey/config.py

$ futurize --stage2 -w cloudmonkey/config.py

RefactoringTool: Refactored cloudmonkey/config.py
--- cloudmonkey/config.py       (original)
+++ cloudmonkey/config.py       (refactored)
@@ -17,6 +17,8 @@
 # under the License.

 from __future__ import print_function
+from future import standard_library
+standard_library.install_aliases()
 from functools import reduce
 __version__ = "5.3.3"
 __description__ = "Command Line Interface for Apache CloudStack"
@@ -30,7 +32,7 @@
     import os
     import sys

-    from ConfigParser import ConfigParser
+    from configparser import ConfigParser
     from os.path import expanduser
 except ImportError as e:
     print("ImportError", e)
@@ -114,18 +116,18 @@
             try:
                 config.add_section(section)
                 if section not in mandatory_sections:
-                    for key in default_profile.keys():
+                    for key in list(default_profile.keys()):
                         config.set(section, key, default_profile[key])
                 else:
-                    for key in config_fields[section].keys():
+                    for key in list(config_fields[section].keys()):
                         config.set(section, key, config_fields[section][key])
             except ValueError as e:
                 print("Server profile name cannot be", profile)
                 sys.exit(1)
         if section in mandatory_sections:
-            section_keys = config_fields[section].keys()
+            section_keys = list(config_fields[section].keys())
         else:
-            section_keys = default_profile.keys()
+            section_keys = list(default_profile.keys())
         for key in section_keys:
             try:
                 if not (has_profile_changed and section == profile):
@@ -143,9 +145,8 @@
     if not os.path.exists(config_dir):
         os.makedirs(config_dir)

-    config_options = reduce(lambda x, y: x + y, map(lambda x:
-                            config_fields[x].keys(), config_fields.keys()))
-    config_options += default_profile.keys()
+    config_options = reduce(lambda x, y: x + y, [list(config_fields[x].keys()) for x in list(config_fields.keys())])
+    config_options += list(default_profile.keys())

     config = ConfigParser()
     if os.path.exists(config_file):
@@ -171,8 +172,7 @@
         print("Server profile cannot be", profile)
         sys.exit(1)

-    set_attr("profile_names", filter(lambda x: x != "core" and x != "ui",
-                                     config.sections()))
+    set_attr("profile_names", [x for x in config.sections() if x != "core" and x != "ui"])

     if not config.has_section(profile):
         print(("Selected profile (%s) does not exist," +
@@ -182,14 +182,14 @@
         except ValueError as e:
             print("Server profile name cannot be", profile)
             sys.exit(1)
-        for key in default_profile.keys():
+        for key in list(default_profile.keys()):
             config.set(profile, key, default_profile[key])

     for section in (mandatory_sections + [profile]):
         if section in mandatory_sections:
-            section_keys = config_fields[section].keys()
+            section_keys = list(config_fields[section].keys())
         else:
-            section_keys = default_profile.keys()
+            section_keys = list(default_profile.keys())
         for key in section_keys:
             try:
                 set_attr(key, config.get(section, key))
RefactoringTool: Files that were modified:
RefactoringTool: cloudmonkey/config.py

インストールに再度挑戦
$ python setup.py develop

If you're upgrading, run the following to enable parameter completion:
  cloudmonkey sync
  cloudmonkey set paramcompletion true
Parameter completion may fail, if the above is not run!
running develop
running egg_info
creating cloudmonkey.egg-info
writing dependency_links to cloudmonkey.egg-info/dependency_links.txt
writing cloudmonkey.egg-info/PKG-INFO
writing entry points to cloudmonkey.egg-info/entry_points.txt
writing top-level names to cloudmonkey.egg-info/top_level.txt
writing requirements to cloudmonkey.egg-info/requires.txt
writing manifest file 'cloudmonkey.egg-info/SOURCES.txt'
reading manifest file 'cloudmonkey.egg-info/SOURCES.txt'
writing manifest file 'cloudmonkey.egg-info/SOURCES.txt'
running build_ext
Creating /Users/kentaro/porting/ven/lib/python3.5/site-packages/cloudmonkey.egg-link (link to .)
Adding cloudmonkey 5.3.3 to easy-install.pth file
Installing cloudmonkey script to /Users/kentaro/porting/ven/bin

Installed /Users/kentaro/porting/cloudstack-cloudmonkey
Processing dependencies for cloudmonkey==5.3.3
Searching for requests-toolbelt
Reading https://pypi.python.org/simple/requests-toolbelt/
Best match: requests-toolbelt 0.7.0
Downloading https://pypi.python.org/packages/59/78/1d391d30ebf74079a8e4de6ab66fdca5362903ef2df64496f4697e9bb626/requests-toolbelt-0.7.0.tar.gz#md5=bfe2009905f460f4764c32cfbbf4205f
Processing requests-toolbelt-0.7.0.tar.gz
Writing /var/folders/vl/nj7qbspd7hlgll3chjfgvl400000gn/T/easy_install-l6qjdcgd/requests-toolbelt-0.7.0/setup.cfg
Running requests-toolbelt-0.7.0/setup.py -q bdist_egg --dist-dir /var/folders/vl/nj7qbspd7hlgll3chjfgvl400000gn/T/easy_install-l6qjdcgd/requests-toolbelt-0.7.0/egg-dist-tmp-xbz2yomj
no previously-included directories found matching 'docs/_build'
warning: no previously-included files matching '*.py[cdo]' found anywhere in distribution
warning: no previously-included files matching '__pycache__' found anywhere in distribution
warning: no previously-included files matching '*.so' found anywhere in distribution
warning: no previously-included files matching '*.pyd' found anywhere in distribution
zip_safe flag not set; analyzing archive contents...
Copying requests_toolbelt-0.7.0-py3.5.egg to /Users/kentaro/porting/ven/lib/python3.5/site-packages
Adding requests-toolbelt 0.7.0 to easy-install.pth file

Installed /Users/kentaro/porting/ven/lib/python3.5/site-packages/requests_toolbelt-0.7.0-py3.5.egg
Searching for dicttoxml
Reading https://pypi.python.org/simple/dicttoxml/
Best match: dicttoxml 1.7.4
Downloading https://pypi.python.org/packages/74/36/534db111db9e7610a41641a1f6669a964aacaf51858f466de264cc8dcdd9/dicttoxml-1.7.4.tar.gz#md5=ec5643a048cf32dad3c28db236b923e4
Processing dicttoxml-1.7.4.tar.gz
Writing /var/folders/vl/nj7qbspd7hlgll3chjfgvl400000gn/T/easy_install-3yyvt_ku/dicttoxml-1.7.4/setup.cfg
Running dicttoxml-1.7.4/setup.py -q bdist_egg --dist-dir /var/folders/vl/nj7qbspd7hlgll3chjfgvl400000gn/T/easy_install-3yyvt_ku/dicttoxml-1.7.4/egg-dist-tmp-1oj02owk
zip_safe flag not set; analyzing archive contents...
Copying dicttoxml-1.7.4-py3.5.egg to /Users/kentaro/porting/ven/lib/python3.5/site-packages
Adding dicttoxml 1.7.4 to easy-install.pth file

Installed /Users/kentaro/porting/ven/lib/python3.5/site-packages/dicttoxml-1.7.4-py3.5.egg
Searching for requests==2.11.1
Best match: requests 2.11.1
Adding requests 2.11.1 to easy-install.pth file

Using /Users/kentaro/porting/ven/lib/python3.5/site-packages
Searching for prettytable==0.7.2
Best match: prettytable 0.7.2
Adding prettytable 0.7.2 to easy-install.pth file

Using /Users/kentaro/porting/ven/lib/python3.5/site-packages
Searching for argcomplete==1.4.1
Best match: argcomplete 1.4.1
Adding argcomplete 1.4.1 to easy-install.pth file

Using /Users/kentaro/porting/ven/lib/python3.5/site-packages
Searching for Pygments==2.1.3
Best match: Pygments 2.1.3
Adding Pygments 2.1.3 to easy-install.pth file
Installing pygmentize script to /Users/kentaro/porting/ven/bin

Using /Users/kentaro/porting/ven/lib/python3.5/site-packages
Finished processing dependencies for cloudmonkey==5.3.3

インストールに成功した。

$ cloudmonkey

Traceback (most recent call last):
  File "/Users/kentaro/porting/ven/bin/cloudmonkey", line 9, in <module>
    load_entry_point('cloudmonkey', 'console_scripts', 'cloudmonkey')()
  File "/Users/kentaro/porting/ven/lib/python3.5/site-packages/pkg_resources/__init__.py", line 542, in load_entry_point
    return get_distribution(dist).load_entry_point(group, name)
  File "/Users/kentaro/porting/ven/lib/python3.5/site-packages/pkg_resources/__init__.py", line 2569, in load_entry_point
    return ep.load()
  File "/Users/kentaro/porting/ven/lib/python3.5/site-packages/pkg_resources/__init__.py", line 2229, in load
    return self.resolve()
  File "/Users/kentaro/porting/ven/lib/python3.5/site-packages/pkg_resources/__init__.py", line 2235, in resolve
    module = __import__(self.module_name, fromlist=['__name__'], level=0)
  File "/Users/kentaro/porting/cloudstack-cloudmonkey/cloudmonkey/cloudmonkey.py", line 48
    except ImportError, e:
                      ^
SyntaxError: invalid syntax

でもエラーになって終了しました。

今日はここまで。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0