Commit c1c5db60 authored by Laurent Bachelier's avatar Laurent Bachelier

PEP 8 fixes

Also add some missing __all__ and fix incorrect plugin names.
parent c3a2be89
......@@ -88,6 +88,7 @@ class ConsolePart(object):
else:
return line
class Command(ConsolePart):
DESCRIPTION = None
WORKDIR = True
......@@ -111,6 +112,7 @@ class Command(ConsolePart):
return self.cmd(args)
class CommandParent(object):
DESCRIPTION = None
......
......@@ -22,8 +22,10 @@ from urlparse import urlsplit
from urllib import quote
from paste.url import URL
__all__ = ['compact', 'quote_url', 'quote_path', 'quote_and_decode_url']
UNSAFE_CHARS = {
'?': quote('?'),
'&': quote('&'),
......@@ -36,26 +38,31 @@ UNSAFE_CHARS = {
'$': quote('$'),
}
def compact(text):
return text.replace('\n', ' ').strip()
def quote_path(path):
"""
Quote a path (see quote_url)
"""
return ''.join([UNSAFE_CHARS.get(c, c) for c in path])
def quote_url(url):
"""
Quote the path part of an URL object and return the full URL as a string.
Special characters in the URL are not considered as the query string or any other
parameters, they should be in their dedicated variables of the URL class.
Special characters in the URL are not considered as the query string or
any other parameters, they should be in their dedicated variables
of the URL class.
"""
purl = urlsplit(url.url)
# we do not support escaping an URL with a scheme and netloc yet
assert not purl.scheme and not purl.netloc
return URL(quote_path(url.url), vars=url.vars).href
def quote_and_decode_url(url):
"""
Like quote_url but for usage in Mako templates
......
......@@ -21,6 +21,7 @@
from collections import defaultdict
from copy import deepcopy
class IObject(object):
def __init__(self, storage):
self.storage = storage
......@@ -69,8 +70,9 @@ class IObject(object):
def is_modified(self):
return self._old_data != self.data
class ConfigDict(defaultdict):
def __init__(self, _ = None):
def __init__(self, _=None):
defaultdict.__init__(self, dict)
def cleanup(self):
......
......@@ -83,4 +83,3 @@ class Plugin(object):
return
router.register_view(*args, **kwargs)
......@@ -64,7 +64,7 @@ class TextListAction(ViewAction):
filenames = []
for f in self.ctx.iter_files():
if f.isdir():
filenames.append(f.get_name()+'/')
filenames.append(f.get_name() + '/')
else:
filenames.append(f.get_name())
......
......@@ -73,13 +73,13 @@ class AssetAction(Action):
# first look if there is a (read-only) gzipped version of the asset
# these files may be created by installers and are supposed to be
# always up to date
realpath_gz = realpath+'.gz'
realpath_gz = realpath + '.gz'
if os.path.exists(realpath_gz):
return realpath_gz
# then look for a local cache of that asset, if not present, create it
cachedir = os.path.join(self.ctx.storage.path, 'assets_cache')
dest = os.path.join(cachedir, filename+'.gz')
dest = os.path.join(cachedir, filename + '.gz')
if not os.path.exists(dest) or mtime != int(os.path.getmtime(dest)):
if not os.path.isdir(cachedir):
os.makedirs(cachedir)
......
......@@ -79,7 +79,7 @@ class GetConfigCmd(Command):
return 1
section, key = args.section_key.split('.')
if not config.data[section].has_key(key):
if not key in config.data[section]:
print >>sys.stderr, 'Error: %s is undefined.' % args.section_key
return 2
......@@ -157,6 +157,7 @@ class ConfigCmdParent(CommandParent):
raise GetConfigError('User "%s" does not exist.' % config_name)
return u
class ConfigPlugin(Plugin):
def init(self):
self.register_cli_command('config', ConfigCmdParent)
......
......@@ -132,7 +132,7 @@ class ContactsManagement(ConsolePart):
print '%s q)%s --stop--' % (self.BOLD, self.NC)
print ''
r = self.print_menu()
except (KeyboardInterrupt,EOFError):
except (KeyboardInterrupt, EOFError):
break
if r.isdigit():
......@@ -192,6 +192,7 @@ class ContactsManagement(ConsolePart):
self.BOLD, user.name, self.NC,
user.realname, user.email)
class ContactsSelection(ContactsManagement):
def __init__(self, storage, sel_users=[], sel_groups=None):
ContactsManagement.__init__(self, storage)
......@@ -255,6 +256,7 @@ class ContactsSelection(ContactsManagement):
else:
self.sel_groups.add(group.name)
class ContactsAddCmd(Command):
DESCRIPTION = 'Add a contact'
......@@ -267,6 +269,7 @@ class ContactsAddCmd(Command):
if not cm.add_contact(args.username):
return 1
class ContactsPasswordCmd(Command):
DESCRIPTION = 'Change the password of a contact'
......@@ -279,6 +282,7 @@ class ContactsPasswordCmd(Command):
if not cm.edit_contact_password(args.username):
return 1
class ContactsGenKeyCmd(Command):
DESCRIPTION = 'Create or reset the key on a contact'
......@@ -291,6 +295,7 @@ class ContactsGenKeyCmd(Command):
if not cm.generate_contact_key(args.username):
return 1
class ContactsMenuCmd(Command):
DESCRIPTION = 'Display the contacts menu'
......@@ -298,6 +303,7 @@ class ContactsMenuCmd(Command):
cm = ContactsManagement(self.storage)
cm.main()
class ContactsMergeCmd(Command):
DESCRIPTION = 'Merge contacts from another working directory'
......@@ -317,6 +323,7 @@ class ContactsMergeCmd(Command):
user.save()
print 'Imported %s (%s <%s>)' % (user.name, user.realname, user.email)
class ContactsListCmd(Command):
DESCRIPTION = 'List contacts'
WORKDIR = True
......@@ -325,6 +332,7 @@ class ContactsListCmd(Command):
cm = ContactsManagement(self.storage)
cm.print_users()
class ContactsRemoveCmd(Command):
DESCRIPTION = 'Remove a contact'
WORKDIR = True
......@@ -354,7 +362,7 @@ class LoginAction(Action):
or quote_url(self.ctx.root_url)
return form
def get(self, form = None):
def get(self, form=None):
form = form or self._get_form()
self.ctx.template_vars['form'] = form
self.ctx.res.body = self.ctx.render('login.html')
......
......@@ -45,6 +45,7 @@ class InitCmd(Command):
self.storage = Storage.create(self.working_dir)
print 'Ass2m working directory created.'
class TreeCmd(Command):
DESCRIPTION = 'Display the working tree'
WORKDIR = True
......@@ -70,7 +71,8 @@ class TreeCmd(Command):
self.print_perms(path + '/', depth)
for filename in files:
self.print_perms(os.path.join(path, filename), depth+1)
self.print_perms(os.path.join(path, filename), depth + 1)
class ChViewCmd(Command):
DESCRIPTION = 'Change default view of a path'
......@@ -94,6 +96,7 @@ class ChViewCmd(Command):
f.view = args.view
f.save()
class ChModCmd(Command):
DESCRIPTION = 'Change permissions of a path'
......
......@@ -59,8 +59,8 @@ class Event(object):
self.f.clear_user_perms()
for username in self.users.iterkeys():
self.f.set_user_perms(username, self.f.PERM_WRITE|
self.f.PERM_READ|
self.f.set_user_perms(username, self.f.PERM_WRITE |
self.f.PERM_READ |
self.f.PERM_LIST)
self.f.view = 'event'
self.f.save()
......@@ -83,7 +83,7 @@ class Event(object):
fp.write('%s\n\n' % binary(self.place))
fp.write('Attendees:\n')
stats = [0, 0, 0]
for username, state in sorted(self.users.items(), key=lambda (k,v): (v,k)):
for username, state in sorted(self.users.items(), key=lambda (k, v): (v, k)):
realname = self.f.storage.get_user(username).realname
checked = self.get_sign(state)
stats[state] += 1
......@@ -151,6 +151,7 @@ class Event(object):
def add_user(self, username):
self.users[username] = self.USER_WAITING
class EventCmd(Command):
DESCRIPTION = 'Create or edit an event'
WORKDIR = True
......@@ -200,7 +201,7 @@ class EventCmd(Command):
continue
if r == 'q':
continue
except (KeyboardInterrupt,EOFError):
except (KeyboardInterrupt, EOFError):
print '\nAborted.'
else:
event.save()
......@@ -245,6 +246,7 @@ class EventCmd(Command):
for added in cs.sel_users - set(event.users.keys()):
event.add_user(added)
class EventAction(ViewAction):
def get(self, state=None):
user_state = None
......@@ -285,6 +287,7 @@ class EventAction(ViewAction):
def put(self):
self.get(Event.USER_CONFIRMED)
class EventsPlugin(Plugin):
def init(self):
self.register_cli_command('event', EventCmd)
......
......@@ -27,6 +27,10 @@ from ass2m.plugin import Plugin
from ass2m.routes import View
from ass2m.server import ViewAction, FileApp
__all__ = ['GalleryPlugin']
class ListGalleryAction(ViewAction):
def get(self):
dirs = []
......@@ -51,13 +55,14 @@ class ListGalleryAction(ViewAction):
self.ctx.template_vars['description'] = description
self.ctx.res.body = self.ctx.render('list-gallery.html')
class DownloadThumbnailAction(ViewAction):
DEFAULT_SIZE = 300
def _get_size(self):
try:
size = int(self.ctx.req.str_GET['thumb_size'])
except (KeyError,ValueError):
except (KeyError, ValueError):
size = self.DEFAULT_SIZE
else:
if size < 1 or size > 1000:
......
......@@ -24,6 +24,10 @@ from ass2m.plugin import Plugin
from ass2m.routes import View
from ass2m.server import ViewAction, FileApp
__all__ = ['WebsitePlugin']
class WebsiteAction(ViewAction):
PAGENAME = 'index.html'
......@@ -36,7 +40,8 @@ class WebsiteAction(ViewAction):
else:
self.ctx.res = FileApp(path)
class CorePlugin(Plugin):
class WebsitePlugin(Plugin):
def init(self):
self.register_web_view(
View(object_type='directory', name='website'),
......
......@@ -22,8 +22,8 @@ __all__ = ['View', 'Router']
class View(object):
def __init__(self, name, object_type = None, mimetype = None,
verbose_name = None, public = True):
def __init__(self, name, object_type=None, mimetype=None,
verbose_name=None, public=True):
"""
name: Arbitrary name.
object_type: "file" or "directory", or None for both.
......@@ -73,7 +73,6 @@ class View(object):
priority += 1
return priority
def __str__(self):
return self.name
......@@ -91,7 +90,7 @@ class Router(object):
"""
self.actions[name] = action
def register_view(self, view, action, priority = None):
def register_view(self, view, action, priority=None):
"""
Register a view. A view is related to a file.
See the View class for more details.
......@@ -110,7 +109,7 @@ class Router(object):
"""
return self.actions.get(name)
def find_view(self, f, name = None):
def find_view(self, f, name=None):
"""
Find a view suitable for a file
f: File
......
......@@ -38,8 +38,10 @@ from .users import Anonymous
from .routes import Router
from .filters import quote_url, quote_path
__all__ = ['ViewAction', 'Action', 'Server', 'FileApp']
class Context(object):
SANITIZE_REGEXP = re.compile(r'/[%s+r]+/|\\+|/+' % re.escape(r'/.'))
......@@ -54,7 +56,7 @@ class Context(object):
script_path = quote_path(environ['SCRIPT_URL'])
if self.req.path_info:
level = len(self.req.path_info.split('/')) - 1
environ['SCRIPT_NAME'] = '/'.join(script_path.split('/')[:-level])+'/'
environ['SCRIPT_NAME'] = '/'.join(script_path.split('/')[:-level]) + '/'
else:
environ['SCRIPT_NAME'] = script_path
self.res = Response()
......@@ -105,7 +107,7 @@ class Context(object):
config.save()
# store the absolute root url (useful when in CLI)
if not config.data["web"].get("root_url"):
config.data["web"]["root_url"] = self.req.host_url+self.root_url.href
config.data["web"]["root_url"] = self.req.host_url + self.root_url.href
config.save()
def _init_default_response(self):
......@@ -137,7 +139,7 @@ class Context(object):
if self.user.has_perms(f, f.PERM_LIST):
yield f
def login(self, user, set_cookie = True):
def login(self, user, set_cookie=True):
"""
Log in an user.
user: User
......@@ -156,7 +158,7 @@ class Context(object):
self.update_session()
self.user = user
def logout(self, delete_cookie = True):
def logout(self, delete_cookie=True):
"""
Log out the current user.
delete_cookie: bool, to chose if we remove the cookie to forget the user.
......
......@@ -37,6 +37,7 @@ class GlobalConfig(IObject):
def _get_confname(self):
return 'config'
class GroupsConfig(IObject, dict):
def __init__(self, storage):
IObject.__init__(self, storage)
......@@ -60,6 +61,7 @@ class GroupsConfig(IObject, dict):
self.data[name]['users'] = ' '.join([uname for uname in group.users])
self.data[name]['description'] = group.description
class Storage(object):
DIRNAME = '.ass2m'
DATA_PATHS = [os.path.realpath(os.path.join(os.path.dirname(__file__), os.path.pardir, 'data')),
......@@ -101,7 +103,7 @@ class Storage(object):
# Default perms on /
f = File(storage, '')
f.perms = {'all': File.PERM_READ|File.PERM_LIST}
f.perms = {'all': File.PERM_READ | File.PERM_LIST}
f.save()
return storage
......@@ -165,7 +167,7 @@ class Storage(object):
data = ConfigDict()
for sec in config.sections():
data[sec] = dict([(k,v.decode('utf-8')) for k,v in config.items(sec)])
data[sec] = dict([(k, v.decode('utf-8')) for k, v in config.items(sec)])
return data
......
......@@ -24,6 +24,7 @@ from mako.lookup import TemplateLookup
from .storage import Storage
from .version import VERSION
def build_lookup(storage):
"""
Get the Mako TemplateLookup.
......@@ -39,6 +40,7 @@ def build_lookup(storage):
default_filters=['decode.utf8'],
imports=imports)
def build_vars(storage):
"""
Get useful default variables to use with Mako templates.
......
......@@ -22,16 +22,19 @@ __all__ = ['Group', 'IUser', 'User', 'Anonymous']
from .obj import IObject
import os
import hashlib
from binascii import hexlify
class Group(object):
def __init__(self, name):
self.name = name
self.description = u''
self.users = []
class IUser(object):
def has_perms(self, f, perm):
raise NotImplementedError()
......@@ -39,6 +42,7 @@ class IUser(object):
def __str__(self):
return self.name
class User(IUser, IObject):
def __init__(self, storage, name):
self.name = name
......@@ -102,7 +106,7 @@ class User(IUser, IObject):
@staticmethod
def hash_password(password, salt, version):
assert version == 1 # the only one supported for now
assert version == 1 # the only one supported for now
return hashlib.sha512(password + salt).hexdigest()
def is_valid_password(self, password):
......
......@@ -29,7 +29,7 @@ setup(name="ass2m",
zip_safe=False,
packages=find_packages(),
scripts=['bin/ass2m', 'bin/ass2m-serve'],
data_files = [
data_files=[
('%s/assets' % DATA_DIR, glob('data/assets/*')),
('%s/templates' % DATA_DIR, glob('data/templates/*')),
('%s/scripts' % DATA_DIR, glob('data/scripts/*')),
......
......@@ -10,6 +10,7 @@ import shutil
import json
from datetime import datetime
class ApiTest(TestCase):
def setUp(self):
self.root = mkdtemp(prefix='ass2m_test_root')
......
......@@ -8,6 +8,7 @@ from tempfile import mkdtemp
import os
import shutil
class AssetsTest(TestCase):
def setUp(self):
self.root = mkdtemp(prefix='ass2m_test_root')
......
......@@ -9,6 +9,7 @@ import shutil
import sys
from StringIO import StringIO
class BaseCLITest(TestCase):
def setUp(self):
self.root = mkdtemp(prefix='ass2m_test_root')
......@@ -18,7 +19,7 @@ class BaseCLITest(TestCase):
if self.root:
shutil.rmtree(self.root)
def beginCapture(self, with_stderr = False):
def beginCapture(self, with_stderr=False):
self.stdout = sys.stdout
# begin capture
sys.stdout = StringIO()
......@@ -48,8 +49,8 @@ class BaseCLITest(TestCase):
self.beginCapture()
assert self.app.main(['ass2m_test', 'tree']) in (0, None)
output = self.endCapture()
assert re.match(re.escape(r'/')+r'\s+'+re.escape(r'all(rl-)'), output, re.S)
assert re.match(".+"+re.escape(r'/.ass2m/')+r'\s+'+re.escape(r'all(---)'), output, re.S)
assert re.match(re.escape(r'/') + r'\s+' + re.escape(r'all(rl-)'), output, re.S)
assert re.match(".+" + re.escape(r'/.ass2m/') + r'\s+' + re.escape(r'all(---)'), output, re.S)
def test_findRoot(self):
self.beginCapture()
......
......@@ -8,6 +8,7 @@ from tempfile import mkdtemp
import os
import shutil
class BaseWebTest(TestCase):
def setUp(self):
self.root = mkdtemp(prefix='ass2m_test_root')
......
......@@ -8,6 +8,7 @@ import shutil
import sys
from StringIO import StringIO
class ConfigCLITest(TestCase):
def setUp(self):
self.root = mkdtemp(prefix='ass2m_test_root')
......@@ -28,7 +29,7 @@ class ConfigCLITest(TestCase):
if self.root:
shutil.rmtree(self.root)
def beginCapture(self, with_stderr = False):
def beginCapture(self, with_stderr=False):
self.stdout = sys.stdout
# begin capture
sys.stdout = StringIO()
......@@ -50,7 +51,7 @@ class ConfigCLITest(TestCase):
return captured
def test_resolve(self):
for p in (os.path.join(self.root, 'penguins'), os.path.join(self.root, 'penguins'+os.path.sep)):
for p in (os.path.join(self.root, 'penguins'), os.path.join(self.root, 'penguins' + os.path.sep)):
self.beginCapture()
assert self.app.main(['ass2m_test', 'config', '-f', p, 'resolve']) in (0, None)
output = self.endCapture()
......@@ -90,11 +91,11 @@ class ConfigCLITest(TestCase):
self.beginCapture()
assert self.app.main(['ass2m_test', 'config', '-f', self.root, 'list']) in (0, None)
output = self.endCapture()
assert len(output) # it has the default perms
assert len(output) # it has the default perms
self.beginCapture()
assert self.app.main(['ass2m_test', 'config', '-u', 'penguin', 'list']) in (0, None)
output = self.endCapture() # no ValueError
output = self.endCapture() # no ValueError
assert len(output)
def test_filterList(self):
......@@ -102,7 +103,7 @@ class ConfigCLITest(TestCase):
assert self.app.main(['ass2m_test', 'config', '-u', 'penguin', 'list']) in (0, None)
output = self.endCapture()
assert len(output)
output.split('\n').index('info.realname=Penguin') # no ValueError
output.split('\n').index('info.realname=Penguin') # no ValueError
self.beginCapture()
assert self.app.main(['ass2m_test', 'config', '-u', 'penguin', 'list', 'auth']) in (0, None)
......
......@@ -9,6 +9,7 @@ from tempfile import mkdtemp
import os
import shutil
class EventTest(TestCase):
def setUp(self):
self.root = mkdtemp(prefix='ass2m_test_root')
......@@ -47,7 +48,7 @@ Attendees:
f = storage.get_file('/event1.txt')
f.view = 'event'
f.perms['u.penguin'] = f.PERM_READ|f.PERM_WRITE
f.perms['u.penguin'] = f.PERM_READ | f.PERM_WRITE
f.save()
def tearDown(self):
......
......@@ -10,6 +10,7 @@ from StringIO import StringIO
import os
import shutil
class GalleryTest(TestCase):
def setUp(self):
self.root = mkdtemp(prefix='ass2m_test_root')
......
......@@ -8,6 +8,7 @@ from webtest import TestApp
from tempfile import mkdtemp
import shutil
class LoginTest(TestCase):
def setUp(self):
self.root = mkdtemp(prefix='ass2m_test_root')
......
......@@ -8,6 +8,7 @@ from tempfile import mkdtemp
import os
import shutil
class RootDirTest(TestCase):
def setUp(self):
self.root = mkdtemp(prefix='ass2m_test_root')
......
from ass2m.routes import View, Router
from unittest import TestCase
def fn1():
pass
def fn2():
pass
class FakeFile(object):
def __init__(self, name, object_type, view = None, mimetype = None):
def __init__(self, name, object_type, view=None, mimetype=None):
self.name = name
self.object_type = object_type
self.view = view
......@@ -20,6 +23,7 @@ class FakeFile(object):
def get_mimetype(self):
return self.mimetype
class RoutesTest(TestCase):
def test_actions(self):
router = Router()
......
......@@ -7,6 +7,7 @@ from tempfile import mkdtemp
import shutil
import os
class StorageTest(TestCase):
def setUp(self):
self.root = mkdtemp(prefix='ass2m_test_root')
......@@ -24,7 +25,7 @@ class StorageTest(TestCase):
def test_configDict(self):
d = ConfigDict()
d['a']
d['b']['a']=1
d['b']['a'] = 1
assert len(d) == 2
d.cleanup()
assert len(d) == 1
......@@ -137,13 +138,13 @@ class StorageTest(TestCase):
def test_filePreAndPost(self):
f = File(self.storage, '/penguin')
f.perms['all'] = File.PERM_READ|File.PERM_LIST
f.perms['all'] = File.PERM_READ | File.PERM_LIST
f.view = 'html'
f.save()
f = File(self.storage, '/penguin')
f.read()
assert f.perms['all'] == File.PERM_READ|File.PERM_LIST
assert f.perms['all'] == File.PERM_READ | File.PERM_LIST
assert f.view == 'html'
f.remove()
assert len(f.perms) == 0
......@@ -282,11 +283,12 @@ class StorageTest(TestCase):
class Chafouin(File):
def __init__(self, fakesize):
self.fakesize = fakesize
def get_size(self):
return self.fakesize
assert Chafouin(1).get_human_size() == '1.0 B'
assert Chafouin(1024).get_human_size() == '1.0 KiB'
assert Chafouin(4*1024+128).get_human_size() == '4.1 KiB'
assert Chafouin(4*1024*1024).get_human_size() == '4.0 MiB'
assert Chafouin(42*1024*1024*1024).get_human_size() == '42.0 GiB'
assert Chafouin(4 * 1024 + 128).get_human_size() == '4.1 KiB'
assert Chafouin(4 * 1024 * 1024).get_human_size() == '4.0 MiB'
assert Chafouin(42 * 1024 * 1024 * 1024).get_human_size() == '42.0 GiB'