Improvements in the WebDAV client. Transmission of binary files seems to have bugs.

This commit is contained in:
Gaetan Delannay 2010-10-15 15:14:28 +02:00
parent 990e16c6e7
commit 3cfc24fe02
2 changed files with 68 additions and 24 deletions

View file

@ -1,11 +1,13 @@
# ------------------------------------------------------------------------------
import os, re, httplib, sys
import os, re, httplib, sys, stat
from StringIO import StringIO
from mimetypes import guess_type
from base64 import encodestring
from appy.shared.utils import copyData
# ------------------------------------------------------------------------------
urlRex = re.compile(r'http://([^:/]+)(:[0-9]+)?(/.+)?', re.I)
binaryRex = re.compile(r'[\000-\006\177-\277]')
# ------------------------------------------------------------------------------
class Resource:
@ -26,6 +28,11 @@ class Resource:
self.uri = uri or '/'
else: raise 'Wrong URL: %s' % str(url)
def __repr__(self):
port = ':' + str(self.port)
if self.port == 80: port = ''
return '<Dav resource at %s%s/%s>' % (self.url, port, self.uri)
def updateHeaders(self, headers):
# Add credentials if present
if not (self.username and self.password): return
@ -39,7 +46,7 @@ class Resource:
headers['Accept'] = '*/*'
return headers
def sendRequest(self, method, uri, body=None, headers={}):
def sendRequest(self, method, uri, body=None, headers={}, bodyType=None):
'''Sends a HTTP request with p_method, for p_uri.'''
conn = httplib.HTTP()
conn.connect(self.host, self.port)
@ -48,7 +55,7 @@ class Resource:
self.updateHeaders(headers)
for n, v in headers.items(): conn.putheader(n, v)
conn.endheaders()
if body: conn.send(body)
if body: copyData(body, conn, 'send', type=bodyType)
ver, code, msg = conn.getreply()
data = conn.getfile().read()
conn.close()
@ -78,30 +85,24 @@ class Resource:
'''
if type == 'fileName':
# p_content is the name of a file on disk
f = file(content, 'rb')
body = f.read()
f.close()
fileName = os.path.basename(content)
fileType, encoding = guess_type(fileName)
size = os.stat(content)[stat.ST_SIZE]
body = file(content, 'rb')
name = os.path.basename(content)
fileType, encoding = guess_type(content)
bodyType = 'file'
elif type == 'zope':
# p_content is a "Zope" file, ie a OFS.Image.File instance
fileName = name
# p_name is given
fileType = content.content_type
encoding = None
if isinstance(content.data, basestring):
# The file content is here, in one piece
body = content.data
else:
# There are several parts to this file.
body = ''
data = content.data
while data is not None:
body += data.data
data = data.next
fileUri = self.uri + '/' + fileName
headers = {}
size = content.size
body = content
bodyType = 'zope'
fileUri = self.uri + '/' + name
headers = {'Content-Length': str(size)}
if fileType: headers['Content-Type'] = fileType
if encoding: headers['Content-Encoding'] = encoding
headers['Content-Length'] = str(len(body))
return self.sendRequest('PUT', fileUri, body, headers)
res = self.sendRequest('PUT', fileUri, body, headers, bodyType=bodyType)
# Close the file when relevant
if type =='fileName': body.close()
# ------------------------------------------------------------------------------

View file

@ -47,7 +47,7 @@ def cleanFolder(folder, exts=extsToClean, verbose=False):
if verbose: print 'Removing %s...' % fileToRemove
os.remove(fileToRemove)
# ------------------------------------------------------------------------------
def copyFolder(source, dest, cleanDest=False):
'''Copies the content of folder p_source to folder p_dest. p_dest is
created, with intermediary subfolders if required. If p_cleanDest is
@ -70,6 +70,48 @@ def copyFolder(source, dest, cleanDest=False):
# Copy a subfolder (recursively)
copyFolder(sourceName, destName)
# ------------------------------------------------------------------------------
def encodeData(data, encoding=None):
'''Applies some p_encoding to string p_data, but only if an p_encoding is
specified.'''
if not encoding: return data
return data.encode(encoding)
# ------------------------------------------------------------------------------
def copyData(data, target, targetMethod, type='string', encoding=None,
chunkSize=1024):
'''Copies p_data to a p_target, using p_targetMethod. For example, it copies
p_data which is a string containing the binary content of a file, to
p_target, which can be a HTTP connection or a file object.
p_targetMethod can be "write" (files) or "send" (HTTP connections) or ...
p_type can be "string", "file" or "zope". In the latter case it is an
instance of OFS.Image.File. If p_type is "file", one may, in p_chunkSize,
specify the amount of bytes transmitted at a time.
If an p_encoding is specified, it is applied on p_data before copying.
Note that if the p_target is a Python file, it must be opened in a way
that is compatible with the content of p_data, ie file('myFile.doc','wb')
if content is binary.'''
dump = getattr(target, targetMethod)
if type == 'string': dump(encodeData(data, encoding))
elif type == 'file':
while True:
chunk = data.read(chunkSize)
if not chunk: break
dump(encodeData(chunk, encoding))
elif type == 'zope':
# A OFS.Image.File instance can be split into several chunks
if isinstance(data.data, basestring): # One chunk
dump(encodeData(data.data, encoding))
else:
# Several chunks
data = data.data
while data is not None:
dump(encodeData(data.data, encoding))
data = data.next
# ------------------------------------------------------------------------------
class Traceback:
'''Dumps the last traceback into a string.'''
@ -229,6 +271,7 @@ class CodeAnalysis:
print '%s: %d files, %d lines (%.0f%% comments, %.0f%% blank)' % \
(self.name, self.numberOfFiles, lines, commentRate, blankRate)
# ------------------------------------------------------------------------------
class LinesCounter:
'''Counts and classifies the lines of code within a folder hierarchy.'''
def __init__(self, folderOrModule):