Eradicated Flavour and PodTemplate classes (for the latter, use Pod fields instead); Added a code analyser; Groups can now be slaves in master/slaves relationships; Refs have more params (show a confirmation popup before adding an object, add an object without creation form); Code for Refs has been refactored to comply with the new way to organize Types; Added a WebDAV client library.
This commit is contained in:
parent
9f4db88bdf
commit
990e16c6e7
47 changed files with 1006 additions and 1297 deletions
107
shared/dav.py
Normal file
107
shared/dav.py
Normal file
|
@ -0,0 +1,107 @@
|
|||
# ------------------------------------------------------------------------------
|
||||
import os, re, httplib, sys
|
||||
from StringIO import StringIO
|
||||
from mimetypes import guess_type
|
||||
from base64 import encodestring
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
urlRex = re.compile(r'http://([^:/]+)(:[0-9]+)?(/.+)?', re.I)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
class Resource:
|
||||
'''Every instance of this class represents some web resource accessible
|
||||
through WebDAV.'''
|
||||
|
||||
def __init__(self, url, username=None, password=None):
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.url = url
|
||||
|
||||
# Split the URL into its components
|
||||
res = urlRex.match(url)
|
||||
if res:
|
||||
host, port, uri = res.group(1,2,3)
|
||||
self.host = host
|
||||
self.port = port and int(port[1:]) or 80
|
||||
self.uri = uri or '/'
|
||||
else: raise 'Wrong URL: %s' % str(url)
|
||||
|
||||
def updateHeaders(self, headers):
|
||||
# Add credentials if present
|
||||
if not (self.username and self.password): return
|
||||
if headers.has_key('Authorization'): return
|
||||
credentials = '%s:%s' % (self.username,self.password)
|
||||
credentials = credentials.replace('\012','')
|
||||
headers['Authorization'] = "Basic %s" % encodestring(credentials)
|
||||
headers['User-Agent'] = 'WebDAV.client'
|
||||
headers['Host'] = self.host
|
||||
headers['Connection'] = 'close'
|
||||
headers['Accept'] = '*/*'
|
||||
return headers
|
||||
|
||||
def sendRequest(self, method, uri, body=None, headers={}):
|
||||
'''Sends a HTTP request with p_method, for p_uri.'''
|
||||
conn = httplib.HTTP()
|
||||
conn.connect(self.host, self.port)
|
||||
conn.putrequest(method, uri)
|
||||
# Add HTTP headers
|
||||
self.updateHeaders(headers)
|
||||
for n, v in headers.items(): conn.putheader(n, v)
|
||||
conn.endheaders()
|
||||
if body: conn.send(body)
|
||||
ver, code, msg = conn.getreply()
|
||||
data = conn.getfile().read()
|
||||
conn.close()
|
||||
return data
|
||||
|
||||
def mkdir(self, name):
|
||||
'''Creates a folder named p_name in this resource.'''
|
||||
folderUri = self.uri + '/' + name
|
||||
#body = '<d:propertyupdate xmlns:d="DAV:"><d:set><d:prop>' \
|
||||
# '<d:displayname>%s</d:displayname></d:prop></d:set>' \
|
||||
# '</d:propertyupdate>' % name
|
||||
return self.sendRequest('MKCOL', folderUri)
|
||||
|
||||
def delete(self, name):
|
||||
'''Deletes a file or a folder (and all contained files if any) named
|
||||
p_name within this resource.'''
|
||||
toDeleteUri = self.uri + '/' + name
|
||||
return self.sendRequest('DELETE', toDeleteUri)
|
||||
|
||||
def add(self, content, type='fileName', name=''):
|
||||
'''Adds a file in this resource. p_type can be:
|
||||
- "fileName" In this case, p_content is the path to a file on disk
|
||||
and p_name is ignored;
|
||||
- "zope" In this case, p_content is an instance of
|
||||
OFS.Image.File and the name of the file is given in
|
||||
p_name.
|
||||
'''
|
||||
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)
|
||||
elif type == 'zope':
|
||||
# p_content is a "Zope" file, ie a OFS.Image.File instance
|
||||
fileName = name
|
||||
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 = {}
|
||||
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)
|
||||
# ------------------------------------------------------------------------------
|
149
shared/utils.py
149
shared/utils.py
|
@ -125,4 +125,153 @@ def normalizeString(s, usage='fileName'):
|
|||
# ------------------------------------------------------------------------------
|
||||
typeLetters = {'b': bool, 'i': int, 'j': long, 'f':float, 's':str, 'u':unicode,
|
||||
'l': list, 'd': dict}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
class CodeAnalysis:
|
||||
'''This class holds information about some code analysis (line counts) that
|
||||
spans some folder hierarchy.'''
|
||||
def __init__(self, name):
|
||||
self.name = name # Let's give a name for the analysis
|
||||
self.numberOfFiles = 0 # The total number of analysed files
|
||||
self.emptyLines = 0 # The number of empty lines within those files
|
||||
self.commentLines = 0 # The number of comment lines
|
||||
# A code line is defined as anything that is not an empty or comment
|
||||
# line.
|
||||
self.codeLines = 0
|
||||
|
||||
def numberOfLines(self):
|
||||
'''Computes the total number of lines within analysed files.'''
|
||||
return self.emptyLines + self.commentLines + self.codeLines
|
||||
|
||||
def analyseZptFile(self, theFile):
|
||||
'''Analyses the ZPT file named p_fileName.'''
|
||||
inDoc = False
|
||||
for line in theFile:
|
||||
stripped = line.strip()
|
||||
# Manage a comment
|
||||
if not inDoc and (line.find('<tal:comment ') != -1):
|
||||
inDoc = True
|
||||
if inDoc:
|
||||
self.commentLines += 1
|
||||
if line.find('</tal:comment>') != -1:
|
||||
inDoc = False
|
||||
continue
|
||||
# Manage an empty line
|
||||
if not stripped:
|
||||
self.emptyLines += 1
|
||||
else:
|
||||
self.codeLines += 1
|
||||
|
||||
docSeps = ('"""', "'''")
|
||||
def isPythonDoc(self, line, start, isStart=False):
|
||||
'''Returns True if we find, in p_line, the start of a docstring (if
|
||||
p_start is True) or the end of a docstring (if p_start is False).
|
||||
p_isStart indicates if p_line is the start of the docstring.'''
|
||||
if start:
|
||||
res = line.startswith(self.docSeps[0]) or \
|
||||
line.startswith(self.docSeps[1])
|
||||
else:
|
||||
sepOnly = (line == self.docSeps[0]) or (line == self.docSeps[1])
|
||||
if sepOnly:
|
||||
# If the line contains the separator only, is this the start or
|
||||
# the end of the docstring?
|
||||
if isStart: res = False
|
||||
else: res = True
|
||||
else:
|
||||
res = line.endswith(self.docSeps[0]) or \
|
||||
line.endswith(self.docSeps[1])
|
||||
return res
|
||||
|
||||
def analysePythonFile(self, theFile):
|
||||
'''Analyses the Python file named p_fileName.'''
|
||||
# Are we in a docstring ?
|
||||
inDoc = False
|
||||
for line in theFile:
|
||||
stripped = line.strip()
|
||||
# Manage a line that is within a docstring
|
||||
inDocStart = False
|
||||
if not inDoc and self.isPythonDoc(stripped, start=True):
|
||||
inDoc = True
|
||||
inDocStart = True
|
||||
if inDoc:
|
||||
self.commentLines += 1
|
||||
if self.isPythonDoc(stripped, start=False, isStart=inDocStart):
|
||||
inDoc = False
|
||||
continue
|
||||
# Manage an empty line
|
||||
if not stripped:
|
||||
self.emptyLines += 1
|
||||
continue
|
||||
# Manage a comment line
|
||||
if line.startswith('#'):
|
||||
self.commentLines += 1
|
||||
continue
|
||||
# If we are here, we have a code line.
|
||||
self.codeLines += 1
|
||||
|
||||
def analyseFile(self, fileName):
|
||||
'''Analyses file named p_fileName.'''
|
||||
self.numberOfFiles += 1
|
||||
theFile = file(fileName)
|
||||
if fileName.endswith('.py'):
|
||||
self.analysePythonFile(theFile)
|
||||
elif fileName.endswith('.pt'):
|
||||
self.analyseZptFile(theFile)
|
||||
theFile.close()
|
||||
|
||||
def printReport(self):
|
||||
'''Returns the analysis report as a string, only if there is at least
|
||||
one analysed line.'''
|
||||
lines = self.numberOfLines()
|
||||
if not lines: return
|
||||
commentRate = (self.commentLines / float(lines)) * 100.0
|
||||
blankRate = (self.emptyLines / float(lines)) * 100.0
|
||||
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):
|
||||
if isinstance(folderOrModule, basestring):
|
||||
# It is the path of some folder
|
||||
self.folder = folderOrModule
|
||||
else:
|
||||
# It is a Python module
|
||||
self.folder = os.path.dirname(folderOrModule.__file__)
|
||||
# These dicts will hold information about analysed files
|
||||
self.python = {False: CodeAnalysis('Python'),
|
||||
True: CodeAnalysis('Python (test)')}
|
||||
self.zpt = {False: CodeAnalysis('ZPT'),
|
||||
True: CodeAnalysis('ZPT (test)')}
|
||||
# Are we currently analysing real or test code?
|
||||
self.inTest = False
|
||||
|
||||
def printReport(self):
|
||||
'''Displays on stdout a small analysis report about self.folder.'''
|
||||
for zone in (False, True): self.python[zone].printReport()
|
||||
for zone in (False, True): self.zpt[zone].printReport()
|
||||
|
||||
def run(self):
|
||||
'''Let's start the analysis of self.folder.'''
|
||||
# The test markers will allow us to know if we are analysing test code
|
||||
# or real code within a given part of self.folder code hierarchy.
|
||||
testMarker1 = '%stest%s' % (os.sep, os.sep)
|
||||
testMarker2 = '%stest' % os.sep
|
||||
j = os.path.join
|
||||
for root, folders, files in os.walk(self.folder):
|
||||
rootName = os.path.basename(root)
|
||||
if rootName.startswith('.') or \
|
||||
(rootName in ('tmp', 'temp')):
|
||||
continue
|
||||
# Are we in real code or in test code ?
|
||||
self.inTest = False
|
||||
if root.endswith(testMarker2) or (root.find(testMarker1) != -1):
|
||||
self.inTest = True
|
||||
# Scan the files in this folder
|
||||
for fileName in files:
|
||||
if fileName.endswith('.py'):
|
||||
self.python[self.inTest].analyseFile(j(root, fileName))
|
||||
elif fileName.endswith('.pt'):
|
||||
self.zpt[self.inTest].analyseFile(j(root, fileName))
|
||||
self.printReport()
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue