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 StringIO import StringIO
from mimetypes import guess_type from mimetypes import guess_type
from base64 import encodestring from base64 import encodestring
from appy.shared.utils import copyData
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
urlRex = re.compile(r'http://([^:/]+)(:[0-9]+)?(/.+)?', re.I) urlRex = re.compile(r'http://([^:/]+)(:[0-9]+)?(/.+)?', re.I)
binaryRex = re.compile(r'[\000-\006\177-\277]')
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
class Resource: class Resource:
@ -26,6 +28,11 @@ class Resource:
self.uri = uri or '/' self.uri = uri or '/'
else: raise 'Wrong URL: %s' % str(url) 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): def updateHeaders(self, headers):
# Add credentials if present # Add credentials if present
if not (self.username and self.password): return if not (self.username and self.password): return
@ -39,7 +46,7 @@ class Resource:
headers['Accept'] = '*/*' headers['Accept'] = '*/*'
return headers 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.''' '''Sends a HTTP request with p_method, for p_uri.'''
conn = httplib.HTTP() conn = httplib.HTTP()
conn.connect(self.host, self.port) conn.connect(self.host, self.port)
@ -48,7 +55,7 @@ class Resource:
self.updateHeaders(headers) self.updateHeaders(headers)
for n, v in headers.items(): conn.putheader(n, v) for n, v in headers.items(): conn.putheader(n, v)
conn.endheaders() conn.endheaders()
if body: conn.send(body) if body: copyData(body, conn, 'send', type=bodyType)
ver, code, msg = conn.getreply() ver, code, msg = conn.getreply()
data = conn.getfile().read() data = conn.getfile().read()
conn.close() conn.close()
@ -78,30 +85,24 @@ class Resource:
''' '''
if type == 'fileName': if type == 'fileName':
# p_content is the name of a file on disk # p_content is the name of a file on disk
f = file(content, 'rb') size = os.stat(content)[stat.ST_SIZE]
body = f.read() body = file(content, 'rb')
f.close() name = os.path.basename(content)
fileName = os.path.basename(content) fileType, encoding = guess_type(content)
fileType, encoding = guess_type(fileName) bodyType = 'file'
elif type == 'zope': elif type == 'zope':
# p_content is a "Zope" file, ie a OFS.Image.File instance # p_content is a "Zope" file, ie a OFS.Image.File instance
fileName = name # p_name is given
fileType = content.content_type fileType = content.content_type
encoding = None encoding = None
if isinstance(content.data, basestring): size = content.size
# The file content is here, in one piece body = content
body = content.data bodyType = 'zope'
else: fileUri = self.uri + '/' + name
# There are several parts to this file. headers = {'Content-Length': str(size)}
body = ''
data = content.data
while data is not None:
body += data.data
data = data.next
fileUri = self.uri + '/' + fileName
headers = {}
if fileType: headers['Content-Type'] = fileType if fileType: headers['Content-Type'] = fileType
if encoding: headers['Content-Encoding'] = encoding if encoding: headers['Content-Encoding'] = encoding
headers['Content-Length'] = str(len(body)) res = self.sendRequest('PUT', fileUri, body, headers, bodyType=bodyType)
return self.sendRequest('PUT', fileUri, body, headers) # 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 if verbose: print 'Removing %s...' % fileToRemove
os.remove(fileToRemove) os.remove(fileToRemove)
# ------------------------------------------------------------------------------
def copyFolder(source, dest, cleanDest=False): def copyFolder(source, dest, cleanDest=False):
'''Copies the content of folder p_source to folder p_dest. p_dest is '''Copies the content of folder p_source to folder p_dest. p_dest is
created, with intermediary subfolders if required. If p_cleanDest 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) # Copy a subfolder (recursively)
copyFolder(sourceName, destName) 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: class Traceback:
'''Dumps the last traceback into a string.''' '''Dumps the last traceback into a string.'''
@ -229,6 +271,7 @@ class CodeAnalysis:
print '%s: %d files, %d lines (%.0f%% comments, %.0f%% blank)' % \ print '%s: %d files, %d lines (%.0f%% comments, %.0f%% blank)' % \
(self.name, self.numberOfFiles, lines, commentRate, blankRate) (self.name, self.numberOfFiles, lines, commentRate, blankRate)
# ------------------------------------------------------------------------------
class LinesCounter: class LinesCounter:
'''Counts and classifies the lines of code within a folder hierarchy.''' '''Counts and classifies the lines of code within a folder hierarchy.'''
def __init__(self, folderOrModule): def __init__(self, folderOrModule):