Permalink
Browse files

Blank lines should not contain any whitespace

Pinax has decided for open source projects to conform to the blank
lines should not contain whitespace style guideline. We want to do less
style guide enforcing with our contributors and make it easier for them
to contribute.

This does not mean we won't enforce this style guideline. Consistency is
still king and mixing whitespace lines with no whitespace lines is
discouraged.
  • Loading branch information...
1 parent fbf609e commit b2bdc5aee39bef59811dbc47bbfc41e359e320ec @paltman paltman committed Aug 28, 2013
View
3 .pylintrc
@@ -4,6 +4,7 @@ load-plugins=pinax.checkers.style
[MESSAGES CONTROL]
# Pointless whinging
# C0111 = Missing docstring
+# C1001 = Old style classes
# R0904 = Too Many Public Methods
# I0011 = Locally Disabling
# W0232 = Class has no __init__ method
@@ -13,7 +14,7 @@ load-plugins=pinax.checkers.style
# W0511 = Turn off warnings for TODO comments
# R0913 = Too many arguments
-disable=C0111,R0904,I0011,W0232,R0903,R0922,R0201,W0511,R0913
+disable=C0111,R0904,I0011,W0232,R0903,R0922,R0201,W0511,R0913,C9901,C1001
[REPORTS]
include-ids=yes
View
3 .travis.yml
@@ -8,9 +8,10 @@ install:
- pip install -q Django==$DJANGO --use-mirrors
- pip install -q django-nose --use-mirrors
- pip install -q pylint --use-mirrors
+ - pip install -q pep8 --use-mirrors
- pip install -q pinax.checkers --use-mirrors
- pip install -e . --use-mirrors
before_script:
- - "pylint --rcfile=.pylintrc notification"
+ - lint.sh
script:
- python runtests.py
View
15 CONTRIBUTING.md
@@ -71,7 +71,6 @@ Django's coding style:
* Use double quotes not single quotes. Single quotes are allowed in cases
where a double quote is needed in the string. This makes the code read
cleaner in those cases.
-* Blank lines are indented to the appropriate level for the block they are in.
* Docstrings always use three double quotes on a line of their own, so, for
example, a single line docstring should take up three lines not one.
* Imports are grouped specifically and ordered alphabetically. This is shown
@@ -107,27 +106,27 @@ Here is an example of these rules applied:
"""
A model for storing a task.
"""
-
+
creator = models.ForeignKey(User)
created = models.DateTimeField(default=timezone.now)
modified = models.DateTimeField(default=timezone.now)
-
+
objects = models.Manager()
-
+
class Meta:
verbose_name = _("task")
verbose_name_plural = _("tasks")
-
+
def __unicode__(self):
return self.summary
-
+
def save(self, **kwargs):
self.modified = datetime.now()
super(Task, self).save(**kwargs)
-
+
def get_absolute_url(self):
return reverse("task_detail", kwargs={"task_id": self.pk})
-
+
# custom methods
View
1 lint.sh
@@ -0,0 +1 @@
+pylint --rcfile=.pylintrc notification -r y && pep8 --ignore=E501 notification
View
8 notification/backends/base.py
@@ -13,21 +13,21 @@ def __init__(self, medium_id, spam_sensitivity=None):
self.medium_id = medium_id
if spam_sensitivity is not None:
self.spam_sensitivity = spam_sensitivity
-
+
def can_send(self, user, notice_type):
"""
Determines whether this backend is allowed to send a notification to
the given user and notice_type.
"""
from notification.models import NoticeSetting
return NoticeSetting.for_user(user, notice_type, self.medium_id).send
-
+
def deliver(self, recipient, sender, notice_type, extra_context):
"""
Deliver a notification to the given recipient.
"""
raise NotImplementedError()
-
+
def get_formatted_messages(self, formats, label, context):
"""
Returns a dictionary with the format identifier as the key. The values are
@@ -42,7 +42,7 @@ def get_formatted_messages(self, formats, label, context):
"notification/%s/%s" % (label, fmt),
"notification/%s" % fmt), context_instance=context)
return format_templates
-
+
def default_context(self):
default_http_protocol = getattr(settings, "DEFAULT_HTTP_PROTOCOL", "http")
current_site = Site.objects.get_current()
View
14 notification/backends/email.py
@@ -8,35 +8,35 @@
class EmailBackend(backends.BaseBackend):
spam_sensitivity = 2
-
+
def can_send(self, user, notice_type):
can_send = super(EmailBackend, self).can_send(user, notice_type)
if can_send and user.email:
return True
return False
-
+
def deliver(self, recipient, sender, notice_type, extra_context):
# TODO: require this to be passed in extra_context
-
+
context = self.default_context()
context.update({
"recipient": recipient,
"sender": sender,
"notice": ugettext(notice_type.display),
})
context.update(extra_context)
-
+
messages = self.get_formatted_messages((
"short.txt",
"full.txt"
), notice_type.label, context)
-
+
subject = "".join(render_to_string("notification/email_subject.txt", {
"message": messages["short.txt"],
}, context).splitlines())
-
+
body = render_to_string("notification/email_body.txt", {
"message": messages["full.txt"],
}, context)
-
+
send_mail(subject, body, settings.DEFAULT_FROM_EMAIL, [recipient.email])
View
8 notification/engine.py
@@ -25,7 +25,7 @@ def send_all(*args):
lock = FileLock(args[0])
else:
lock = FileLock("send_notices")
-
+
logging.debug("acquiring lock...")
try:
lock.acquire(LOCK_WAIT_TIMEOUT)
@@ -36,10 +36,10 @@ def send_all(*args):
logging.debug("waiting for the lock timed out. quitting.")
return
logging.debug("acquired.")
-
+
batches, sent, sent_actual = 0, 0, 0
start_time = time.time()
-
+
try:
# nesting the try statement to be Python 2.4
try:
@@ -86,7 +86,7 @@ def send_all(*args):
logging.debug("releasing lock...")
lock.release()
logging.debug("released.")
-
+
logging.info("")
logging.info("{} batches, {} sent".format(batches, sent,))
logging.info("done in {:.2f} seconds".format(time.time() - start_time))
View
86 notification/lockfile.py
@@ -182,56 +182,56 @@ def __init__(self, path, threaded=True):
"%s.%s%s" % (self.hostname,
tname,
self.pid))
-
+
def acquire(self, timeout=None):
"""
Acquire the lock.
-
+
* If timeout is omitted (or None), wait forever trying to lock the
file.
-
+
* If timeout > 0, try to acquire the lock for that many seconds. If
the lock period expires and the file is still locked, raise
LockTimeout.
-
+
* If timeout <= 0, raise AlreadyLocked immediately if the file is
already locked.
"""
raise NotImplementedError("implement in subclass")
-
+
def release(self):
"""
Release the lock.
If the file is not locked, raise NotLocked.
"""
raise NotImplementedError("implement in subclass")
-
+
def is_locked(self):
"""
Tell whether or not the file is locked.
"""
raise NotImplementedError("implement in subclass")
-
+
def i_am_locking(self):
"""
Return True if this object is locking the file.
"""
raise NotImplementedError("implement in subclass")
-
+
def break_lock(self):
"""
Remove a lock. Useful if a locking thread failed to unlock.
"""
raise NotImplementedError("implement in subclass")
-
+
def __enter__(self):
"""
Context manager support.
"""
self.acquire()
return self
-
+
def __exit__(self, *_exc):
"""
Context manager support.
@@ -241,17 +241,17 @@ def __exit__(self, *_exc):
class LinkFileLock(LockBase):
"""Lock access to a file using atomic property of link(2)."""
-
+
def acquire(self, timeout=None):
try:
open(self.unique_name, "wb").close()
except IOError:
raise LockFailed("failed to create %s" % self.unique_name)
-
+
end_time = time.time()
if timeout is not None and timeout > 0:
end_time += timeout
-
+
while True:
# Try and create a hard link to it.
try:
@@ -271,27 +271,27 @@ def acquire(self, timeout=None):
raise LockTimeout
else:
raise AlreadyLocked
- time.sleep(timeout is not None and timeout/10 or 0.1)
+ time.sleep(timeout is not None and (timeout / 10) or 0.1)
else:
# Link creation succeeded. We"re good to go.
return
-
+
def release(self):
if not self.is_locked():
raise NotLocked
elif not os.path.exists(self.unique_name):
raise NotMyLock
os.unlink(self.unique_name)
os.unlink(self.lock_file)
-
+
def is_locked(self):
return os.path.exists(self.lock_file)
-
+
def i_am_locking(self):
return (self.is_locked() and
os.path.exists(self.unique_name) and
os.stat(self.unique_name).st_nlink == 2)
-
+
def break_lock(self):
if os.path.exists(self.lock_file):
os.unlink(self.lock_file)
@@ -315,17 +315,17 @@ def __init__(self, path, threaded=True):
self.lock_file,
"{}.{}{}".format(self.hostname, tname, self.pid)
)
-
+
def acquire(self, timeout=None):
end_time = time.time()
if timeout is not None and timeout > 0:
end_time += timeout
-
+
if timeout is None:
wait = 0.1
else:
wait = max(0, timeout / 10)
-
+
while True:
try:
os.mkdir(self.lock_file)
@@ -349,22 +349,22 @@ def acquire(self, timeout=None):
else:
open(self.unique_name, "wb").close()
return
-
+
def release(self):
if not self.is_locked():
raise NotLocked
elif not os.path.exists(self.unique_name):
raise NotMyLock
os.unlink(self.unique_name)
os.rmdir(self.lock_file)
-
+
def is_locked(self):
return os.path.exists(self.lock_file)
-
+
def i_am_locking(self):
return (self.is_locked() and
os.path.exists(self.unique_name))
-
+
def break_lock(self):
if os.path.exists(self.lock_file):
for name in os.listdir(self.lock_file):
@@ -374,21 +374,21 @@ def break_lock(self):
class SQLiteFileLock(LockBase):
"""Demonstration of using same SQL-based locking."""
-
+
import tempfile
_fd, testdb = tempfile.mkstemp()
os.close(_fd)
os.unlink(testdb)
del _fd, tempfile
-
+
def __init__(self, path, threaded=True):
LockBase.__init__(self, path, threaded)
self.lock_file = unicode(self.lock_file)
self.unique_name = unicode(self.unique_name)
-
+
import sqlite3
self.connection = sqlite3.connect(SQLiteFileLock.testdb)
-
+
cursor = self.connection.cursor()
try:
cursor.execute("create table locks"
@@ -402,7 +402,7 @@ def __init__(self, path, threaded=True):
self.connection.commit()
import atexit
atexit.register(os.unlink, SQLiteFileLock.testdb)
-
+
def create_lock(self, cursor):
# Not locked. Try to lock it.
cursor.execute("insert into locks"
@@ -411,7 +411,7 @@ def create_lock(self, cursor):
" (?, ?)",
(self.lock_file, self.unique_name))
self.connection.commit()
-
+
# Check to see if we are the only lock holder.
cursor.execute("select * from locks where unique_name = ?",
(self.unique_name,))
@@ -423,7 +423,7 @@ def create_lock(self, cursor):
self.connection.commit()
return False
return True
-
+
def i_am_the_only_lock(self, cursor):
# Check to see if we are the only lock holder.
cursor.execute("select * from locks where unique_name = ?",
@@ -432,29 +432,29 @@ def i_am_the_only_lock(self, cursor):
if len(rows) == 1:
# We"re the locker, so go home.
return
-
+
def acquire(self, timeout=None):
end_time = time.time()
if timeout is not None and timeout > 0:
end_time += timeout
-
+
if timeout is None:
wait = 0.1
elif timeout <= 0:
wait = 0
else:
wait = timeout / 10
-
+
cursor = self.connection.cursor()
-
+
while True:
if self.is_locked():
if self.i_am_the_only_lock(cursor):
return
else:
if self.create_lock(cursor):
return
-
+
# Maybe we should wait a bit longer.
if timeout is not None and time.time() > end_time:
if timeout > 0:
@@ -463,10 +463,10 @@ def acquire(self, timeout=None):
else:
# Someone else has the lock and we are impatient..
raise AlreadyLocked
-
+
# Well, okay. We"ll give it a bit longer.
time.sleep(wait)
-
+
def release(self):
if not self.is_locked():
raise NotLocked
@@ -477,30 +477,30 @@ def release(self):
" where unique_name = ?",
(self.unique_name,))
self.connection.commit()
-
+
def _who_is_locking(self):
cursor = self.connection.cursor()
cursor.execute("select unique_name from locks"
" where lock_file = ?",
(self.lock_file,))
return cursor.fetchone()[0]
-
+
def is_locked(self):
cursor = self.connection.cursor()
cursor.execute("select * from locks"
" where lock_file = ?",
(self.lock_file,))
rows = cursor.fetchall()
return not not rows
-
+
def i_am_locking(self):
cursor = self.connection.cursor()
cursor.execute("select * from locks"
" where lock_file = ?"
" and unique_name = ?",
(self.lock_file, self.unique_name))
return not not cursor.fetchall()
-
+
def break_lock(self):
cursor = self.connection.cursor()
cursor.execute("delete from locks"
View
2 notification/management/commands/emit_notices.py
@@ -7,7 +7,7 @@
class Command(BaseCommand):
help = "Emit queued notices."
-
+
def handle(self, *args, **options):
logging.basicConfig(level=logging.DEBUG, format="%(message)s")
logging.info("-" * 72)
View
34 notification/models.py
@@ -29,26 +29,26 @@ def create_notice_type(label, display, description, **kwargs):
class NoticeType(models.Model):
-
+
label = models.CharField(_("label"), max_length=40)
display = models.CharField(_("display"), max_length=50)
description = models.CharField(_("description"), max_length=100)
-
+
# by default only on for media with sensitivity less than or equal to this number
default = models.IntegerField(_("default"))
-
+
def __unicode__(self):
return self.label
-
+
class Meta:
verbose_name = _("notice type")
verbose_name_plural = _("notice types")
-
+
@classmethod
def create(cls, label, display, description, default=2, verbosity=1):
"""
Creates a new NoticeType.
-
+
This is intended to be used by other apps as a post_syncdb manangement step.
"""
try:
@@ -78,17 +78,17 @@ class NoticeSetting(models.Model):
Indicates, for a given user, whether to send notifications
of a given type to a given medium.
"""
-
+
user = models.ForeignKey(User, verbose_name=_("user"))
notice_type = models.ForeignKey(NoticeType, verbose_name=_("notice type"))
medium = models.CharField(_("medium"), max_length=1, choices=NOTICE_MEDIA)
send = models.BooleanField(_("send"))
-
+
class Meta:
verbose_name = _("notice setting")
verbose_name_plural = _("notice settings")
unique_together = ("user", "notice_type", "medium")
-
+
@classmethod
def for_user(cls, user, notice_type, medium):
try:
@@ -130,9 +130,9 @@ def get_notification_language(user):
def send_now(users, label, extra_context=None, sender=None):
"""
Creates a new notice.
-
+
This is intended to be how other apps create new notices.
-
+
notification.send(user, "friends_invite_sent", {
"spam": "eggs",
"foo": "bar",
@@ -141,28 +141,28 @@ def send_now(users, label, extra_context=None, sender=None):
sent = False
if extra_context is None:
extra_context = {}
-
+
notice_type = NoticeType.objects.get(label=label)
-
+
current_language = get_language()
-
+
for user in users:
# get user language for user from language store defined in
# NOTIFICATION_LANGUAGE_MODULE setting
try:
language = get_notification_language(user)
except LanguageStoreNotAvailable:
language = None
-
+
if language is not None:
# activate the user's language
activate(language)
-
+
for backend in NOTIFICATION_BACKENDS.values():
if backend.can_send(user, notice_type):
backend.deliver(user, sender, notice_type, extra_context)
sent = True
-
+
# reset environment to original language
activate(current_language)
return sent
View
4 notification/templates/notification/notice_settings.html
@@ -9,7 +9,7 @@
{% block body %}
<h2>{% trans "Notification Settings" %}</h2>
-
+
{% url "account_settings" as email_url %}
{% if not request.user.email %}
{% blocktrans %}
@@ -47,7 +47,7 @@
</tr>
{% endfor %}
</table>
-
+
<div class="form-actions">
<button type="submit" class="btn btn-primary pull-right">{% trans "Change" %}</button>
</div>
View
3 notification/urls.py
@@ -3,6 +3,7 @@
from notification.views import notice_settings
-urlpatterns = patterns("",
+urlpatterns = patterns(
+ "",
url(r"^settings/$", notice_settings, name="notification_notice_settings"),
)
View
14 notification/views.py
@@ -11,14 +11,14 @@
def notice_settings(request):
"""
The notice settings view.
-
+
Template: :template:`notification/notice_settings.html`
-
+
Context:
-
+
notice_types
A list of all :model:`notification.NoticeType` objects.
-
+
notice_settings
A dictionary containing ``column_headers`` for each ``NOTICE_MEDIA``
and ``rows`` containing a list of dictionaries: ``notice_type``, a
@@ -45,16 +45,16 @@ def notice_settings(request):
setting.save()
settings_row.append((form_label, setting.send))
settings_table.append({"notice_type": notice_type, "cells": settings_row})
-
+
if request.method == "POST":
next_page = request.POST.get("next_page", ".")
return HttpResponseRedirect(next_page)
-
+
settings = {
"column_headers": [medium_display for medium_id, medium_display in NOTICE_MEDIA],
"rows": settings_table,
}
-
+
return render_to_response("notification/notice_settings.html", {
"notice_types": notice_types,
"notice_settings": settings,

0 comments on commit b2bdc5a

Please sign in to comment.