mirror of
https://github.com/DarrenOfficial/dpaste.git
synced 2024-11-23 11:56:36 +11:00
Black'ed the entire codebase
black dpaste/ --skip-string-normalization --exclude="migrations"
This commit is contained in:
parent
3b85c0e910
commit
00c6f058b9
15 changed files with 248 additions and 202 deletions
|
@ -1,9 +1,7 @@
|
|||
VERSION = (3, 0, 'a', 1)
|
||||
|
||||
__version__ = '{major}.{minor}{rest}'.format(
|
||||
major=VERSION[0],
|
||||
minor=VERSION[1],
|
||||
rest=''.join(str(i) for i in VERSION[2:])
|
||||
major=VERSION[0], minor=VERSION[1], rest=''.join(str(i) for i in VERSION[2:])
|
||||
)
|
||||
|
||||
default_app_config = 'dpaste.apps.dpasteAppConfig'
|
||||
|
|
|
@ -96,10 +96,11 @@ class dpasteAppConfig(AppConfig):
|
|||
from dpaste.highlight import (
|
||||
PlainTextHighlighter,
|
||||
MarkdownHighlighter,
|
||||
RestructuredTextHighlighter
|
||||
RestructuredTextHighlighter,
|
||||
)
|
||||
|
||||
return [
|
||||
(self.PLAIN_TEXT_SYMBOL, 'Plain Text', PlainTextHighlighter),
|
||||
(self.PLAIN_TEXT_SYMBOL, 'Plain Text', PlainTextHighlighter),
|
||||
('_markdown', 'Markdown', MarkdownHighlighter),
|
||||
('_rst', 'reStructuredText', RestructuredTextHighlighter),
|
||||
]
|
||||
|
@ -116,10 +117,8 @@ class dpasteAppConfig(AppConfig):
|
|||
|
||||
If the Highlight Class is not given, PygmentsHighlighter is used.
|
||||
"""
|
||||
from dpaste.highlight import (
|
||||
PlainCodeHighlighter,
|
||||
SolidityHighlighter
|
||||
)
|
||||
from dpaste.highlight import PlainCodeHighlighter, SolidityHighlighter
|
||||
|
||||
return [
|
||||
(self.PLAIN_CODE_SYMBOL, 'Plain Code', PlainCodeHighlighter),
|
||||
('abap', 'ABAP'),
|
||||
|
@ -204,6 +203,7 @@ class dpasteAppConfig(AppConfig):
|
|||
"""
|
||||
if apps.is_installed('django.contrib.sites'):
|
||||
from django.contrib.sites.shortcuts import get_current_site
|
||||
|
||||
site = get_current_site(request)
|
||||
if site:
|
||||
return 'https://{0}'.format(site.domain)
|
||||
|
|
|
@ -29,34 +29,27 @@ class SnippetForm(forms.ModelForm):
|
|||
label=_('Content'),
|
||||
widget=forms.Textarea(attrs={'placeholder': _('Awesome code goes here...')}),
|
||||
max_length=config.MAX_CONTENT_LENGTH,
|
||||
strip=False
|
||||
strip=False,
|
||||
)
|
||||
|
||||
lexer = forms.ChoiceField(
|
||||
label=_('Lexer'),
|
||||
initial=LEXER_DEFAULT,
|
||||
choices=LEXER_CHOICES
|
||||
label=_('Lexer'), initial=LEXER_DEFAULT, choices=LEXER_CHOICES
|
||||
)
|
||||
|
||||
expires = forms.ChoiceField(
|
||||
label=_('Expires'),
|
||||
choices=config.EXPIRE_CHOICES,
|
||||
initial=config.EXPIRE_DEFAULT
|
||||
label=_('Expires'), choices=config.EXPIRE_CHOICES, initial=config.EXPIRE_DEFAULT
|
||||
)
|
||||
|
||||
# Honeypot field
|
||||
title = forms.CharField(
|
||||
label=_('Title'),
|
||||
required=False,
|
||||
widget=forms.TextInput(attrs={'autocomplete': 'off'})
|
||||
widget=forms.TextInput(attrs={'autocomplete': 'off'}),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Snippet
|
||||
fields = (
|
||||
'content',
|
||||
'lexer',
|
||||
)
|
||||
fields = ('content', 'lexer')
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(SnippetForm, self).__init__(*args, **kwargs)
|
||||
|
|
|
@ -19,6 +19,7 @@ config = apps.get_app_config('dpaste')
|
|||
# Highlight Code Snippets
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
class Highlighter(object):
|
||||
template_name = 'dpaste/highlight/code.html'
|
||||
|
||||
|
@ -47,6 +48,7 @@ class Highlighter(object):
|
|||
|
||||
class PlainTextHighlighter(Highlighter):
|
||||
"""Plain Text. Just replace linebreaks."""
|
||||
|
||||
template_name = 'dpaste/highlight/text.html'
|
||||
|
||||
def highlight(self, code_string, **kwargs):
|
||||
|
@ -55,20 +57,33 @@ class PlainTextHighlighter(Highlighter):
|
|||
|
||||
class MarkdownHighlighter(PlainTextHighlighter):
|
||||
"""Markdown"""
|
||||
extensions = ('tables', 'fenced-code', 'footnotes', 'autolink,',
|
||||
'strikethrough', 'underline', 'quote', 'superscript',
|
||||
'math')
|
||||
|
||||
extensions = (
|
||||
'tables',
|
||||
'fenced-code',
|
||||
'footnotes',
|
||||
'autolink,',
|
||||
'strikethrough',
|
||||
'underline',
|
||||
'quote',
|
||||
'superscript',
|
||||
'math',
|
||||
)
|
||||
render_flags = ('skip-html',)
|
||||
|
||||
def highlight(self, code_string, **kwargs):
|
||||
import misaka
|
||||
return mark_safe(misaka.html(code_string,
|
||||
extensions=self.extensions,
|
||||
render_flags=self.render_flags))
|
||||
|
||||
return mark_safe(
|
||||
misaka.html(
|
||||
code_string, extensions=self.extensions, render_flags=self.render_flags
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class RestructuredTextHighlighter(PlainTextHighlighter):
|
||||
"""Restructured Text"""
|
||||
|
||||
rst_part_name = 'html_body'
|
||||
publish_args = {
|
||||
'writer_name': 'html5_polyglot',
|
||||
|
@ -78,11 +93,12 @@ class RestructuredTextHighlighter(PlainTextHighlighter):
|
|||
'halt_level': 5,
|
||||
'report_level': 2,
|
||||
'warning_stream': '/dev/null',
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
def highlight(self, code_string, **kwargs):
|
||||
from docutils.core import publish_parts
|
||||
|
||||
self.publish_args['source'] = code_string
|
||||
parts = publish_parts(**self.publish_args)
|
||||
return mark_safe(parts[self.rst_part_name])
|
||||
|
@ -93,6 +109,7 @@ class RestructuredTextHighlighter(PlainTextHighlighter):
|
|||
|
||||
class NakedHtmlFormatter(HtmlFormatter):
|
||||
"""Pygments HTML formatter with no further HTML tags."""
|
||||
|
||||
def wrap(self, source, outfile):
|
||||
return self._wrap_code(source)
|
||||
|
||||
|
@ -105,11 +122,14 @@ class PlainCodeHighlighter(Highlighter):
|
|||
"""
|
||||
Plain Code. No highlighting but Pygments like span tags around each line.
|
||||
"""
|
||||
|
||||
def highlight(self, code_string, **kwargs):
|
||||
return '\n'.join([
|
||||
'<span class="plain">{}</span>'.format(escape(l) or '​')
|
||||
return '\n'.join(
|
||||
[
|
||||
'<span class="plain">{}</span>'.format(escape(l) or '​')
|
||||
for l in code_string.splitlines()
|
||||
])
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class PygmentsHighlighter(Highlighter):
|
||||
|
@ -117,6 +137,7 @@ class PygmentsHighlighter(Highlighter):
|
|||
Highlight code string with Pygments. The lexer is automatically
|
||||
determined by the lexer name.
|
||||
"""
|
||||
|
||||
formatter = NakedHtmlFormatter()
|
||||
lexer = None
|
||||
lexer_fallback = PythonLexer()
|
||||
|
@ -139,6 +160,7 @@ class SolidityHighlighter(PygmentsHighlighter):
|
|||
# SolidityLexer does not necessarily need to be installed
|
||||
# since its imported here and not used later.
|
||||
from pygments_lexer_solidity import SolidityLexer
|
||||
|
||||
self.lexer = SolidityLexer()
|
||||
|
||||
|
||||
|
@ -167,12 +189,13 @@ def get_highlighter_class(lexer_name):
|
|||
# Generate a list of Form choices of all lexer.
|
||||
LEXER_CHOICES = (
|
||||
(_('Text'), [i[:2] for i in config.TEXT_FORMATTER]),
|
||||
(_('Code'), [i[:2] for i in config.CODE_FORMATTER])
|
||||
(_('Code'), [i[:2] for i in config.CODE_FORMATTER]),
|
||||
)
|
||||
|
||||
# List of all Lexer Keys
|
||||
LEXER_KEYS = [i[0] for i in config.TEXT_FORMATTER] + \
|
||||
[i[0] for i in config.CODE_FORMATTER]
|
||||
LEXER_KEYS = [i[0] for i in config.TEXT_FORMATTER] + [
|
||||
i[0] for i in config.CODE_FORMATTER
|
||||
]
|
||||
|
||||
# The default lexer which we fallback in case of
|
||||
# an error or if not supplied in an API call.
|
||||
|
|
|
@ -8,14 +8,15 @@ class Command(BaseCommand):
|
|||
help = "Purges snippets that are expired"
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('--dry-run', action='store_true', dest='dry_run',
|
||||
help='Don\'t do anything.'),
|
||||
parser.add_argument(
|
||||
'--dry-run', action='store_true', dest='dry_run', help='Don\'t do anything.'
|
||||
),
|
||||
|
||||
def handle(self, *args, **options):
|
||||
deleteable_snippets = Snippet.objects.filter(
|
||||
expires__isnull=False,
|
||||
expire_type=Snippet.EXPIRE_TIME,
|
||||
expires__lte=timezone.now()
|
||||
expires__lte=timezone.now(),
|
||||
)
|
||||
if len(deleteable_snippets) == 0:
|
||||
self.stdout.write(u"No snippets to delete.")
|
||||
|
|
|
@ -16,11 +16,14 @@ R = SystemRandom()
|
|||
|
||||
def generate_secret_id(length):
|
||||
if length > config.SLUG_LENGTH:
|
||||
logger.warning('Slug creation triggered a duplicate, '
|
||||
'consider increasing the SLUG_LENGTH.')
|
||||
logger.warning(
|
||||
'Slug creation triggered a duplicate, '
|
||||
'consider increasing the SLUG_LENGTH.'
|
||||
)
|
||||
|
||||
secret_id = ''.join([R.choice(config.SLUG_CHOICES)
|
||||
for i in range(length or config.SLUG_LENGTH)])
|
||||
secret_id = ''.join(
|
||||
[R.choice(config.SLUG_CHOICES) for i in range(length or config.SLUG_LENGTH)]
|
||||
)
|
||||
|
||||
# Check if this slug already exists, if not, return this new slug
|
||||
try:
|
||||
|
@ -30,7 +33,7 @@ def generate_secret_id(length):
|
|||
|
||||
# Otherwise create a new slug which is +1 character longer
|
||||
# than the previous one.
|
||||
return generate_secret_id(length=length+1)
|
||||
return generate_secret_id(length=length + 1)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
|
@ -45,21 +48,24 @@ class Snippet(models.Model):
|
|||
)
|
||||
|
||||
secret_id = models.CharField(
|
||||
_('Secret ID'), max_length=255, blank=True, null=True, unique=True)
|
||||
_('Secret ID'), max_length=255, blank=True, null=True, unique=True
|
||||
)
|
||||
content = models.TextField(_('Content'))
|
||||
lexer = models.CharField(
|
||||
_('Lexer'), max_length=30, default=highlight.LEXER_DEFAULT)
|
||||
published = models.DateTimeField(
|
||||
_('Published'), auto_now_add=True)
|
||||
lexer = models.CharField(_('Lexer'), max_length=30, default=highlight.LEXER_DEFAULT)
|
||||
published = models.DateTimeField(_('Published'), auto_now_add=True)
|
||||
expire_type = models.PositiveSmallIntegerField(
|
||||
_('Expire Type'), choices=EXPIRE_CHOICES, default=EXPIRE_CHOICES[0][0])
|
||||
expires = models.DateTimeField(
|
||||
_('Expires'), blank=True, null=True)
|
||||
view_count = models.PositiveIntegerField(
|
||||
_('View count'), default=0)
|
||||
_('Expire Type'), choices=EXPIRE_CHOICES, default=EXPIRE_CHOICES[0][0]
|
||||
)
|
||||
expires = models.DateTimeField(_('Expires'), blank=True, null=True)
|
||||
view_count = models.PositiveIntegerField(_('View count'), default=0)
|
||||
parent = models.ForeignKey(
|
||||
'self', null=True, blank=True, verbose_name=_('Parent Snippet'),
|
||||
related_name='children', on_delete=models.CASCADE)
|
||||
'self',
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name=_('Parent Snippet'),
|
||||
related_name='children',
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ('-published',)
|
||||
|
|
|
@ -49,9 +49,7 @@ USE_I18N = True
|
|||
USE_L10N = False
|
||||
|
||||
LANGUAGE_CODE = 'en'
|
||||
LANGUAGES = (
|
||||
('en', 'English'),
|
||||
)
|
||||
LANGUAGES = (('en', 'English'),)
|
||||
|
||||
# LOCALE_PATHS = (
|
||||
# os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'locale')),
|
||||
|
@ -63,9 +61,7 @@ LANGUAGES = (
|
|||
|
||||
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
|
||||
|
||||
STATICFILES_DIRS = (
|
||||
os.path.join(PROJECT_DIR, 'build'),
|
||||
)
|
||||
STATICFILES_DIRS = (os.path.join(PROJECT_DIR, 'build'),)
|
||||
|
||||
STATICFILES_FINDERS = (
|
||||
'django.contrib.staticfiles.finders.FileSystemFinder',
|
||||
|
@ -107,9 +103,9 @@ TEMPLATES = [
|
|||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.template.context_processors.i18n',
|
||||
],
|
||||
]
|
||||
},
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
INSTALLED_APPS = [
|
||||
|
@ -146,16 +142,12 @@ CSP_STYLE_SRC = ("'self'", "'unsafe-inline'")
|
|||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
'filters': {
|
||||
'require_debug_false': {
|
||||
'()': 'django.utils.log.RequireDebugFalse'
|
||||
}
|
||||
},
|
||||
'filters': {'require_debug_false': {'()': 'django.utils.log.RequireDebugFalse'}},
|
||||
'handlers': {
|
||||
'mail_admins': {
|
||||
'level': 'ERROR',
|
||||
'filters': ['require_debug_false'],
|
||||
'class': 'django.utils.log.AdminEmailHandler'
|
||||
'class': 'django.utils.log.AdminEmailHandler',
|
||||
}
|
||||
},
|
||||
'loggers': {
|
||||
|
@ -163,6 +155,6 @@ LOGGING = {
|
|||
'handlers': ['mail_admins'],
|
||||
'level': 'ERROR',
|
||||
'propagate': True,
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
@ -4,9 +4,4 @@ Settings for the test suite
|
|||
|
||||
from .base import *
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': ':memory:',
|
||||
}
|
||||
}
|
||||
DATABASES = {'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': ':memory:'}}
|
||||
|
|
|
@ -11,7 +11,6 @@ config = apps.get_app_config('dpaste')
|
|||
|
||||
|
||||
class SnippetAPITestCase(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.api_url = reverse('dpaste_api_create_snippet')
|
||||
self.client = Client(enforce_csrf_checks=True)
|
||||
|
@ -84,7 +83,6 @@ class SnippetAPITestCase(TestCase):
|
|||
self.assertTrue(content.startswith('http'))
|
||||
self.assertTrue(content.endswith('\n'))
|
||||
|
||||
|
||||
def test_json_format(self):
|
||||
"""
|
||||
The 'new' url format is just the link with a linebreak.
|
||||
|
@ -92,7 +90,7 @@ class SnippetAPITestCase(TestCase):
|
|||
data = {
|
||||
'content': u"Hello Wörld.\n\tGood Bye",
|
||||
'format': 'json',
|
||||
'lexer': 'haskell'
|
||||
'lexer': 'haskell',
|
||||
}
|
||||
|
||||
response = self.client.post(self.api_url, data)
|
||||
|
@ -102,6 +100,7 @@ class SnippetAPITestCase(TestCase):
|
|||
self.assertEqual(Snippet.objects.count(), 1)
|
||||
|
||||
from json import loads
|
||||
|
||||
json_data = loads(content)
|
||||
|
||||
# Response is valid json, containing, content, lexer and url
|
||||
|
@ -118,7 +117,7 @@ class SnippetAPITestCase(TestCase):
|
|||
data = {
|
||||
'content': u"Hello Wörld.\n\tGood Bye",
|
||||
'format': 'broken-format',
|
||||
'lexer': 'haskell'
|
||||
'lexer': 'haskell',
|
||||
}
|
||||
|
||||
response = self.client.post(self.api_url, data)
|
||||
|
@ -129,26 +128,25 @@ class SnippetAPITestCase(TestCase):
|
|||
"""
|
||||
A broken lexer will fail loudly.
|
||||
"""
|
||||
data = {
|
||||
'content': u"Hello Wörld.\n\tGood Bye",
|
||||
'lexer': 'foobar'
|
||||
}
|
||||
data = {'content': u"Hello Wörld.\n\tGood Bye", 'lexer': 'foobar'}
|
||||
response = self.client.post(self.api_url, data)
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(Snippet.objects.count(), 0)
|
||||
|
||||
def test_expire_choices_none_given(self):
|
||||
# No expire choice given will set a default expiration of one month
|
||||
response = self.client.post(self.api_url, {
|
||||
'content': u"Hello Wörld.\n\tGood Bye"})
|
||||
response = self.client.post(
|
||||
self.api_url, {'content': u"Hello Wörld.\n\tGood Bye"}
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(Snippet.objects.count(), 1)
|
||||
self.assertTrue(Snippet.objects.all()[0].expires)
|
||||
|
||||
def test_expire_choices_invalid_given(self):
|
||||
# A expire choice that does not exist returns a BadRequest
|
||||
response = self.client.post(self.api_url, {
|
||||
'content': u"Hello Wörld.\n\tGood Bye", 'expires': 'foobar'})
|
||||
response = self.client.post(
|
||||
self.api_url, {'content': u"Hello Wörld.\n\tGood Bye", 'expires': 'foobar'}
|
||||
)
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(Snippet.objects.count(), 0)
|
||||
|
||||
|
@ -156,37 +154,45 @@ class SnippetAPITestCase(TestCase):
|
|||
Test all the different expiration choices. We dont actually test
|
||||
the deletion, since thats handled in the `test_snippet` section.
|
||||
"""
|
||||
|
||||
def test_valid_expiration_choices_onetime(self):
|
||||
response = self.client.post(self.api_url, {
|
||||
'content': u"Hello Wörld.\n\tGood Bye", 'expires': 'onetime'})
|
||||
response = self.client.post(
|
||||
self.api_url, {'content': u"Hello Wörld.\n\tGood Bye", 'expires': 'onetime'}
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(Snippet.objects.count(), 1)
|
||||
self.assertEqual(Snippet.objects.all()[0].expire_type, Snippet.EXPIRE_ONETIME)
|
||||
|
||||
def test_valid_expiration_choices_never(self):
|
||||
response = self.client.post(self.api_url, {
|
||||
'content': u"Hello Wörld.\n\tGood Bye", 'expires': 'never'})
|
||||
response = self.client.post(
|
||||
self.api_url, {'content': u"Hello Wörld.\n\tGood Bye", 'expires': 'never'}
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(Snippet.objects.count(), 1)
|
||||
self.assertEqual(Snippet.objects.all()[0].expire_type, Snippet.EXPIRE_KEEP)
|
||||
|
||||
def test_valid_expiration_choices_hour(self):
|
||||
response = self.client.post(self.api_url, {
|
||||
'content': u"Hello Wörld.\n\tGood Bye", 'expires': 3600})
|
||||
response = self.client.post(
|
||||
self.api_url, {'content': u"Hello Wörld.\n\tGood Bye", 'expires': 3600}
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(Snippet.objects.count(), 1)
|
||||
self.assertTrue(Snippet.objects.all()[0].expires)
|
||||
|
||||
def test_valid_expiration_choices_week(self):
|
||||
response = self.client.post(self.api_url, {
|
||||
'content': u"Hello Wörld.\n\tGood Bye", 'expires': 3600 * 24 * 7})
|
||||
response = self.client.post(
|
||||
self.api_url,
|
||||
{'content': u"Hello Wörld.\n\tGood Bye", 'expires': 3600 * 24 * 7},
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(Snippet.objects.count(), 1)
|
||||
self.assertTrue(Snippet.objects.all()[0].expires)
|
||||
|
||||
def test_valid_expiration_choices_month(self):
|
||||
response = self.client.post(self.api_url, {
|
||||
'content': u"Hello Wörld.\n\tGood Bye", 'expires': 3600 * 24 * 30})
|
||||
response = self.client.post(
|
||||
self.api_url,
|
||||
{'content': u"Hello Wörld.\n\tGood Bye", 'expires': 3600 * 24 * 30},
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(Snippet.objects.count(), 1)
|
||||
self.assertTrue(Snippet.objects.all()[0].expires)
|
||||
|
@ -195,22 +201,24 @@ class SnippetAPITestCase(TestCase):
|
|||
"""
|
||||
No lexer and no filename given returns a BadRequest.
|
||||
"""
|
||||
response = self.client.post(self.api_url, {
|
||||
'content': u"Hello Wörld.\n\tGood Bye",
|
||||
'lexer': '',
|
||||
'filename': ''
|
||||
})
|
||||
response = self.client.post(
|
||||
self.api_url,
|
||||
{'content': u"Hello Wörld.\n\tGood Bye", 'lexer': '', 'filename': ''},
|
||||
)
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
def test_filename_given(self):
|
||||
"""
|
||||
No lexer and a Python filename will set a 'python' lexer.
|
||||
"""
|
||||
response = self.client.post(self.api_url, {
|
||||
'content': u"Hello Wörld.\n\tGood Bye",
|
||||
'lexer': '',
|
||||
'filename': 'helloworld.py'
|
||||
})
|
||||
response = self.client.post(
|
||||
self.api_url,
|
||||
{
|
||||
'content': u"Hello Wörld.\n\tGood Bye",
|
||||
'lexer': '',
|
||||
'filename': 'helloworld.py',
|
||||
},
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(Snippet.objects.count(), 1)
|
||||
self.assertEqual(Snippet.objects.all()[0].lexer, 'python')
|
||||
|
@ -219,11 +227,14 @@ class SnippetAPITestCase(TestCase):
|
|||
"""
|
||||
A unknown filename will create a 'plain' code snippet.
|
||||
"""
|
||||
response = self.client.post(self.api_url, {
|
||||
'content': u"Hello Wörld.\n\tGood Bye",
|
||||
'lexer': '',
|
||||
'filename': 'helloworld.helloworld'
|
||||
})
|
||||
response = self.client.post(
|
||||
self.api_url,
|
||||
{
|
||||
'content': u"Hello Wörld.\n\tGood Bye",
|
||||
'lexer': '',
|
||||
'filename': 'helloworld.helloworld',
|
||||
},
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(Snippet.objects.count(), 1)
|
||||
self.assertEqual(Snippet.objects.all()[0].lexer, config.PLAIN_CODE_SYMBOL)
|
||||
|
@ -232,11 +243,14 @@ class SnippetAPITestCase(TestCase):
|
|||
"""
|
||||
A given lexer will overwrite whats the filename guessing.
|
||||
"""
|
||||
response = self.client.post(self.api_url, {
|
||||
'content': u"Hello Wörld.\n\tGood Bye",
|
||||
'lexer': 'php',
|
||||
'filename': 'helloworld.py'
|
||||
})
|
||||
response = self.client.post(
|
||||
self.api_url,
|
||||
{
|
||||
'content': u"Hello Wörld.\n\tGood Bye",
|
||||
'lexer': 'php',
|
||||
'filename': 'helloworld.py',
|
||||
},
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(Snippet.objects.count(), 1)
|
||||
self.assertEqual(Snippet.objects.all()[0].lexer, 'php')
|
||||
|
@ -248,5 +262,3 @@ class SnippetAPITestCase(TestCase):
|
|||
content = ' one\n two\n three\n four'
|
||||
self.client.post(self.api_url, {'content': content})
|
||||
self.assertEqual(Snippet.objects.all()[0].content, content)
|
||||
|
||||
|
||||
|
|
|
@ -4,12 +4,14 @@ from textwrap import dedent
|
|||
|
||||
from django.test import TestCase
|
||||
|
||||
from dpaste.highlight import PlainCodeHighlighter, PygmentsHighlighter, \
|
||||
RestructuredTextHighlighter
|
||||
from dpaste.highlight import (
|
||||
PlainCodeHighlighter,
|
||||
PygmentsHighlighter,
|
||||
RestructuredTextHighlighter,
|
||||
)
|
||||
|
||||
|
||||
class HighlightAPITestCase(TestCase):
|
||||
|
||||
def test_plain_code(self):
|
||||
"""
|
||||
PLAIN_CODE is not run through Pygments, test it separately.
|
||||
|
@ -32,15 +34,13 @@ class HighlightAPITestCase(TestCase):
|
|||
"""
|
||||
Whitespace on the first line is retained, also on subsequent lines.
|
||||
"""
|
||||
input = (' vär=1\n'
|
||||
' vär=2\n'
|
||||
' vär=3\n'
|
||||
' vär=4')
|
||||
input = ' vär=1\n' ' vär=2\n' ' vär=3\n' ' vär=4'
|
||||
expected = (
|
||||
'<span class="plain"> vär=1</span>\n'
|
||||
'<span class="plain"> vär=2</span>\n'
|
||||
'<span class="plain"> vär=3</span>\n'
|
||||
'<span class="plain"> vär=4</span>')
|
||||
'<span class="plain"> vär=4</span>'
|
||||
)
|
||||
value = PlainCodeHighlighter().highlight(input)
|
||||
self.assertEqual(value, expected)
|
||||
|
||||
|
@ -67,15 +67,13 @@ class HighlightAPITestCase(TestCase):
|
|||
"""
|
||||
Whitespace on the first line is retained, also on subsequent lines.
|
||||
"""
|
||||
input = (' var\n'
|
||||
' var\n'
|
||||
' var\n'
|
||||
' var')
|
||||
input = ' var\n' ' var\n' ' var\n' ' var'
|
||||
expected = (
|
||||
' <span class="n">var</span>\n'
|
||||
' <span class="n">var</span>\n'
|
||||
' <span class="n">var</span>\n'
|
||||
' <span class="n">var</span>\n')
|
||||
' <span class="n">var</span>\n'
|
||||
)
|
||||
value = PygmentsHighlighter().highlight(input, 'python')
|
||||
self.assertEqual(value, expected)
|
||||
|
||||
|
@ -84,12 +82,14 @@ class HighlightAPITestCase(TestCase):
|
|||
rst Syntax thats not valid must not raise an exception (SystemMessage)
|
||||
"""
|
||||
# (SEVERE/4) Missing matching underline for section title overline.
|
||||
input = dedent("""
|
||||
input = dedent(
|
||||
"""
|
||||
=========================
|
||||
Generate 15 random numbers
|
||||
70 180 3 179 192 117 75 72 90 190 49 159 63 14 55
|
||||
=========================
|
||||
""")
|
||||
"""
|
||||
)
|
||||
try:
|
||||
RestructuredTextHighlighter().highlight(input)
|
||||
except Exception as e:
|
||||
|
|
|
@ -15,7 +15,6 @@ config = apps.get_app_config('dpaste')
|
|||
|
||||
|
||||
class SnippetTestCase(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.client = Client()
|
||||
self.new_url = reverse('snippet_new')
|
||||
|
@ -132,7 +131,6 @@ class SnippetTestCase(TestCase):
|
|||
self.assertEqual(response.status_code, 404)
|
||||
self.assertEqual(Snippet.objects.count(), 0)
|
||||
|
||||
|
||||
def test_snippet_notfound(self):
|
||||
url = reverse('snippet_details', kwargs={'snippet_id': 'abcd'})
|
||||
response = self.client.get(url, follow=True)
|
||||
|
@ -203,8 +201,12 @@ class SnippetTestCase(TestCase):
|
|||
def test_raw(self):
|
||||
data = self.valid_form_data()
|
||||
self.client.post(self.new_url, data, follow=True)
|
||||
response = self.client.get(reverse('snippet_details_raw', kwargs={
|
||||
'snippet_id': Snippet.objects.all()[0].secret_id}))
|
||||
response = self.client.get(
|
||||
reverse(
|
||||
'snippet_details_raw',
|
||||
kwargs={'snippet_id': Snippet.objects.all()[0].secret_id},
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, data['content'])
|
||||
|
@ -217,22 +219,23 @@ class SnippetTestCase(TestCase):
|
|||
|
||||
def test_xss_text_lexer(self):
|
||||
# Simple 'text' lexer
|
||||
data = self.valid_form_data(content=self.XSS_ORIGINAL,
|
||||
lexer=config.PLAIN_TEXT_SYMBOL)
|
||||
data = self.valid_form_data(
|
||||
content=self.XSS_ORIGINAL, lexer=config.PLAIN_TEXT_SYMBOL
|
||||
)
|
||||
response = self.client.post(self.new_url, data, follow=True)
|
||||
self.assertContains(response, self.XSS_ESCAPED)
|
||||
|
||||
def test_xss_code_lexer(self):
|
||||
# Simple 'code' lexer
|
||||
data = self.valid_form_data(content=self.XSS_ORIGINAL,
|
||||
lexer=config.PLAIN_CODE_SYMBOL)
|
||||
data = self.valid_form_data(
|
||||
content=self.XSS_ORIGINAL, lexer=config.PLAIN_CODE_SYMBOL
|
||||
)
|
||||
response = self.client.post(self.new_url, data, follow=True)
|
||||
self.assertContains(response, self.XSS_ESCAPED)
|
||||
|
||||
def test_xss_pygments_lexer(self):
|
||||
# Pygments based lexer
|
||||
data = self.valid_form_data(content=self.XSS_ORIGINAL,
|
||||
lexer='python')
|
||||
data = self.valid_form_data(content=self.XSS_ORIGINAL, lexer='python')
|
||||
response = self.client.post(self.new_url, data, follow=True)
|
||||
self.assertContains(response, self.XSS_ESCAPED)
|
||||
|
||||
|
@ -253,8 +256,9 @@ class SnippetTestCase(TestCase):
|
|||
|
||||
def test_snippet_history_delete_all(self):
|
||||
# Empty list, delete all raises no error
|
||||
response = self.client.post(reverse('snippet_history'),
|
||||
{'delete': 1}, follow=True)
|
||||
response = self.client.post(
|
||||
reverse('snippet_history'), {'delete': 1}, follow=True
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(Snippet.objects.count(), 0)
|
||||
|
||||
|
@ -266,8 +270,9 @@ class SnippetTestCase(TestCase):
|
|||
self.assertEqual(Snippet.objects.count(), 2)
|
||||
|
||||
# Delete all of them
|
||||
response = self.client.post(reverse('snippet_history'),
|
||||
{'delete': 1}, follow=True)
|
||||
response = self.client.post(
|
||||
reverse('snippet_history'), {'delete': 1}, follow=True
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(Snippet.objects.count(), 0)
|
||||
|
||||
|
@ -315,7 +320,6 @@ class SnippetTestCase(TestCase):
|
|||
PygmentsHighlighter().highlight('code', 'python')
|
||||
PygmentsHighlighter().highlight('code', 'doesnotexist')
|
||||
|
||||
|
||||
def test_random_slug_generation(self):
|
||||
"""
|
||||
Set the max length of a slug to 1, so we wont have more than 60
|
||||
|
@ -325,8 +329,9 @@ class SnippetTestCase(TestCase):
|
|||
"""
|
||||
for i in range(0, 100):
|
||||
Snippet.objects.create(content='foobar')
|
||||
slug_list = Snippet.objects.values_list(
|
||||
'secret_id', flat=True).order_by('published')
|
||||
slug_list = Snippet.objects.values_list('secret_id', flat=True).order_by(
|
||||
'published'
|
||||
)
|
||||
self.assertEqual(len(set(slug_list)), 100)
|
||||
|
||||
def test_leading_white_is_retained_in_db(self):
|
||||
|
|
|
@ -7,18 +7,21 @@ from .. import views
|
|||
L = getattr(settings, 'DPASTE_SLUG_LENGTH', 4)
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$',
|
||||
views.SnippetView.as_view(), name='snippet_new'),
|
||||
|
||||
url(r'^about/$',
|
||||
TemplateView.as_view(template_name='dpaste/about.html'), name='dpaste_about'),
|
||||
|
||||
url(r'^history/$',
|
||||
views.SnippetHistory.as_view(), name='snippet_history'),
|
||||
|
||||
url(r'^(?P<snippet_id>[a-zA-Z0-9]{%d,})/?$' % L,
|
||||
views.SnippetDetailView.as_view(), name='snippet_details'),
|
||||
|
||||
url(r'^(?P<snippet_id>[a-zA-Z0-9]{%d,})/raw/?$' % L,
|
||||
views.SnippetRawView.as_view(), name='snippet_details_raw'),
|
||||
url(r'^$', views.SnippetView.as_view(), name='snippet_new'),
|
||||
url(
|
||||
r'^about/$',
|
||||
TemplateView.as_view(template_name='dpaste/about.html'),
|
||||
name='dpaste_about',
|
||||
),
|
||||
url(r'^history/$', views.SnippetHistory.as_view(), name='snippet_history'),
|
||||
url(
|
||||
r'^(?P<snippet_id>[a-zA-Z0-9]{%d,})/?$' % L,
|
||||
views.SnippetDetailView.as_view(),
|
||||
name='snippet_details',
|
||||
),
|
||||
url(
|
||||
r'^(?P<snippet_id>[a-zA-Z0-9]{%d,})/raw/?$' % L,
|
||||
views.SnippetRawView.as_view(),
|
||||
name='snippet_details_raw',
|
||||
),
|
||||
]
|
||||
|
|
|
@ -4,5 +4,5 @@ from django.views.decorators.csrf import csrf_exempt
|
|||
from ..views import APIView
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^api/$', csrf_exempt(APIView.as_view()), name='dpaste_api_create_snippet'),
|
||||
url(r'^api/$', csrf_exempt(APIView.as_view()), name='dpaste_api_create_snippet')
|
||||
]
|
||||
|
|
|
@ -3,13 +3,19 @@ import difflib
|
|||
import json
|
||||
|
||||
from django.apps import apps
|
||||
from django.http import Http404, HttpResponse, HttpResponseBadRequest, \
|
||||
HttpResponseRedirect
|
||||
from django.http import (
|
||||
Http404,
|
||||
HttpResponse,
|
||||
HttpResponseBadRequest,
|
||||
HttpResponseRedirect,
|
||||
)
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import ugettext
|
||||
from django.views.defaults import page_not_found as django_page_not_found, \
|
||||
server_error as django_server_error
|
||||
from django.views.defaults import (
|
||||
page_not_found as django_page_not_found,
|
||||
server_error as django_server_error,
|
||||
)
|
||||
from django.views.generic import FormView
|
||||
from django.views.generic.base import TemplateView, View
|
||||
from django.views.generic.detail import DetailView
|
||||
|
@ -28,18 +34,18 @@ config = apps.get_app_config('dpaste')
|
|||
# Snippet Handling
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
class SnippetView(FormView):
|
||||
"""
|
||||
Create a new snippet.
|
||||
"""
|
||||
|
||||
form_class = SnippetForm
|
||||
template_name = 'dpaste/new.html'
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super(SnippetView, self).get_form_kwargs()
|
||||
kwargs.update({
|
||||
'request': self.request,
|
||||
})
|
||||
kwargs.update({'request': self.request})
|
||||
return kwargs
|
||||
|
||||
def form_valid(self, form):
|
||||
|
@ -52,6 +58,7 @@ class SnippetDetailView(SnippetView, DetailView):
|
|||
Details list view of a snippet. Handles the actual view, reply and
|
||||
tree/diff view.
|
||||
"""
|
||||
|
||||
queryset = Snippet.objects.all()
|
||||
template_name = 'dpaste/details.html'
|
||||
slug_url_kwarg = 'snippet_id'
|
||||
|
@ -78,8 +85,10 @@ class SnippetDetailView(SnippetView, DetailView):
|
|||
snippet = self.get_object()
|
||||
|
||||
# One-Time snippet get deleted if the view count matches our limit
|
||||
if (snippet.expire_type == Snippet.EXPIRE_ONETIME and
|
||||
snippet.view_count >= config.ONETIME_LIMIT):
|
||||
if (
|
||||
snippet.expire_type == Snippet.EXPIRE_ONETIME
|
||||
and snippet.view_count >= config.ONETIME_LIMIT
|
||||
):
|
||||
snippet.delete()
|
||||
raise Http404()
|
||||
|
||||
|
@ -91,10 +100,7 @@ class SnippetDetailView(SnippetView, DetailView):
|
|||
|
||||
def get_initial(self):
|
||||
snippet = self.get_object()
|
||||
return {
|
||||
'content': snippet.content,
|
||||
'lexer': snippet.lexer,
|
||||
}
|
||||
return {'content': snippet.content, 'lexer': snippet.lexer}
|
||||
|
||||
def form_valid(self, form):
|
||||
snippet = form.save(parent=self.get_object())
|
||||
|
@ -114,7 +120,7 @@ class SnippetDetailView(SnippetView, DetailView):
|
|||
snippet.content.splitlines(),
|
||||
ugettext('Previous Snippet'),
|
||||
ugettext('Current Snippet'),
|
||||
n=1
|
||||
n=1,
|
||||
)
|
||||
diff_code = '\n'.join(d).strip()
|
||||
highlighted = PygmentsHighlighter().render(diff_code, 'diff')
|
||||
|
@ -126,10 +132,12 @@ class SnippetDetailView(SnippetView, DetailView):
|
|||
self.object = self.get_object()
|
||||
|
||||
ctx = super(SnippetDetailView, self).get_context_data(**kwargs)
|
||||
ctx.update({
|
||||
'wordwrap': self.object.lexer in highlight.LEXER_WORDWRAP,
|
||||
'diff': self.get_snippet_diff(),
|
||||
})
|
||||
ctx.update(
|
||||
{
|
||||
'wordwrap': self.object.lexer in highlight.LEXER_WORDWRAP,
|
||||
'diff': self.get_snippet_diff(),
|
||||
}
|
||||
)
|
||||
return ctx
|
||||
|
||||
|
||||
|
@ -137,6 +145,7 @@ class SnippetRawView(SnippetDetailView):
|
|||
"""
|
||||
Display the raw content of a snippet
|
||||
"""
|
||||
|
||||
def render_to_response(self, context, **response_kwargs):
|
||||
snippet = self.get_object()
|
||||
response = HttpResponse(snippet.content)
|
||||
|
@ -150,6 +159,7 @@ class SnippetHistory(TemplateView):
|
|||
Display the last `n` snippets created by this user (and saved in his
|
||||
session).
|
||||
"""
|
||||
|
||||
template_name = 'dpaste/history.html'
|
||||
|
||||
def get_user_snippets(self):
|
||||
|
@ -169,9 +179,7 @@ class SnippetHistory(TemplateView):
|
|||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super(SnippetHistory, self).get_context_data(**kwargs)
|
||||
ctx.update({
|
||||
'snippet_list': self.get_user_snippets(),
|
||||
})
|
||||
ctx.update({'snippet_list': self.get_user_snippets()})
|
||||
return ctx
|
||||
|
||||
|
||||
|
@ -184,6 +192,7 @@ class APIView(View):
|
|||
"""
|
||||
API View
|
||||
"""
|
||||
|
||||
def _format_default(self, s):
|
||||
"""
|
||||
The default response is the snippet URL wrapped in quotes.
|
||||
|
@ -204,11 +213,13 @@ class APIView(View):
|
|||
The `json` format export.
|
||||
"""
|
||||
base_url = config.get_base_url(request=self.request)
|
||||
return json.dumps({
|
||||
'url': '{url}{path}'.format(url=base_url, path=s.get_absolute_url()),
|
||||
'content': s.content,
|
||||
'lexer': s.lexer,
|
||||
})
|
||||
return json.dumps(
|
||||
{
|
||||
'url': '{url}{path}'.format(url=base_url, path=s.get_absolute_url()),
|
||||
'content': s.content,
|
||||
'lexer': s.lexer,
|
||||
}
|
||||
)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
content = request.POST.get('content', '')
|
||||
|
@ -222,13 +233,18 @@ class APIView(View):
|
|||
|
||||
# We need at least a lexer or a filename
|
||||
if not lexer and not filename:
|
||||
return HttpResponseBadRequest('No lexer or filename given. Unable to '
|
||||
'determine a highlight. Valid lexers are: %s' % ', '.join(highlight.LEXER_KEYS))
|
||||
return HttpResponseBadRequest(
|
||||
'No lexer or filename given. Unable to '
|
||||
'determine a highlight. Valid lexers are: %s'
|
||||
% ', '.join(highlight.LEXER_KEYS)
|
||||
)
|
||||
|
||||
# A lexer is given, check if its valid at all
|
||||
if lexer and lexer not in highlight.LEXER_KEYS:
|
||||
return HttpResponseBadRequest('Invalid lexer "%s" given. Valid lexers are: %s' % (
|
||||
lexer, ', '.join(highlight.LEXER_KEYS)))
|
||||
return HttpResponseBadRequest(
|
||||
'Invalid lexer "%s" given. Valid lexers are: %s'
|
||||
% (lexer, ', '.join(highlight.LEXER_KEYS))
|
||||
)
|
||||
|
||||
# No lexer is given, but we have a filename, try to get the lexer
|
||||
# out of it. In case Pygments cannot determine the lexer of the
|
||||
|
@ -245,17 +261,16 @@ class APIView(View):
|
|||
if expires not in expire_options:
|
||||
return HttpResponseBadRequest(
|
||||
'Invalid expire choice "{}" given. Valid values are: {}'.format(
|
||||
expires, ', '.join(expire_options)))
|
||||
expires, ', '.join(expire_options)
|
||||
)
|
||||
)
|
||||
expires, expire_type = get_expire_values(expires)
|
||||
else:
|
||||
expires = datetime.datetime.now() + datetime.timedelta(seconds=60 * 60 * 24)
|
||||
expire_type = Snippet.EXPIRE_TIME
|
||||
|
||||
snippet = Snippet.objects.create(
|
||||
content=content,
|
||||
lexer=lexer,
|
||||
expires=expires,
|
||||
expire_type=expire_type,
|
||||
content=content, lexer=lexer, expires=expires, expire_type=expire_type
|
||||
)
|
||||
|
||||
# Custom formatter for the API response
|
||||
|
@ -272,6 +287,7 @@ class APIView(View):
|
|||
# handle them here.
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
def page_not_found(request, exception=None, template_name='dpaste/404.html'):
|
||||
return django_page_not_found(request, exception, template_name=template_name)
|
||||
|
||||
|
|
|
@ -8,7 +8,9 @@ https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/
|
|||
"""
|
||||
|
||||
import os
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dpaste.settings.local")
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
application = get_wsgi_application()
|
||||
|
|
Loading…
Reference in a new issue