├── MANIFEST.in ├── MANIFEST ├── .gitignore ├── setup.py ├── README.md ├── test.py └── wordpress.py /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README* 2 | include *.py 3 | -------------------------------------------------------------------------------- /MANIFEST: -------------------------------------------------------------------------------- 1 | README.md 2 | setup.py 3 | test.py 4 | wordpress.py 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[co] 2 | 3 | # Packages 4 | *.egg 5 | *.egg-info 6 | dist 7 | build 8 | eggs 9 | parts 10 | bin 11 | develop-eggs 12 | .installed.cfg 13 | 14 | # Installer logs 15 | pip-log.txt 16 | 17 | # Unit test / coverage reports 18 | .coverage 19 | .tox 20 | .cache 21 | .DS_Store 22 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | with open('README.md') as f: 4 | readme = f.read() 5 | 6 | setup( 7 | name = "python-wordpress", 8 | description = "A Python client for the WordPress JSON API plugin", 9 | long_description = readme, 10 | version = "0.1.3", 11 | author = "Chris Amico", 12 | author_email = "eyeseast@gmail.com", 13 | py_modules = ['wordpress'], 14 | url = "https://github.com/eyeseast/python-wordpress", 15 | license = "MIT", 16 | install_requires = ['httplib2'], 17 | classifiers = [ 18 | 'Environment :: Web Environment', 19 | "Intended Audience :: Developers", 20 | "Natural Language :: English", 21 | "Operating System :: OS Independent", 22 | "Programming Language :: Python", 23 | "Topic :: Software Development :: Libraries :: Python Modules", 24 | ] 25 | ) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Python WordPress 2 | ================== 3 | 4 | A simple Python library for talking to Wordpress in JSON. This library relies on the [WordPress JSON API plugin][1]. This won't work if your WordPress site doesn't have the plugin installed. 5 | 6 | [1]: http://wordpress.org/extend/plugins/json-api/ "WordPress JSON API" 7 | 8 | Install: 9 | 10 | $ pip install python-wordpress 11 | 12 | Or `git clone` and `python setup.py install` and such. 13 | 14 | Usage: 15 | 16 | >>> from wordpress import WordPress 17 | >>> wp = WordPress('http://example.com/blog/') 18 | >>> posts = wp.get_recent_posts() 19 | 20 | For now, this only covers the *read* portions of the API under the `core` controller. See the [WordPress JSON API documentation][2] for details. 21 | 22 | [2]: http://wordpress.org/extend/plugins/json-api/other_notes/ 23 | 24 | Tests assume a WordPress blog running on [MAMP][3] at http://localhost:8888/wordpress, but you can change that by setting `WORDPRESS_BLOG_URL` as an environment variable, like so: 25 | 26 | WORDPRESS_BLOG_URL=http://wordpress.local.host python test.py 27 | 28 | [3]: http://www.mamp.info/en/index.html "MAMP!" -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import httplib2 4 | import os 5 | import unittest 6 | import urlparse 7 | from wordpress import WordPress, json, DEFAULT_METHODS 8 | 9 | class WordPressTest(unittest.TestCase): 10 | 11 | def setUp(self): 12 | self.blog_url = os.environ.get('WORDPRESS_BLOG_URL', 'http://localhost:8888/wordpress') 13 | self.http = httplib2.Http() 14 | self.wp = WordPress(self.blog_url, cache=None) 15 | 16 | def check_response(self, result, url): 17 | "Check that the parsed result matches the given URL" 18 | r, c = self.http.request(url) 19 | c = json.loads(c) 20 | self.assertEqual(c, result) 21 | 22 | def test_get_info(self): 23 | "Test basic info" 24 | result = self.wp.info() 25 | self.check_response(result, '%s/?dev=1&json=info' % self.blog_url) 26 | 27 | def test_get_recent_posts(self): 28 | "Test getting recent posts" 29 | result = self.wp.get_recent_posts() 30 | self.check_response(result, '%s/api/get_recent_posts/?dev=1' % self.blog_url) 31 | 32 | def test_get_post(self): 33 | """ 34 | Test getting a single post 35 | """ 36 | # don't assume there's a post with ID 1 37 | # this assumes get_recent_posts works, will raise KeyError if not 38 | results = self.wp.get_recent_posts(count=3) 39 | for post in results['posts']: 40 | ID = post['id'] 41 | post = self.wp.get_post(id=ID) 42 | self.check_response(post, '%s/?json=get_post&id=%s&dev=1' % (self.blog_url, ID)) 43 | 44 | def test_get_categories(self): 45 | """ 46 | Test getting all active categories 47 | """ 48 | result = self.wp.get_category_index() 49 | self.check_response(result, '%s/?json=get_category_index&dev=1' % self.blog_url) 50 | 51 | def test_bad_method(self): 52 | """ 53 | Trying to call something that isn't a real method should raise AttributeError 54 | """ 55 | # for the record, this is an odd way to test this 56 | with self.assertRaises(AttributeError): 57 | self.wp.do_something_bad 58 | 59 | def test_proxy(self): 60 | """ 61 | WordPress should return the JSON version of any path. 62 | """ 63 | result = self.wp.proxy('/') 64 | self.check_response(result, '%s/?json=1' % self.blog_url) 65 | 66 | 67 | if __name__ == '__main__': 68 | unittest.main() -------------------------------------------------------------------------------- /wordpress.py: -------------------------------------------------------------------------------- 1 | """ 2 | A Python client for the WordPress JSON API 3 | """ 4 | # This library is aimed at easier communication between 5 | # Python-based projects and a wordpress installation. 6 | 7 | __author__ = "Chris Amico (eyeseast@gmail.com)" 8 | __version__ = "0.1.2" 9 | __license__ = "MIT" 10 | 11 | import httplib2 12 | import os 13 | import urllib 14 | import urlparse 15 | try: 16 | import json 17 | except ImportError: 18 | import simplejson as json 19 | 20 | __all__ = ('WordPress', 'WordPressError') 21 | 22 | # Setting debug to True will add requested URLs to returned JSON 23 | DEBUG = False 24 | 25 | DEFAULT_METHODS = [ 26 | 'info', 27 | 'get_recent_posts', 28 | 'get_post', 29 | 'get_page', 30 | 'get_date_posts', 31 | 'get_category_posts', 32 | 'get_tag_posts', 33 | 'get_author_posts', 34 | 'get_search_results', 35 | 'get_date_index', 36 | 'get_category_index', 37 | 'get_tag_index', 38 | 'get_author_index', 39 | 'get_page_index', 40 | # 'get_nonce' 41 | ] 42 | 43 | class WordPressError(Exception): 44 | """ 45 | An error for things that break with WordPress 46 | """ 47 | 48 | # ## WordPress 49 | # Create an instance with a blog's URL, and cache setting. WordPress uses 50 | # [httplib2](http://code.google.com/p/httplib2/) under the hood, and caching 51 | # is pluggable. It's possible to pass in a `django.core.cache.cache` object 52 | # here to use the same cache backend you've set up for a Django project. 53 | # 54 | # See [httplib2 docs](http://httplib2.googlecode.com/hg/doc/html/libhttplib2.html#id1) 55 | # for more on cache backends. 56 | class WordPress(object): 57 | """ 58 | The main wrapper 59 | """ 60 | def __init__(self, blog_url, cache='.cache'): 61 | self.dev = 0 62 | self.blog_url = blog_url 63 | if not self.blog_url.endswith('?'): 64 | self.blog_url += "?" 65 | self._http = httplib2.Http(cache) 66 | 67 | def __repr__(self): 68 | return "" % self.blog_url.rstrip('?') 69 | 70 | # method does most of the dirty work. Pass in an API method and 71 | # some kwargs and it grabs, parses and returns some useful JSON 72 | def method(self, method_name, **kwargs): 73 | """ 74 | Build a URL for this method + kwargs, then fetch it. 75 | """ 76 | kwargs.setdefault('dev', self.dev) 77 | kwargs['json'] = method_name 78 | url = self.blog_url + urllib.urlencode(kwargs) 79 | 80 | return self.fetch(url) 81 | 82 | def fetch(self, url): 83 | "Grab, parse and return the actual response" 84 | r, c = self._http.request(url) 85 | c = json.loads(c) 86 | if c.get('status', '').lower() == "error" or "error" in c: 87 | raise WordPressError(c.get('error')) 88 | 89 | if DEBUG: 90 | c['_url'] = url 91 | return c 92 | 93 | def proxy(self, path): 94 | """ 95 | Get the JSON version of the given path, joined with self.blog_url 96 | """ 97 | url = urlparse.urljoin(self.blog_url, path) + '?' 98 | url += urllib.urlencode({'json': 1}) 99 | 100 | return self.fetch(url) 101 | 102 | # The WordPress JSON API is really simple. Give it a named method 103 | # in a query string--`?json=get_recent_posts`--and it returns JSON. 104 | # Since all we're doing is passing methods and arguments, there's 105 | # little point re-defining those methods in Python 106 | def __getattr__(self, method_name): 107 | """ 108 | Return a callable API method if `method` is in self._methods 109 | """ 110 | if method_name in DEFAULT_METHODS: 111 | def _api_method(**kwargs): 112 | return self.method(method_name, **kwargs) 113 | _api_method.__name__ = method_name 114 | return _api_method 115 | else: 116 | raise AttributeError 117 | --------------------------------------------------------------------------------