Skip to content

Commit 7ea957a

Browse files
committed
Merge pull request #58 from qiniu/develop
Release v6.1.0
2 parents 504fbec + e027dc9 commit 7ea957a

20 files changed

+649
-118
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
*.py[cod]
66

7+
my-test-env.sh
78

89
##
910
## from https://github.com/github/gitignore/blob/master/Python.gitignore

.travis.yml

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ language: python
22
python:
33
- "2.6"
44
- "2.7"
5+
install:
6+
- "pip install coverage --use-mirrors"
57
before_script:
68
- export QINIU_ACCESS_KEY="X0XpjFmLMTJpHB_ESHjeolCtipk-1U3Ok7LVTdoN"
79
- export QINIU_SECRET_KEY="wenlwkU1AYwNBf7Q9cCoG4VT_GYyrHE9AS_R2u81"
8-
- export QINIU_PIC_KEY="hello_jpg"
9-
- export QINIU_NOEXIST_PIC_KEY="no_exist"
10-
- export QINIU_BUCKET_NAME="pysdk"
11-
- export QINIU_DOMAIN="pysdk.qiniudn.com"
12-
script:
13-
- python setup.py nosetests
14-
- export PYTHONPATH="$PYTHONPATH:." && python docs/demo.py
10+
- export QINIU_TEST_BUCKET="pysdk"
11+
- export QINIU_TEST_DOMAIN="pysdk.qiniudn.com"
12+
- export PYTHONPATH="$PYTHONPATH:."
13+
script:
14+
- nosetests --with-coverage --cover-package=qiniu
15+
- python docs/demo.py
16+
- python docs/gist/conf.py

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
11
## CHANGE LOG
22

