Permalink
Browse files

create pypi install script

  • Loading branch information...
1 parent b53fdb3 commit edf60e9fe2c0e882c3e3762149b521b551319371 @michaelliao committed Sep 26, 2012
Showing with 245 additions and 365 deletions.
  1. +2 −0 .gitignore
  2. +3 −0 CHANGES.txt
  3. +202 −0 LICENSE.txt
  4. +9 −5 README
  5. +28 −0 setup.py
  6. +0 −359 src/weibo1.py
  7. +1 −1 {src → }/weibo.py
View
2 .gitignore
@@ -38,4 +38,6 @@
Icon?
ehthumbs.db
Thumbs.db
+dist
+MANIFEST
View
3 CHANGES.txt
@@ -0,0 +1,3 @@
+Version 1.0.5 released 2012-9-26
+
+* Initial release.
View
202 LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
View
14 README
@@ -1,8 +1,12 @@
-欢迎使用Sina Weibo Python客户端!
+This software is a 100% Python interface to Sina weibo OAuth2 API.
+It is the client side software which allows access Sina weibo API.
+Visit http://open.weibo.com/ for more information.
-sinaweibopy是Python专用的支持新浪微博API的OAuth 1、OAuth 2客户端,无依赖,100%纯Py,单个文件,代码简洁,运行可靠,也是新浪微博官方推荐的Python SDK:
+This package was originally written by Michael Liao. You can find
+docs and source code at:
-http://open.weibo.com/wiki/SDK#Python_SDK
+http://michaelliao.github.com/sinaweibopy/
-详细文档请移步:
-http://michaelliao.github.com/sinaweibopy/
+Please report bugs or any suggestions at:
+
+https://github.com/michaelliao/sinaweibopy
View
28 setup.py
@@ -0,0 +1,28 @@
+from distutils.core import setup
+import sys
+
+kw = dict(
+ name = 'sinaweibopy',
+ version = '1.0.5',
+ description = 'Sina Weibo OAuth2 API Python SDK',
+ long_description = open('README', 'r').read(),
+ author = 'Michael Liao',
+ author_email = 'askxuefeng@gmail.com',
+ url = 'https://github.com/michaelliao/sinaweibopy',
+ download_url = 'https://github.com/michaelliao/sinaweibopy',
+ py_modules = ['weibo'],
+ classifiers = [
+ 'Development Status :: 5 - Production/Stable',
+ 'Environment :: Web Environment',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: Apache Software License',
+ 'Operating System :: OS Independent',
+ 'Programming Language :: Python',
+ 'Topic :: Internet',
+ 'Topic :: Software Development :: Libraries :: Python Modules',
+ ])
+
+if sys.version_info[1]==5:
+ kw['install_requires'] = ['simplejson']
+
+setup(**kw)
View
359 src/weibo1.py
@@ -1,359 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-__version__ = '1.0'
-__author__ = 'Liao Xuefeng (askxuefeng@gmail.com)'
-
-'''
-Python client SDK for sina weibo API using OAuth 1.0
-'''
-
-try:
- import json
-except ImportError:
- import simplejson as json
-import time
-import hmac
-import uuid
-import base64
-import urllib
-import urllib2
-import hashlib
-import logging
-
-_OAUTH_SIGN_METHOD = 'HMAC-SHA1'
-_OAUTH_VERSION = '1.0'
-
-class OAuthToken(object):
-
- def __init__(self, oauth_token, oauth_token_secret, oauth_verifier=None, **kw):
- self.oauth_token = oauth_token
- self.oauth_token_secret = oauth_token_secret
- self.oauth_verifier = oauth_verifier
- for k, v in kw.iteritems():
- setattr(self, k, v)
-
- def __str__(self):
- attrs = [s for s in dir(self) if not s.startswith('__')]
- kvs = ['%s = %s' % (k, getattr(self, k)) for k in attrs]
- return ', '.join(kvs)
-
- __repr__ = __str__
-
-class APIClient(object):
- def __init__(self, app_key, app_secret, token=None, callback=None, domain='api.t.sina.com.cn'):
- self.app_key = str(app_key)
- self.app_secret = str(app_secret)
- if token:
- if isinstance(token, OAuthToken):
- if token.oauth_token:
- self.oauth_token = token.oauth_token
- if token.oauth_token_secret:
- self.oauth_token_secret = token.oauth_token_secret
- if token.oauth_verifier:
- self.oauth_verifier = token.oauth_verifier
- else:
- raise TypeError('token parameter must be instance of OAuthToken.')
- self.callback = callback
- self.api_url = 'http://%s' % domain
- self.get = HttpObject(self, _HTTP_GET)
- self.post = HttpObject(self, _HTTP_POST)
-
- def _oauth_request(self, method, url, **kw):
- params = dict( \
- oauth_consumer_key=self.app_key, \
- oauth_nonce=_generate_nonce(), \
- oauth_signature_method=_OAUTH_SIGN_METHOD, \
- oauth_timestamp=str(int(time.time())), \
- oauth_version=_OAUTH_VERSION, \
- oauth_token=self.oauth_token)
- params.update(kw)
- m = 'GET' if method==_HTTP_GET else 'POST'
- bs = _generate_base_string(m, url, **params)
- key = '%s&%s' % (self.app_secret, self.oauth_token_secret)
- oauth_signature = _generate_signature(key, bs)
- print 'params:', params
- print 'base string:', bs
- print 'key:', key, 'sign:', oauth_signature
- print 'url:', url
- r = _http_call(url, method, self.__build_oauth_header(params, oauth_signature=oauth_signature), **kw)
- return r
-
- def get_request_token(self):
- '''
- Step 1: request oauth token.
- Returns:
- OAuthToken object contains oauth_token and oauth_token_secret
- '''
- params = dict(oauth_callback=self.callback, \
- oauth_consumer_key=self.app_key, \
- oauth_nonce=_generate_nonce(), \
- oauth_signature_method=_OAUTH_SIGN_METHOD, \
- oauth_timestamp=str(int(time.time())), \
- oauth_version=_OAUTH_VERSION)
- url = '%s/oauth/request_token' % self.api_url
- bs = _generate_base_string('GET', url, **params)
- params['oauth_signature'] = base64.b64encode(hmac.new('%s&' % self.app_secret, bs, hashlib.sha1).digest())
- r = _http_call(url, _HTTP_GET, return_json=False, **params)
- kw = _parse_params(r, False)
- return OAuthToken(**kw)
-
- def get_authorize_url(self, oauth_token):
- '''
- Step 2: get authorize url and redirect to it.
- Args:
- oauth_token: oauth_token str that returned from request_token:
- oauth_token = client.request_token().oauth_token
- Returns:
- redirect url, e.g. "http://api.t.sina.com.cn/oauth/authorize?oauth_token=ABCD1234XYZ"
- '''
- return '%s/oauth/authorize?oauth_token=%s' % (self.api_url, oauth_token)
-
- def get_access_token(self):
- '''
- get access token from request token:
- request_token = OAuthToken(oauth_token, oauth_secret, oauth_verifier)
- client = APIClient(appkey, appsecret, request_token)
- access_token = client.get_access_token()
- '''
- params = {
- 'oauth_consumer_key': self.app_key,
- 'oauth_timestamp': str(int(time.time())),
- 'oauth_nonce': _generate_nonce(),
- 'oauth_version': _OAUTH_VERSION,
- 'oauth_signature_method': _OAUTH_SIGN_METHOD,
- 'oauth_token': self.oauth_token,
- 'oauth_verifier': self.oauth_verifier,
- }
- url = '%s/oauth/access_token' % self.api_url
- bs = _generate_base_string('GET', url, **params)
- key = '%s&%s' % (self.app_secret, self.oauth_token_secret)
- oauth_signature = _generate_signature(key, bs)
- authorization = self.__build_oauth_header(params, oauth_signature=oauth_signature)
- r = _http_call(url, _HTTP_GET, authorization, return_json=False)
- kw = _parse_params(r, False)
- return OAuthToken(**kw)
-
- def __build_oauth_header(self, params, **kw):
- '''
- build oauth header like: Authorization: OAuth oauth_token="xxx", oauth_nonce="123"
- Args:
- params: parameter dict.
- **kw: any additional key-value parameters.
- '''
- d = dict(**kw)
- d.update(params)
- L = [r'%s="%s"' % (k, v) for k, v in d.iteritems() if k.startswith('oauth_')]
- return 'OAuth %s' % ', '.join(L)
-
- def __getattr__(self, attr):
- ' a shortcut for client.get.funcall() to client.funcall() '
- return getattr(self.get, attr)
-
-def _obj_hook(pairs):
- '''
- convert json object to python object.
- '''
- o = JsonObject()
- for k, v in pairs.iteritems():
- o[str(k)] = v
- return o
-
-class APIError(StandardError):
- '''
- raise APIError if got failed json message.
- '''
- def __init__(self, error_code, error, request):
- self.error_code = error_code
- self.error = error
- self.request = request
- StandardError.__init__(self, error)
-
- def __str__(self):
- return 'APIError: %s: %s, request: %s' % (self.error_code, self.error, self.request)
-
-class JsonObject(dict):
- '''
- general json object that can bind any fields but also act as a dict.
- '''
- def __getattr__(self, attr):
- return self[attr]
-
- def __setattr__(self, attr, value):
- self[attr] = value
-
- def __getstate__(self):
- return self.copy()
-
- def __setstate__(self, state):
- self.update(state)
-
-def _encode_multipart(**kw):
- '''
- Build a multipart/form-data body with generated random boundary.
- '''
- boundary = '----------%s' % hex(int(time.time() * 1000))
- data = []
- for k, v in kw.iteritems():
- data.append('--%s' % boundary)
- if hasattr(v, 'read'):
- # file-like object:
- ext = ''
- filename = getattr(v, 'name', '')
- n = filename.rfind('.')
- if n != (-1):
- ext = filename[n:].lower()
- content = v.read()
- data.append('Content-Disposition: form-data; name="%s"; filename="hidden"' % k)
- data.append('Content-Length: %d' % len(content))
- data.append('Content-Type: %s\r\n' % _guess_content_type(ext))
- data.append(content)
- else:
- data.append('Content-Disposition: form-data; name="%s"\r\n' % k)
- data.append(v.encode('utf-8') if isinstance(v, unicode) else v)
- data.append('--%s--\r\n' % boundary)
- return '\r\n'.join(data), boundary
-
-_CONTENT_TYPES = { '.png': 'image/png', '.gif': 'image/gif', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.jpe': 'image/jpeg' }
-
-def _guess_content_type(ext):
- return _CONTENT_TYPES.get(ext, 'application/octet-stream')
-
-_HTTP_GET = 0
-_HTTP_POST = 1
-_HTTP_UPLOAD = 2
-
-def _http_call(the_url, method, authorization=None, return_json=True, **kw):
- '''
- send an http request and return headers and body if no error.
- '''
- params = None
- boundary = None
- if method==_HTTP_UPLOAD:
- params, boundary = _encode_multipart(**kw)
- else:
- params = _encode_params(**kw)
- http_url = '%s?%s' % (the_url, params) if method==_HTTP_GET and params else the_url
- http_body = None if method==_HTTP_GET else params
- req = urllib2.Request(http_url, data=http_body)
- if authorization:
- print 'Authorization:', authorization
- req.add_header('Authorization', authorization)
- if boundary:
- req.add_header('Content-Type', 'multipart/form-data; boundary=%s' % boundary)
- print method, http_url, 'BODY:', http_body
- resp = urllib2.urlopen(req)
- body = resp.read()
- if return_json:
- r = json.loads(body, object_hook=_obj_hook)
- if hasattr(r, 'error_code'):
- raise APIError(r.error_code, getattr(r, 'error', ''), getattr(r, 'request', ''))
- return r
- return body
-
-class HttpObject(object):
-
- def __init__(self, client, method):
- self.client = client
- self.method = method
-
- def __getattr__(self, attr):
- def wrap(**kw):
- return self.client._oauth_request(self.method, '%s/%s.json' % (self.client.api_url, attr.replace('__', '/')), **kw)
- return wrap
-
-################################################################################
-# utility functions
-################################################################################
-
-def _parse_params(params_str, unicode_value=True):
- '''
- parse a query string as JsonObject (also a dict)
- Args:
- params_str: query string as str.
- unicode_value: return unicode value if True, otherwise str value. default true.
- Returns:
- JsonObject (inherited from dict)
-
- >>> s = _parse_params('a=123&b=X%26Y&c=%E4%B8%AD%E6%96%87')
- >>> s.a
- u'123'
- >>> s.b
- u'X&Y'
- >>> s.c==u'\u4e2d\u6587'
- True
- >>> s = _parse_params('a=123&b=X%26Y&c=%E4%B8%AD%E6%96%87', False)
- >>> s.a
- '123'
- >>> s.b
- 'X&Y'
- >>> s.c=='\xe4\xb8\xad\xe6\x96\x87'
- True
- >>> s.d #doctest: +IGNORE_EXCEPTION_DETAIL
- Traceback (most recent call last):
- ...
- KeyError:
- '''
- d = dict()
- for s in params_str.split('&'):
- n = s.find('=')
- if n>0:
- key = s[:n]
- value = urllib.unquote(s[n+1:])
- d[key] = value.decode('utf-8') if unicode_value else value
- return JsonObject(**d)
-
-def _encode_params(**kw):
- '''
- Encode parameters.
- '''
- if kw:
- args = []
- for k, v in kw.iteritems():
- qv = v.encode('utf-8') if isinstance(v, unicode) else str(v)
- args.append('%s=%s' % (k, _quote(qv)))
- return '&'.join(args)
- return ''
-
-def _quote(s):
- '''
- quote everything including /
-
- >>> _quote(123)
- '123'
- >>> _quote(u'\u4e2d\u6587')
- '%E4%B8%AD%E6%96%87'
- >>> _quote('/?abc=def& _+%')
- '%2F%3Fabc%3Ddef%26%20_%2B%25'
- '''
- if isinstance(s, unicode):
- s = s.encode('utf-8')
- return urllib.quote(str(s), safe='')
-
-def _generate_nonce():
- ' generate random uuid as oauth_nonce '
- return uuid.uuid4().hex
-
-def _generate_signature(key, base_string):
- '''
- generate url-encoded oauth_signature with HMAC-SHA1
- '''
- return _quote(base64.b64encode(hmac.new(key, base_string, hashlib.sha1).digest()))
-
-def _generate_base_string(method, url, **params):
- '''
- generate base string for signature
-
- >>> method = 'GET'
- >>> url = 'http://www.sina.com.cn/news'
- >>> params = dict(a=1, b='A&B')
- >>> _generate_base_string(method, url, **params)
- 'GET&http%3A%2F%2Fwww.sina.com.cn%2Fnews&a%3D1%26b%3DA%2526B'
- '''
- plist = [(_quote(k), _quote(v)) for k, v in params.iteritems()]
- plist.sort()
- return '%s&%s&%s' % (method, _quote(url), _quote('&'.join(['%s=%s' % (k, v) for k, v in plist])))
-
-if __name__=='__main__':
- import doctest
- doctest.testmod()
View
2 src/weibo.py → weibo.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-__version__ = '1.04'
+__version__ = '1.0.5'
__author__ = 'Liao Xuefeng (askxuefeng@gmail.com)'
'''

0 comments on commit edf60e9

Please sign in to comment.