├── .gitignore ├── CHANGES.md ├── LICENSE.md ├── MANIFEST.in ├── README.md ├── README.rst ├── magento ├── __init__.py ├── magento_api.py └── magento_ipython_shell.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.egg-info 3 | dist 4 | build 5 | 6 | .python-magento -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | v0.1.0, 2013/2/13 -- Initial release. 2 | 3 | v0.2.0, 2013/2/13 -- Docs. Extra methods. 4 | 5 | v1.0.0, 2015/7/11 -- Python 3 compatibility. 6 | 7 | v1.0.1, 2015/9/25 -- Fix https regression. 8 | 9 | v1.0.2, 2015/10/7 -- Fix python2 regression. 10 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2013 Vikram Oberoi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *.md 2 | include *.rst 3 | 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # python-magento 2 | 3 | This is a simple Python interface to Magento's XML-RPC API. The API discovers and 4 | makes all of Magento's API methods available to you. 5 | 6 | Note that this is only for magento 1, for magento 2 you would need a different library! 7 | 8 | ## Usage 9 | 10 | ```python 11 | from magento import MagentoAPI 12 | 13 | magento = MagentoAPI("magentohost.com", 80, "test_api_user", "test_api_key") 14 | 15 | magento.help() # Prints out all resources discovered and available. 16 | # cart: create, info, license, order, totals 17 | # cart_coupon: add, remove 18 | # ... (a bunch of other resources) 19 | # sales_order: addComment, cancel, hold, info, list, unhold 20 | 21 | magento.sales_order.help() # 'sales_order' is a resource. 22 | # sales_order: Order API 23 | # - addComment: Add comment to order 24 | # - cancel: Cancel order 25 | # - hold: Hold order 26 | # - info: Retrieve order information 27 | # - list: Retrieve list of orders by filters 28 | # - unhold: Unhold order 29 | 30 | # Let's list sales and add their subtotals! 31 | orders = magento.sales_order.list() 32 | subtotals = [order["subtotal"] for order in orders] 33 | revenue = sum(subtotals) 34 | 35 | # Additionally, you can get API metadata from these calls: 36 | json_description_of_resources = magento.resources() 37 | json_description_of_possible_global_exceptions = magento.global_faults() 38 | json_description_of_possible_resource_exceptions = magento.resource_faults("sales_order") 39 | ``` 40 | 41 | The API discovers and makes all of Magento's API methods available to you. The 42 | best way to learn how to use the API is to play around with it in a Python shell 43 | and refer back to the [Magento API documentation](http://www.magentocommerce.com/api/soap/introduction.html) for docs on the usage of specific methods. 44 | 45 | ## Quick IPython Shell 46 | 47 | The Magento API is massive and takes effort to grok. If you need to use it in some 48 | production capacity, you'll want to jump into a shell frequently and muck around 49 | with inputs and stare at outputs. 50 | 51 | `magento-ipython-shell` will drop you into an IPython shell that has a variable 52 | bound to a MagentoAPI object that is ready for use. 53 | 54 | The shell requires IPython, which is the bee's knees. Install it and get it 55 | working first. Alternately, spin up a Python shell and instantiate the objects 56 | you need. This is just a slightly nicer way to get started mucking around. 57 | 58 | Here's how to launch it: 59 | 60 | ``` 61 | > magento-ipython-shell localhost.com 8888 api_user api_key 62 | 63 | -- magento-ipython-shell ----------------- 64 | Connecting to 'http://localhost.com:8888/magento/api/xmlrpc' 65 | Using API user/key api_user/api_key 66 | Connected! The 'magento' variable is bound to a usable MagentoAPI instance. 67 | -- magento-ipython-shell ----------------- 68 | 69 | Python 2.7.2 (default, Jun 16 2012, 12:38:40) 70 | Type "copyright", "credits" or "license" for more information. 71 | 72 | IPython 0.13.1 -- An enhanced Interactive Python. 73 | ? -> Introduction and overview of IPython's features. 74 | %quickref -> Quick reference. 75 | help -> Python's own help system. 76 | object? -> Details about 'object', use 'object??' for extra details. 77 | 78 | In [1]: 79 | ``` 80 | 81 | Now you can mess around with the `magento` instance. 82 | 83 | ``` 84 | In [1] magento 85 | Out[1]: 86 | 87 | In [2]: magento.help() # Lists all the resources available and their methods. 88 | Resources: 89 | 90 | cart: create, info, license, order, totals 91 | cart_coupon: add, remove 92 | ... (many more) 93 | 94 | In [3]: magento.cart.help() # Describes the methods available under a resource. 95 | cart: Shopping Cart 96 | - create: Create shopping cart 97 | - info: Retrieve information about shopping cart 98 | - license: Get terms and conditions 99 | - order: Create an order from shopping cart 100 | - totals: Get total prices for shopping cart 101 | 102 | In [4]: len(magento.sales_order.list()) # Play around with output. 103 | Out[4]: 2 104 | ``` 105 | 106 | ## Installation 107 | 108 | python-magento is on PyPi: 109 | 110 | * `pip install python-magento` 111 | * `easy_install python-magento` 112 | 113 | ... or grab this code and run `setup.py install` 114 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | python-magento 2 | ============== 3 | 4 | This is a simple Python interface to Magento's XML-RPC API. The API 5 | discovers and makes all of Magento's API methods available to you. 6 | 7 | Usage 8 | ----- 9 | 10 | .. code:: python 11 | 12 | from magento import MagentoAPI 13 | 14 | magento = MagentoAPI("magentohost.com", 80, "test_api_user", "test_api_key") 15 | 16 | magento.help() # Prints out all resources discovered and available. 17 | # cart: create, info, license, order, totals 18 | # cart_coupon: add, remove 19 | # ... (a bunch of other resources) 20 | # sales_order: addComment, cancel, hold, info, list, unhold 21 | 22 | magento.sales_order.help() # 'sales_order' is a resource. 23 | # sales_order: Order API 24 | # - addComment: Add comment to order 25 | # - cancel: Cancel order 26 | # - hold: Hold order 27 | # - info: Retrieve order information 28 | # - list: Retrieve list of orders by filters 29 | # - unhold: Unhold order 30 | 31 | # Let's list sales and add their subtotals! 32 | orders = magento.sales_order.list() 33 | subtotals = [order["subtotal"] for order in orders] 34 | revenue = sum(subtotals) 35 | 36 | # Additionally, you can get API metadata from these calls: 37 | json_description_of_resources = magento.resources() 38 | json_description_of_possible_global_exceptions = magento.global_faults() 39 | json_description_of_possible_resource_exceptions = magento.resource_faults("sales_order") 40 | 41 | The API discovers and makes all of Magento's API methods available to 42 | you. The best way to learn how to use the API is to play around with it 43 | in a Python shell and refer back to the `Magento API 44 | documentation `__ 45 | for docs on the usage of specific methods. 46 | 47 | Quick IPython Shell 48 | ------------------- 49 | 50 | The Magento API is massive and takes effort to grok. If you need to use 51 | it in some production capacity, you'll want to jump into a shell 52 | frequently and muck around with inputs and stare at outputs. 53 | 54 | ``magento-ipython-shell`` will drop you into an IPython shell that has a 55 | variable bound to a MagentoAPI object that is ready for use. 56 | 57 | The shell requires IPython, which is the bee's knees. Install it and get 58 | it working first. Alternately, spin up a Python shell and instantiate 59 | the objects you need. This is just a slightly nicer way to get started 60 | mucking around. 61 | 62 | Here's how to launch it: 63 | 64 | :: 65 | 66 | > magento-ipython-shell localhost.com 8888 api_user api_key 67 | 68 | -- magento-ipython-shell ----------------- 69 | Connecting to 'http://localhost.com:8888/magento/api/xmlrpc' 70 | Using API user/key api_user/api_key 71 | Connected! The 'magento' variable is bound to a usable MagentoAPI instance. 72 | -- magento-ipython-shell ----------------- 73 | 74 | Python 2.7.2 (default, Jun 16 2012, 12:38:40) 75 | Type "copyright", "credits" or "license" for more information. 76 | 77 | IPython 0.13.1 -- An enhanced Interactive Python. 78 | ? -> Introduction and overview of IPython's features. 79 | %quickref -> Quick reference. 80 | help -> Python's own help system. 81 | object? -> Details about 'object', use 'object??' for extra details. 82 | 83 | In [1]: 84 | 85 | Now you can mess around with the ``magento`` instance. 86 | 87 | :: 88 | 89 | In [1] magento 90 | Out[1]: 91 | 92 | In [2]: magento.help() # Lists all the resources available and their methods. 93 | Resources: 94 | 95 | cart: create, info, license, order, totals 96 | cart_coupon: add, remove 97 | ... (many more) 98 | 99 | In [3]: magento.cart.help() # Describes the methods available under a resource. 100 | cart: Shopping Cart 101 | - create: Create shopping cart 102 | - info: Retrieve information about shopping cart 103 | - license: Get terms and conditions 104 | - order: Create an order from shopping cart 105 | - totals: Get total prices for shopping cart 106 | 107 | In [4]: len(magento.sales_order.list()) # Play around with output. 108 | Out[4]: 2 109 | 110 | Installation 111 | ------------ 112 | 113 | python-magento is on PyPi: 114 | 115 | - ``pip install python-magento`` 116 | - ``easy_install python-magento`` 117 | 118 | ... or grab this code and run ``setup.py install`` 119 | -------------------------------------------------------------------------------- /magento/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | 4 | from magento.magento_api import MagentoAPI 5 | assert MagentoAPI 6 | -------------------------------------------------------------------------------- /magento/magento_api.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | 4 | from __future__ import print_function 5 | 6 | try: 7 | import xmlrpclib 8 | except ImportError: 9 | import xmlrpc.client as xmlrpclib 10 | 11 | 12 | DEFAULT_XMLRPC_PATH = '/api/xmlrpc' 13 | 14 | 15 | # Some text formatting stuff for the shell. 16 | def bold(text): 17 | return u'\033[1m%s\033[0m' % text 18 | 19 | 20 | class MagentoTransportMixin(object): 21 | 22 | def __init__(self, *args, **kwargs): 23 | super(MagentoTransportMixin, self).__init__(*args, **kwargs) 24 | 25 | def send_content(self, connection, request_body): 26 | # OWASP ModSecurity Core Rule Set (CRS) Project / cPanel mod_security 27 | # blocks requests that lack an accept header, so add one here 28 | connection.putheader('Accept', 'application/xml') 29 | super(MagentoTransportMixin, self).send_content( 30 | connection, request_body) 31 | 32 | 33 | class MagentoSafeTransport(xmlrpclib.SafeTransport, MagentoTransportMixin): 34 | """Magento XMLRPClib Safe Transport.""" 35 | pass 36 | 37 | 38 | class MagentoTransport(xmlrpclib.Transport, MagentoTransportMixin): 39 | """Magento XMLRPClib Transport.""" 40 | pass 41 | 42 | 43 | class MagentoAPI(object): 44 | 45 | def __init__(self, host, port, api_user, api_key, path=DEFAULT_XMLRPC_PATH, 46 | allow_none=False, verbose=False, proto='http'): 47 | """Logs the client into Magento's API and discovers methods available 48 | to it. Throws an exception if logging in fails. 49 | """ 50 | 51 | self._api_user = api_user 52 | self._api_key = api_key 53 | self._host = host 54 | self._port = str(port) 55 | self._uri = '{}://{}:{}/{}'.format(proto, host, port, path.strip('/')) 56 | 57 | if proto == 'https': 58 | transport = MagentoSafeTransport() 59 | else: 60 | transport = MagentoTransport() 61 | 62 | self._client = xmlrpclib.ServerProxy( 63 | self._uri, allow_none=allow_none, verbose=verbose, 64 | transport=transport) 65 | self.login() 66 | 67 | def __enter__(self): 68 | return self 69 | 70 | def __exit__(self, type, value, traceback): 71 | self.end_session() 72 | 73 | def _discover(self): 74 | """Discovers methods in the XML-RPC API and creates attributes for them 75 | on this object. Enables stuff like "magento.cart.create(...)" to work 76 | without having to define Python methods for each XML-RPC equivalent. 77 | """ 78 | 79 | self._resources = {} 80 | resources = self._client.resources(self._session_id) 81 | for resource in resources: 82 | self._resources[resource['name']] = MagentoResource( 83 | self._client, self._session_id, resource['name'], 84 | resource['title'], resource['methods']) 85 | 86 | def _get_client(self): 87 | """Returns the XML-RPC client, an xmlrpclib.ServerProxy 88 | instance that's connected to the Magento API. Nice for debugging. 89 | """ 90 | 91 | return self._client 92 | 93 | def _get_session_id(self): 94 | """Returns the cart session ID. You'll need it for debugging if you're 95 | sending calls through the client you receive by calling _get_client. 96 | """ 97 | 98 | return self._session_id 99 | 100 | def __getattr__(self, name): 101 | """Intercepts valid Magento paths (e.g. "cart.create") to return 102 | functions that make a valid path's corresponding API call to 103 | the Magento server. 104 | """ 105 | 106 | if name in self._resources: 107 | return self._resources[name] 108 | else: 109 | raise AttributeError 110 | 111 | def login(self): 112 | self._session_id = self._client.login(self._api_user, self._api_key) 113 | self._discover() 114 | 115 | def end_session(self): 116 | self._client.endSession(self._session_id) 117 | 118 | def keep_session_alive(self): 119 | """If the session expired, logs back in.""" 120 | 121 | try: 122 | self.resources() 123 | except xmlrpclib.Fault as fault: 124 | if fault.faultCode == 5: 125 | self.login() 126 | else: 127 | raise 128 | 129 | def resources(self): 130 | """Calls the 'resources' Magento API method. From the Magento docs: 131 | 132 | 'Return a list of available API resources and methods allowed for the 133 | current session.' 134 | """ 135 | 136 | return self._client.resources(self._session_id) 137 | 138 | def global_faults(self): 139 | """Calls the 'globalFaults' Magento API method. From the Magento docs: 140 | 141 | 'Return a list of fault messages and their codes that do not depend on 142 | any resource.' 143 | """ 144 | 145 | return self._client.globalFaults(self._session_id) 146 | 147 | def resource_faults(self, resource_name): 148 | """Calls the 'resourceFaults' Magento API method. From the Magento docs: 149 | 150 | 'Return a list of the specified resource fault messages, if this 151 | resource is allowed in the current session.' 152 | """ 153 | 154 | return self._client.resourceFaults(self._session_id, resource_name) 155 | 156 | def help(self): 157 | """Prints discovered resources and their associated methods. Nice when 158 | noodling in the terminal to wrap your head around Magento's insanity. 159 | """ 160 | 161 | print('Resources:') 162 | print('') 163 | for name in sorted(self._resources.keys()): 164 | methods = sorted(self._resources[name]._methods.keys()) 165 | print('{}: {}'.format(bold(name), ', '.join(methods))) 166 | 167 | def get_host(self): 168 | return self._host 169 | 170 | def get_port(self): 171 | return self._port 172 | 173 | def get_api_user(self): 174 | return self._api_user 175 | 176 | def get_api_key(self): 177 | return self._api_key 178 | 179 | 180 | class MagentoResource(object): 181 | 182 | def __init__(self, client, session_id, name, title, methods): 183 | self._client = client 184 | self._session_id = session_id 185 | self._name = name 186 | self._title = title 187 | self._methods = {} 188 | 189 | for method in methods: 190 | self._methods[method['name']] = method 191 | 192 | def __getattr__(self, name): 193 | if name in self._methods: 194 | return self._get_method_call(name) 195 | else: 196 | raise AttributeError 197 | 198 | def _get_method_call(self, method_name): 199 | path = '.'.join([self._name, method_name]) 200 | 201 | def call_method(*args, **kwargs): 202 | # If we don't have a list of arguments, pass a dictionary. 203 | if not args: 204 | args = kwargs 205 | 206 | return self._client.call(self._session_id, path, args) 207 | return call_method 208 | 209 | def help(self): 210 | print('{}: {}'.format(bold(self._name), self._title)) 211 | for method in sorted(self._methods.keys()): 212 | print(' - {}: {}'.format( 213 | bold(method), self._methods[method]['title'])) 214 | -------------------------------------------------------------------------------- /magento/magento_ipython_shell.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | 4 | from __future__ import print_function 5 | 6 | import sys 7 | import argparse 8 | 9 | from magento.magento_api import MagentoAPI, DEFAULT_XMLRPC_PATH 10 | 11 | try: 12 | from IPython import embed 13 | except: 14 | print('You must have IPython installed to use this shell.\n' 15 | 'Try "pip install ipython", "easy_install ipython", or head ' 16 | 'over to ipython.org') 17 | sys.exit(1) 18 | 19 | 20 | def main(): 21 | parser = argparse.ArgumentParser(description='Launch an IPython shell ' 22 | 'with a MagentoAPI instance, "magento", ' 23 | 'connected to a given endpoint.') 24 | parser.add_argument('host', help='The Magento server host.') 25 | parser.add_argument('port', type=int, default=80, 26 | help='The Magento server port.') 27 | parser.add_argument('api_user', help='The API user to log in as.') 28 | parser.add_argument('api_key', help='The API key to log in with.') 29 | parser.add_argument('-p', '--path', default=DEFAULT_XMLRPC_PATH, 30 | help='The URL path to the XML-RPC API.') 31 | parser.add_argument('-v', '--verbose', action='store_true', 32 | help='Set the XML-RPC client to verbose.') 33 | parser.add_argument ('--proto', default='http', help='Choose between http or https') 34 | args = parser.parse_args() 35 | 36 | url = 'http://{}:{}/{}'.format(args.host, args.port, args.path.strip('/')) 37 | 38 | print('\n\n-- magento-ipython-shell -----------------') 39 | print('Connecting to "{}"'.format(url)) 40 | print('Using API user/key {}/{}'.format(args.api_user, args.api_key)) 41 | 42 | magento = MagentoAPI(args.host, args.port, args.api_user, args.api_key, 43 | path=args.path, verbose=args.verbose, proto=args.proto) 44 | assert magento 45 | 46 | print('Connected! The "magento" variable is bound to a usable MagentoAPI ' 47 | 'instance.\n' 48 | '-- magento-ipython-shell -----------------\n\n') 49 | embed() # Shell time! 50 | 51 | if __name__ == '__main__': 52 | main() 53 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | 4 | try: 5 | from setuptools import setup 6 | except ImportError: 7 | from distutils.core import setup 8 | 9 | 10 | setup( 11 | name='python-magento', 12 | version='1.0.3', 13 | author='Vikram Oberoi', 14 | author_email='voberoi@gmail.com', 15 | maintainer='Bernard Kerckenaere', 16 | maintainer_email='bernieke@bernieke.com', 17 | packages=['magento'], 18 | install_requires=[], 19 | extras_require={ 20 | 'interactive_shell': ['ipython'], 21 | }, 22 | entry_points={ 23 | 'console_scripts': [ 24 | 'magento-ipython-shell = ' 25 | 'magento.magento_ipython_shell:main [interactive_shell]', 26 | ], 27 | }, 28 | url='https://github.com/bernieke/python-magento', 29 | license='MIT License', 30 | description='A Python wrapper to the Magento XML-RPC API.', 31 | long_description=open('README.rst').read(), 32 | classifiers=( 33 | 'Development Status :: 5 - Production/Stable', 34 | 'Intended Audience :: Developers', 35 | 'Natural Language :: English', 36 | 'License :: OSI Approved :: MIT License', 37 | 'Programming Language :: Python', 38 | 'Programming Language :: Python :: 2.7', 39 | 'Programming Language :: Python :: 3.3', 40 | 'Programming Language :: Python :: 3.4', 41 | 'Programming Language :: Python :: 3.5', 42 | ) 43 | ) 44 | --------------------------------------------------------------------------------