3+
### v6.1.0
4+
5+
2013-07-03 issue [#58](https://github.com/qiniu/python-sdk/pull/58)
6+
7+
- 实现最新版的上传API,<http://docs.qiniu.com/api/put.html>
8+
- io.PutExtra更新,废弃callback_params,bucket,和custom_meta,新增params
9+
- 修复[#16](https://github.com/qiniu/python-sdk/issues/16)
10+
- put接口可以传入类文件对象(file-like object)
11+
- 修复[#52](https://github.com/qiniu/python-sdk/issues/52)
12+
13+
314
### v6.0.1
415

516
2013-06-27 issue [#43](https://github.com/qiniu/python-sdk/pull/43)
617

718
- 遵循 [sdkspec v6.0.2](https://github.com/qiniu/sdkspec/tree/v6.0.2)
819
- 现在,rsf.list_prefix在没有更多数据时,err 会返回 rsf.EOF
920

21+
1022
### v6.0.0
1123

1224
2013-06-26 issue [#42](https://github.com/qiniu/python-sdk/pull/42)

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,23 @@ Qiniu Resource Storage SDK for Python
99

1010
参考文档:[七牛云存储 Python SDK 使用指南](https://github.com/qiniu/python-sdk/blob/develop/docs/README.md)
1111

12+
## 单元测试
13+
14+
1. 测试环境
15+
16+
1. [开通七牛开发者帐号](https://portal.qiniu.com/signup)
17+
2. [登录七牛开发者自助平台,查看 Access Key 和 Secret Key](https://portal.qiniu.com/setting/key) 。
18+
3. 在开发者后台新建一个空间
19+
20+
然后将在`test-env.sh`中填入相关信息。
21+
22+
2. 需安装[nosetests](https://nose.readthedocs.org/en/latest/)测试工具。
23+
24+
运行测试:
25+
26+
source test-env.sh
27+
nosetests
28+
1229
## 贡献代码
1330

1431
1. Fork

docs/README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -259,10 +259,12 @@ qiniu.conf.SECRET_KEY = "<YOUR_APP_SECRET_KEY>"
259259
260260
import qiniu.io
261261
262-
extra = qiniu.io.PutExtra(bucket_name)
262+
extra = qiniu.io.PutExtra()
263263
extra.mime_type = "text/plain"
264264
265-
ret, err = qiniu.io.put(uptoken, key, "hello!", extra)
265+
# data 可以是str或read()able对象
266+
data = StringIO.StringIO("hello!")
267+
ret, err = qiniu.io.put(uptoken, key, data, extra)
266268
if err is not None:
267269
error(err)
268270
return
@@ -279,9 +281,8 @@ qiniu.conf.SECRET_KEY = "<YOUR_APP_SECRET_KEY>"
279281
import qiniu.io
280282
281283
localfile = "%s" % __file__
282-
extra = qiniu.io.PutExtra(bucket_name)
283284
284-
ret, err = qiniu.io.put_file(uptoken, key, localfile, extra)
285+
ret, err = qiniu.io.put_file(uptoken, key, localfile)
285286
if err is not None:
286287
error(err)
287288
return

docs/demo.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# -*- coding: utf-8 -*-
22
import os
33
import sys
4+
import StringIO
45

56
# @gist import_io
67
import qiniu.io
@@ -49,9 +50,9 @@ def _setup():
4950
if access_key is None:
5051
exit("请配置环境变量 QINIU_ACCESS_KEY")
5152
secret_key = getenv("QINIU_SECRET_KEY")
52-
bucket_name = getenv("QINIU_BUCKET_NAME")
53-
domain = getenv("QINIU_DOMAIN")
54-
pickey = getenv("QINIU_PIC_KEY")
53+
bucket_name = getenv("QINIU_TEST_BUCKET")
54+
domain = getenv("QINIU_TEST_DOMAIN")
55+
pickey = 'QINIU_UNIT_TEST_PIC'
5556
setup(access_key, secret_key, bucket_name, domain, pickey)
5657

5758
def getenv(name):
@@ -95,9 +96,8 @@ def put_file():
9596

9697
# @gist put_file
9798
localfile = "%s" % __file__
98-
extra = qiniu.io.PutExtra(bucket_name)
99-
100-
ret, err = qiniu.io.put_file(uptoken, key, localfile, extra)
99+
100+
ret, err = qiniu.io.put_file(uptoken, key, localfile)
101101
if err is not None:
102102
error(err)
103103
return
@@ -110,10 +110,12 @@ def put_binary():
110110
qiniu.rs.Client().delete(bucket_name, key)
111111

112112
# @gist put
113-
extra = qiniu.io.PutExtra(bucket_name)
113+
extra = qiniu.io.PutExtra()
114114
extra.mime_type = "text/plain"
115115

116-
ret, err = qiniu.io.put(uptoken, key, "hello!", extra)
116+
# data 可以是str或read()able对象
117+
data = StringIO.StringIO("hello!")
118+
ret, err = qiniu.io.put(uptoken, key, data, extra)
117119
if err is not None:
118120
error(err)
119121
return

qiniu/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# -*- coding: utf-8 -*-
21
'''
32
Qiniu Resource Storage SDK for Python
43
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -7,4 +6,5 @@
76
<https://github.com/qiniu/python-sdk/blob/develop/docs/README.md>
87
'''
98

10-
__version__ = '6.0.1'
9+
# -*- coding: utf-8 -*-
10+
__version__ = '6.1.0'

qiniu/httplib_chunk.py

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
"""
2+
Modified from standard httplib
3+
4+
1. HTTPConnection can send trunked data.
5+
2. Remove httplib's automatic Content-Length insertion when data is a file-like object.
6+
"""
7+
8+
# -*- coding: utf-8 -*-
9+
10+
import httplib
11+
from httplib import _CS_REQ_STARTED, _CS_REQ_SENT
12+
import string
13+
import os
14+
from array import array
15+
16+
class HTTPConnection(httplib.HTTPConnection):
17+
18+
def send(self, data, is_chunked=False):
19+
"""Send `data' to the server."""
20+
if self.sock is None:
21+
if self.auto_open:
22+
self.connect()
23+
else:
24+
raise NotConnected()
25+
26+
if self.debuglevel > 0:
27+
print "send:", repr(data)
28+
blocksize = 8192
29+
if hasattr(data,'read') and not isinstance(data, array):
30+
if self.debuglevel > 0: print "sendIng a read()able"
31+
datablock = data.read(blocksize)
32+
while datablock:
33+
if self.debuglevel > 0:
34+
print 'chunked:', is_chunked
35+
if is_chunked:
36+
if self.debuglevel > 0: print 'send: with trunked data'
37+
lenstr = string.upper(hex(len(datablock))[2:])
38+
self.sock.sendall('%s\r\n%s\r\n' % (lenstr, datablock))
39+
else:
40+
self.sock.sendall(datablock)
41+
datablock = data.read(blocksize)
42+
if is_chunked:
43+
self.sock.sendall('0\r\n\r\n')
44+
else:
45+
self.sock.sendall(data)
46+
47+
48+
def _set_content_length(self, body):
49+
# Set the content-length based on the body.
50+
thelen = None
51+
try:
52+
thelen = str(len(body))
53+
except (TypeError, AttributeError), te:
54+
# Don't send a length if this failed
55+
if self.debuglevel > 0: print "Cannot stat!!"
56+
57+
if thelen is not None:
58+
self.putheader('Content-Length', thelen)
59+
return True
60+
return False
61+
62+
63+
def _send_request(self, method, url, body, headers):
64+
# Honor explicitly requested Host: and Accept-Encoding: headers.
65+
header_names = dict.fromkeys([k.lower() for k in headers])
66+
skips = {}
67+
if 'host' in header_names:
68+
skips['skip_host'] = 1
69+
if 'accept-encoding' in header_names:
70+
skips['skip_accept_encoding'] = 1
71+
72+
self.putrequest(method, url, **skips)
73+
74+
is_chunked = False
75+
if body and header_names.get('Transfer-Encoding') == 'chunked':
76+
is_chunked = True
77+
elif body and ('content-length' not in header_names):
78+
is_chunked = not self._set_content_length(body)
79+
if is_chunked:
80+
self.putheader('Transfer-Encoding', 'chunked')
81+
for hdr, value in headers.iteritems():
82+
self.putheader(hdr, value)
83+
84+
self.endheaders(body, is_chunked=is_chunked)
85+
86+
87+
def endheaders(self, message_body=None, is_chunked=False):
88+
"""Indicate that the last header line has been sent to the server.
89+
90+
This method sends the request to the server. The optional
91+
message_body argument can be used to pass a message body
92+
associated with the request. The message body will be sent in
93+
the same packet as the message headers if it is string, otherwise it is
94+
sent as a separate packet.
95+
"""
96+
if self.__state == _CS_REQ_STARTED:
97+
self.__state = _CS_REQ_SENT
98+
else:
99+
raise CannotSendHeader()
100+
self._send_output(message_body, is_chunked=is_chunked)
101+
102+
103+
def _send_output(self, message_body=None, is_chunked=False):
104+
"""Send the currently buffered request and clear the buffer.
105+
106+
Appends an extra \\r\\n to the buffer.
107+
A message_body may be specified, to be appended to the request.
108+
"""
109+
self._buffer.extend(("", ""))
110+
msg = "\r\n".join(self._buffer)
111+
del self._buffer[:]
112+
# If msg and message_body are sent in a single send() call,
113+
# it will avoid performance problems caused by the interaction
114+
# between delayed ack and the Nagle algorithm.
115+
if isinstance(message_body, str):
116+
msg += message_body
117+
message_body = None
118+
self.send(msg)
119+
if message_body is not None:
120+
#message_body was not a string (i.e. it is a file) and
121+
#we must run the risk of Nagle
122+
self.send(message_body, is_chunked=is_chunked)
123+

0 commit comments

Comments
 (0)