Quote cleanup, fStrings and Deprecation fixes.

This commit is contained in:
Martin Mahner 2019-12-07 07:20:52 +01:00
parent cd586f62fe
commit e54790c8f9
17 changed files with 411 additions and 415 deletions

View file

@ -10,5 +10,5 @@ docutils = "==0.15"
[scripts] [scripts]
runserver = "sh -c \"./manage.py migrate && ./manage.py runserver 0:8000\"" runserver = "sh -c \"./manage.py migrate && ./manage.py runserver 0:8000\""
test = "pytest dpaste" test = "pytest dpaste"
cleanup = "sh -c \"isort -rc dpaste && black --skip-string-normalization --line-length=80 --exclude='/(migrations)/' dpaste\"" cleanup = "sh -c \"isort -rc dpaste && black --line-length=80 --exclude='/(migrations)/' dpaste\""
docs = "sphinx-build docs docs/_build/html" docs = "sphinx-build docs docs/_build/html"

View file

@ -1,9 +1,9 @@
VERSION = (3, 4, 'a0') VERSION = (3, 4, "a0")
__version__ = '{major}.{minor}{rest}'.format( __version__ = "{major}.{minor}{rest}".format(
major=VERSION[0], major=VERSION[0],
minor=VERSION[1], minor=VERSION[1],
rest=''.join(str(i) for i in VERSION[2:]), rest="".join(str(i) for i in VERSION[2:]),
) )
default_app_config = 'dpaste.apps.dpasteAppConfig' default_app_config = "dpaste.apps.dpasteAppConfig"

View file

@ -1,14 +1,14 @@
from django.apps import AppConfig, apps from django.apps import AppConfig, apps
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
class dpasteAppConfig(AppConfig): class dpasteAppConfig(AppConfig):
name = 'dpaste' name = "dpaste"
verbose_name = 'dpaste' verbose_name = "dpaste"
# The application title used throughout the user interface. # The application title used throughout the user interface.
APPLICATION_NAME = 'dpaste' APPLICATION_NAME = "dpaste"
# This content is loaded in the <head> section of each template. # This content is loaded in the <head> section of each template.
# You can use it to add any HTML tags, specifically custom CSS styles. # You can use it to add any HTML tags, specifically custom CSS styles.
@ -24,7 +24,7 @@ class dpasteAppConfig(AppConfig):
# .btn { background-color: blue; border: 3px solid yellow; } # .btn { background-color: blue; border: 3px solid yellow; }
# </style> # </style>
# """ # """
EXTRA_HEAD_HTML = '' EXTRA_HEAD_HTML = ""
# Integer. Length of the random slug for each new snippet. In the rare # Integer. Length of the random slug for each new snippet. In the rare
# case an existing slug is generated again, the length will increase by # case an existing slug is generated again, the length will increase by
@ -35,12 +35,12 @@ class dpasteAppConfig(AppConfig):
# This is intentionally missing l and I as they look too similar with # This is intentionally missing l and I as they look too similar with
# sans-serif fonts. # sans-serif fonts.
SLUG_CHOICES = ( SLUG_CHOICES = (
'abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ1234567890' "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ1234567890"
) )
# String. The lexer key that is pre-selected in the dropdown. Note that # String. The lexer key that is pre-selected in the dropdown. Note that
# this is only used if the user has not saved a snippet before, otherwise # this is only used if the user has not saved a snippet before, otherwise
LEXER_DEFAULT = 'python' LEXER_DEFAULT = "python"
# Integer. Maximum number of bytes per snippet. # Integer. Maximum number of bytes per snippet.
MAX_CONTENT_LENGTH = 250 * 1024 * 1024 MAX_CONTENT_LENGTH = 250 * 1024 * 1024
@ -48,7 +48,7 @@ class dpasteAppConfig(AppConfig):
# A tuple of seconds and a descriptive string used in the lexer # A tuple of seconds and a descriptive string used in the lexer
# expiration dropdown. Example:: # expiration dropdown. Example::
# #
# from django.utils.translation import ugettext_lazy as _ # from django.utils.translation import gettext_lazy as _
# DPASTE_EXPIRE_CHOICES = ( # DPASTE_EXPIRE_CHOICES = (
# (3600, _('In one hour')), # (3600, _('In one hour')),
# (3600 * 24 * 7, _('In one week')), # (3600 * 24 * 7, _('In one week')),
@ -60,17 +60,17 @@ class dpasteAppConfig(AppConfig):
# you set the choice key to ``never``. The management command will ignore # you set the choice key to ``never``. The management command will ignore
# these snippets:: # these snippets::
# #
# from django.utils.translation import ugettext_lazy as _ # from django.utils.translation import gettext_lazy as _
# DPASTE_EXPIRE_CHOICES = ( # DPASTE_EXPIRE_CHOICES = (
# (3600, _('In one hour')), # (3600, _('In one hour')),
# ('never', _('Never')), # ('never', _('Never')),
# ) # )
EXPIRE_CHOICES = ( EXPIRE_CHOICES = (
('onetime', _('One-Time snippet')), ("onetime", _("One-Time snippet")),
(3600, _('In one hour')), (3600, _("In one hour")),
(3600 * 24 * 7, _('In one week')), (3600 * 24 * 7, _("In one week")),
(3600 * 24 * 30, _('In one month')), (3600 * 24 * 30, _("In one month")),
('never', _('Never')), ("never", _("Never")),
) )
# Default value for ``EXPIRE_CHOICES`` # Default value for ``EXPIRE_CHOICES``
@ -81,7 +81,7 @@ class dpasteAppConfig(AppConfig):
# enable one-time snippets you have to add a choice ``onetime`` to the # enable one-time snippets you have to add a choice ``onetime`` to the
# expire choices:: # expire choices::
# #
# from django.utils.translation import ugettext_lazy as _ # from django.utils.translation import gettext_lazy as _
# DPASTE_EXPIRE_CHOICES = ( # DPASTE_EXPIRE_CHOICES = (
# ('onetime', _('One-Time snippet')), # ('onetime', _('One-Time snippet')),
# (3600, _('In one hour')), # (3600, _('In one hour')),
@ -102,11 +102,11 @@ class dpasteAppConfig(AppConfig):
RAW_MODE_PLAIN_TEXT = True RAW_MODE_PLAIN_TEXT = True
# Lexers which have wordwrap enabled by default # Lexers which have wordwrap enabled by default
LEXER_WORDWRAP = ('rst',) LEXER_WORDWRAP = ("rst",)
# Key names of the default text and code lexer. # Key names of the default text and code lexer.
PLAIN_TEXT_SYMBOL = '_text' PLAIN_TEXT_SYMBOL = "_text"
PLAIN_CODE_SYMBOL = '_code' PLAIN_CODE_SYMBOL = "_code"
@property @property
def TEXT_FORMATTER(self): def TEXT_FORMATTER(self):
@ -129,9 +129,9 @@ class dpasteAppConfig(AppConfig):
) )
return [ return [
(self.PLAIN_TEXT_SYMBOL, 'Plain Text', PlainTextHighlighter), (self.PLAIN_TEXT_SYMBOL, "Plain Text", PlainTextHighlighter),
('_markdown', 'Markdown', MarkdownHighlighter), ("_markdown", "Markdown", MarkdownHighlighter),
('_rst', 'reStructuredText', RestructuredTextHighlighter), ("_rst", "reStructuredText", RestructuredTextHighlighter),
] ]
@property @property
@ -159,7 +159,7 @@ class dpasteAppConfig(AppConfig):
from jsx.lexer import JsxLexer from jsx.lexer import JsxLexer
return [ return [
(self.PLAIN_CODE_SYMBOL, 'Plain Code', PlainCodeHighlighter), (self.PLAIN_CODE_SYMBOL, "Plain Code", PlainCodeHighlighter),
# ('abap', 'ABAP'), # ('abap', 'ABAP'),
# ('abnf', 'ABNF'), # ('abnf', 'ABNF'),
# ('ada', 'Ada'), # ('ada', 'Ada'),
@ -180,8 +180,8 @@ class dpasteAppConfig(AppConfig):
# ('antlr-ruby', 'ANTLR With Ruby Target'), # ('antlr-ruby', 'ANTLR With Ruby Target'),
# ('apacheconf', 'ApacheConf'), # ('apacheconf', 'ApacheConf'),
# ('apl', 'APL'), # ('apl', 'APL'),
('applescript', 'AppleScript'), ("applescript", "AppleScript"),
('arduino', 'Arduino'), ("arduino", "Arduino"),
# ('as', 'ActionScript'), # ('as', 'ActionScript'),
# ('as3', 'ActionScript 3'), # ('as3', 'ActionScript 3'),
# ('aspectj', 'AspectJ'), # ('aspectj', 'AspectJ'),
@ -192,8 +192,8 @@ class dpasteAppConfig(AppConfig):
# ('autoit', 'AutoIt'), # ('autoit', 'AutoIt'),
# ('awk', 'Awk'), # ('awk', 'Awk'),
# ('basemake', 'Base Makefile'), # ('basemake', 'Base Makefile'),
('bash', 'Bash'), ("bash", "Bash"),
('bat', 'Batchfile'), ("bat", "Batchfile"),
# ('bbcode', 'BBCode'), # ('bbcode', 'BBCode'),
# ('bc', 'BC'), # ('bc', 'BC'),
# ('befunge', 'Befunge'), # ('befunge', 'Befunge'),
@ -207,7 +207,7 @@ class dpasteAppConfig(AppConfig):
# ('bro', 'Bro'), # ('bro', 'Bro'),
# ('bst', 'BST'), # ('bst', 'BST'),
# ('bugs', 'BUGS'), # ('bugs', 'BUGS'),
('c', 'C'), ("c", "C"),
# ('c-objdump', 'c-objdump'), # ('c-objdump', 'c-objdump'),
# ('ca65', 'ca65 assembler'), # ('ca65', 'ca65 assembler'),
# ('cadl', 'cADL'), # ('cadl', 'cADL'),
@ -226,15 +226,15 @@ class dpasteAppConfig(AppConfig):
# ('cirru', 'Cirru'), # ('cirru', 'Cirru'),
# ('clay', 'Clay'), # ('clay', 'Clay'),
# ('clean', 'Clean'), # ('clean', 'Clean'),
('clojure', 'Clojure'), ("clojure", "Clojure"),
# ('clojurescript', 'ClojureScript'), # ('clojurescript', 'ClojureScript'),
('cmake', 'CMake'), ("cmake", "CMake"),
# ('cobol', 'COBOL'), # ('cobol', 'COBOL'),
# ('cobolfree', 'COBOLFree'), # ('cobolfree', 'COBOLFree'),
('coffee-script', 'CoffeeScript'), ("coffee-script", "CoffeeScript"),
('common-lisp', 'Common Lisp'), ("common-lisp", "Common Lisp"),
# ('componentpascal', 'Component Pascal'), # ('componentpascal', 'Component Pascal'),
('console', 'Console/Bash Session'), ("console", "Console/Bash Session"),
# ('control', 'Debian Control file'), # ('control', 'Debian Control file'),
# ('coq', 'Coq'), # ('coq', 'Coq'),
# ('cpp', 'C++'), # ('cpp', 'C++'),
@ -244,11 +244,11 @@ class dpasteAppConfig(AppConfig):
# ('crmsh', 'Crmsh'), # ('crmsh', 'Crmsh'),
# ('croc', 'Croc'), # ('croc', 'Croc'),
# ('cryptol', 'Cryptol'), # ('cryptol', 'Cryptol'),
('csharp', 'C#'), ("csharp", "C#"),
# ('csound', 'Csound Orchestra'), # ('csound', 'Csound Orchestra'),
# ('csound-document', 'Csound Document'), # ('csound-document', 'Csound Document'),
# ('csound-score', 'Csound Score'), # ('csound-score', 'Csound Score'),
('css', 'CSS'), ("css", "CSS"),
# ('css+django', 'CSS+Django/Jinja'), # ('css+django', 'CSS+Django/Jinja'),
# ('css+erb', 'CSS+Ruby'), # ('css+erb', 'CSS+Ruby'),
# ('css+genshitext', 'CSS+Genshi Text'), # ('css+genshitext', 'CSS+Genshi Text'),
@ -259,17 +259,17 @@ class dpasteAppConfig(AppConfig):
# ('css+php', 'CSS+PHP'), # ('css+php', 'CSS+PHP'),
# ('css+smarty', 'CSS+Smarty'), # ('css+smarty', 'CSS+Smarty'),
# ('cucumber', 'Gherkin'), # ('cucumber', 'Gherkin'),
('cuda', 'CUDA'), ("cuda", "CUDA"),
# ('cypher', 'Cypher'), # ('cypher', 'Cypher'),
# ('cython', 'Cython'), # ('cython', 'Cython'),
# ('d', 'D'), # ('d', 'D'),
# ('d-objdump', 'd-objdump'), # ('d-objdump', 'd-objdump'),
('dart', 'Dart'), ("dart", "Dart"),
('delphi', 'Delphi'), ("delphi", "Delphi"),
# ('dg', 'dg'), # ('dg', 'dg'),
('diff', 'Diff'), ("diff", "Diff"),
('django', 'Django/Jinja'), ("django", "Django/Jinja"),
('docker', 'Docker'), ("docker", "Docker"),
# ('doscon', 'MSDOS Session'), # ('doscon', 'MSDOS Session'),
# ('dpatch', 'Darcs Patch'), # ('dpatch', 'Darcs Patch'),
# ('dtd', 'DTD'), # ('dtd', 'DTD'),
@ -283,12 +283,12 @@ class dpasteAppConfig(AppConfig):
# ('ec', 'eC'), # ('ec', 'eC'),
# ('ecl', 'ECL'), # ('ecl', 'ECL'),
# ('eiffel', 'Eiffel'), # ('eiffel', 'Eiffel'),
('elixir', 'Elixir'), ("elixir", "Elixir"),
# ('elm', 'Elm'), # ('elm', 'Elm'),
# ('emacs', 'EmacsLisp'), # ('emacs', 'EmacsLisp'),
# ('erb', 'ERB'), # ('erb', 'ERB'),
# ('erl', 'Erlang erl session'), # ('erl', 'Erlang erl session'),
('erlang', 'Erlang'), ("erlang", "Erlang"),
# ('evoque', 'Evoque'), # ('evoque', 'Evoque'),
# ('extempore', 'xtlang'), # ('extempore', 'xtlang'),
# ('ezhil', 'Ezhil'), # ('ezhil', 'Ezhil'),
@ -310,7 +310,7 @@ class dpasteAppConfig(AppConfig):
# ('genshitext', 'Genshi Text'), # ('genshitext', 'Genshi Text'),
# ('glsl', 'GLSL'), # ('glsl', 'GLSL'),
# ('gnuplot', 'Gnuplot'), # ('gnuplot', 'Gnuplot'),
('go', 'Go'), ("go", "Go"),
# ('golo', 'Golo'), # ('golo', 'Golo'),
# ('gooddata-cl', 'GoodData-CL'), # ('gooddata-cl', 'GoodData-CL'),
# ('gosu', 'Gosu'), # ('gosu', 'Gosu'),
@ -318,15 +318,15 @@ class dpasteAppConfig(AppConfig):
# ('groovy', 'Groovy'), # ('groovy', 'Groovy'),
# ('gst', 'Gosu Template'), # ('gst', 'Gosu Template'),
# ('haml', 'Haml'), # ('haml', 'Haml'),
('handlebars', 'Handlebars'), ("handlebars", "Handlebars"),
('haskell', 'Haskell'), ("haskell", "Haskell"),
# ('haxeml', 'Hxml'), # ('haxeml', 'Hxml'),
# ('hexdump', 'Hexdump'), # ('hexdump', 'Hexdump'),
# ('hlsl', 'HLSL'), # ('hlsl', 'HLSL'),
# ('hsail', 'HSAIL'), # ('hsail', 'HSAIL'),
('html', 'HTML'), ("html", "HTML"),
# ('html+cheetah', 'HTML+Cheetah'), # ('html+cheetah', 'HTML+Cheetah'),
('html+django', 'HTML + Django/Jinja'), ("html+django", "HTML + Django/Jinja"),
# ('html+evoque', 'HTML+Evoque'), # ('html+evoque', 'HTML+Evoque'),
# ('html+genshi', 'HTML+Genshi'), # ('html+genshi', 'HTML+Genshi'),
# ('html+handlebars', 'HTML+Handlebars'), # ('html+handlebars', 'HTML+Handlebars'),
@ -349,22 +349,22 @@ class dpasteAppConfig(AppConfig):
# ('igor', 'Igor'), # ('igor', 'Igor'),
# ('inform6', 'Inform 6'), # ('inform6', 'Inform 6'),
# ('inform7', 'Inform 7'), # ('inform7', 'Inform 7'),
('ini', 'INI'), ("ini", "INI"),
# ('io', 'Io'), # ('io', 'Io'),
# ('ioke', 'Ioke'), # ('ioke', 'Ioke'),
# ('ipython2', 'IPython'), # ('ipython2', 'IPython'),
# ('ipython3', 'IPython3'), # ('ipython3', 'IPython3'),
('ipythonconsole', 'IPython console session'), ("ipythonconsole", "IPython console session"),
('irc', 'IRC logs'), ("irc", "IRC logs"),
# ('isabelle', 'Isabelle'), # ('isabelle', 'Isabelle'),
# ('j', 'J'), # ('j', 'J'),
# ('jags', 'JAGS'), # ('jags', 'JAGS'),
# ('jasmin', 'Jasmin'), # ('jasmin', 'Jasmin'),
('java', 'Java'), ("java", "Java"),
# ('javascript+mozpreproc', 'Javascript+mozpreproc'), # ('javascript+mozpreproc', 'Javascript+mozpreproc'),
# ('jcl', 'JCL'), # ('jcl', 'JCL'),
# ('jlcon', 'Julia console'), # ('jlcon', 'Julia console'),
('js', 'JavaScript'), ("js", "JavaScript"),
# ('js+cheetah', 'JavaScript+Cheetah'), # ('js+cheetah', 'JavaScript+Cheetah'),
# ('js+django', 'JavaScript+Django/Jinja'), # ('js+django', 'JavaScript+Django/Jinja'),
# ('js+erb', 'JavaScript+Ruby'), # ('js+erb', 'JavaScript+Ruby'),
@ -375,8 +375,8 @@ class dpasteAppConfig(AppConfig):
# ('js+php', 'JavaScript+PHP'), # ('js+php', 'JavaScript+PHP'),
# ('js+smarty', 'JavaScript+Smarty'), # ('js+smarty', 'JavaScript+Smarty'),
# ('jsgf', 'JSGF'), # ('jsgf', 'JSGF'),
('json', 'JSON'), ("json", "JSON"),
('jsx', 'JSX/React'), ("jsx", "JSX/React"),
# ('json-object', 'JSONBareObject'), # ('json-object', 'JSONBareObject'),
# ('jsonld', 'JSON-LD'), # ('jsonld', 'JSON-LD'),
# ('jsp', 'Java Server Page'), # ('jsp', 'Java Server Page'),
@ -385,12 +385,12 @@ class dpasteAppConfig(AppConfig):
# ('kal', 'Kal'), # ('kal', 'Kal'),
# ('kconfig', 'Kconfig'), # ('kconfig', 'Kconfig'),
# ('koka', 'Koka'), # ('koka', 'Koka'),
('kotlin', 'Kotlin'), ("kotlin", "Kotlin"),
# ('lagda', 'Literate Agda'), # ('lagda', 'Literate Agda'),
# ('lasso', 'Lasso'), # ('lasso', 'Lasso'),
# ('lcry', 'Literate Cryptol'), # ('lcry', 'Literate Cryptol'),
# ('lean', 'Lean'), # ('lean', 'Lean'),
('less', 'LessCSS'), ("less", "LessCSS"),
# ('lhs', 'Literate Haskell'), # ('lhs', 'Literate Haskell'),
# ('lidr', 'Literate Idris'), # ('lidr', 'Literate Idris'),
# ('lighty', 'Lighttpd configuration file'), # ('lighty', 'Lighttpd configuration file'),
@ -401,14 +401,14 @@ class dpasteAppConfig(AppConfig):
# ('logos', 'Logos'), # ('logos', 'Logos'),
# ('logtalk', 'Logtalk'), # ('logtalk', 'Logtalk'),
# ('lsl', 'LSL'), # ('lsl', 'LSL'),
('lua', 'Lua'), ("lua", "Lua"),
('make', 'Makefile'), ("make", "Makefile"),
# ('mako', 'Mako'), # ('mako', 'Mako'),
# ('maql', 'MAQL'), # ('maql', 'MAQL'),
# ('mask', 'Mask'), # ('mask', 'Mask'),
# ('mason', 'Mason'), # ('mason', 'Mason'),
# ('mathematica', 'Mathematica'), # ('mathematica', 'Mathematica'),
('matlab', 'Matlab'), ("matlab", "Matlab"),
# ('matlabsession', 'Matlab session'), # ('matlabsession', 'Matlab session'),
# ('md', 'markdown'), # ('md', 'markdown'),
# ('minid', 'MiniD'), # ('minid', 'MiniD'),
@ -433,16 +433,16 @@ class dpasteAppConfig(AppConfig):
# ('newlisp', 'NewLisp'), # ('newlisp', 'NewLisp'),
# ('newspeak', 'Newspeak'), # ('newspeak', 'Newspeak'),
# ('ng2', 'Angular2'), # ('ng2', 'Angular2'),
('nginx', 'Nginx configuration file'), ("nginx", "Nginx configuration file"),
# ('nim', 'Nimrod'), # ('nim', 'Nimrod'),
# ('nit', 'Nit'), # ('nit', 'Nit'),
# ('nixos', 'Nix'), # ('nixos', 'Nix'),
# ('nsis', 'NSIS'), # ('nsis', 'NSIS'),
('numpy', 'NumPy'), ("numpy", "NumPy"),
# ('nusmv', 'NuSMV'), # ('nusmv', 'NuSMV'),
# ('objdump', 'objdump'), # ('objdump', 'objdump'),
# ('objdump-nasm', 'objdump-nasm'), # ('objdump-nasm', 'objdump-nasm'),
('objective-c', 'Objective-C'), ("objective-c", "Objective-C"),
# ('objective-c++', 'Objective-C++'), # ('objective-c++', 'Objective-C++'),
# ('objective-j', 'Objective-J'), # ('objective-j', 'Objective-J'),
# ('ocaml', 'OCaml'), # ('ocaml', 'OCaml'),
@ -455,14 +455,14 @@ class dpasteAppConfig(AppConfig):
# ('pan', 'Pan'), # ('pan', 'Pan'),
# ('parasail', 'ParaSail'), # ('parasail', 'ParaSail'),
# ('pawn', 'Pawn'), # ('pawn', 'Pawn'),
('perl', 'Perl'), ("perl", "Perl"),
# ('perl6', 'Perl6'), # ('perl6', 'Perl6'),
('php', 'PHP'), ("php", "PHP"),
# ('pig', 'Pig'), # ('pig', 'Pig'),
# ('pike', 'Pike'), # ('pike', 'Pike'),
# ('pkgconfig', 'PkgConfig'), # ('pkgconfig', 'PkgConfig'),
# ('plpgsql', 'PL/pgSQL'), # ('plpgsql', 'PL/pgSQL'),
('postgresql', 'PostgreSQL SQL dialect'), ("postgresql", "PostgreSQL SQL dialect"),
# ('postscript', 'PostScript'), # ('postscript', 'PostScript'),
# ('pot', 'Gettext Catalog'), # ('pot', 'Gettext Catalog'),
# ('pov', 'POVRay'), # ('pov', 'POVRay'),
@ -479,7 +479,7 @@ class dpasteAppConfig(AppConfig):
# ('pycon', 'Python console session'), # ('pycon', 'Python console session'),
# ('pypylog', 'PyPy Log'), # ('pypylog', 'PyPy Log'),
# ('pytb', 'Python Traceback'), # ('pytb', 'Python Traceback'),
('python', 'Python'), ("python", "Python"),
# ('python3', 'Python 3'), # ('python3', 'Python 3'),
# ('qbasic', 'QBasic'), # ('qbasic', 'QBasic'),
# ('qml', 'QML'), # ('qml', 'QML'),
@ -494,7 +494,7 @@ class dpasteAppConfig(AppConfig):
# ('ragel-objc', 'Ragel in Objective C Host'), # ('ragel-objc', 'Ragel in Objective C Host'),
# ('ragel-ruby', 'Ragel in Ruby Host'), # ('ragel-ruby', 'Ragel in Ruby Host'),
# ('raw', 'Raw token data'), # ('raw', 'Raw token data'),
('rb', 'Ruby'), ("rb", "Ruby"),
# ('rbcon', 'Ruby irb session'), # ('rbcon', 'Ruby irb session'),
# ('rconsole', 'RConsole'), # ('rconsole', 'RConsole'),
# ('rd', 'Rd'), # ('rd', 'Rd'),
@ -511,17 +511,17 @@ class dpasteAppConfig(AppConfig):
# ('robotframework', 'RobotFramework'), # ('robotframework', 'RobotFramework'),
# ('rql', 'RQL'), # ('rql', 'RQL'),
# ('rsl', 'RSL'), # ('rsl', 'RSL'),
('rst', 'reStructuredText'), ("rst", "reStructuredText"),
# ('rts', 'TrafficScript'), # ('rts', 'TrafficScript'),
('rust', 'Rust'), ("rust", "Rust"),
# ('sas', 'SAS'), # ('sas', 'SAS'),
('sass', 'Sass'), ("sass", "Sass"),
# ('sc', 'SuperCollider'), # ('sc', 'SuperCollider'),
# ('scala', 'Scala'), # ('scala', 'Scala'),
# ('scaml', 'Scaml'), # ('scaml', 'Scaml'),
# ('scheme', 'Scheme'), # ('scheme', 'Scheme'),
# ('scilab', 'Scilab'), # ('scilab', 'Scilab'),
('scss', 'SCSS'), ("scss", "SCSS"),
# ('shen', 'Shen'), # ('shen', 'Shen'),
# ('silver', 'Silver'), # ('silver', 'Silver'),
# ('slim', 'Slim'), # ('slim', 'Slim'),
@ -531,19 +531,19 @@ class dpasteAppConfig(AppConfig):
# ('sml', 'Standard ML'), # ('sml', 'Standard ML'),
# ('snobol', 'Snobol'), # ('snobol', 'Snobol'),
# ('snowball', 'Snowball'), # ('snowball', 'Snowball'),
('sol', 'Solidity'), ("sol", "Solidity"),
# ('sourceslist', 'Debian Sourcelist'), # ('sourceslist', 'Debian Sourcelist'),
# ('sp', 'SourcePawn'), # ('sp', 'SourcePawn'),
# ('sparql', 'SPARQL'), # ('sparql', 'SPARQL'),
# ('spec', 'RPMSpec'), # ('spec', 'RPMSpec'),
# ('splus', 'S'), # ('splus', 'S'),
('sql', 'SQL'), ("sql", "SQL"),
# ('sqlite3', 'sqlite3con'), # ('sqlite3', 'sqlite3con'),
# ('squidconf', 'SquidConf'), # ('squidconf', 'SquidConf'),
# ('ssp', 'Scalate Server Page'), # ('ssp', 'Scalate Server Page'),
# ('stan', 'Stan'), # ('stan', 'Stan'),
# ('stata', 'Stata'), # ('stata', 'Stata'),
('swift', 'Swift'), ("swift", "Swift"),
# ('swig', 'SWIG'), # ('swig', 'SWIG'),
# ('systemverilog', 'systemverilog'), # ('systemverilog', 'systemverilog'),
# ('tads3', 'TADS 3'), # ('tads3', 'TADS 3'),
@ -556,7 +556,7 @@ class dpasteAppConfig(AppConfig):
# ('termcap', 'Termcap'), # ('termcap', 'Termcap'),
# ('terminfo', 'Terminfo'), # ('terminfo', 'Terminfo'),
# ('terraform', 'Terraform'), # ('terraform', 'Terraform'),
('tex', 'TeX'), ("tex", "TeX"),
# ('text', 'Text only'), # ('text', 'Text only'),
# ('thrift', 'Thrift'), # ('thrift', 'Thrift'),
# ('todotxt', 'Todotxt'), # ('todotxt', 'Todotxt'),
@ -566,7 +566,7 @@ class dpasteAppConfig(AppConfig):
# ('tsql', 'Transact-SQL'), # ('tsql', 'Transact-SQL'),
# ('turtle', 'Turtle'), # ('turtle', 'Turtle'),
# ('twig', 'Twig'), # ('twig', 'Twig'),
('typoscript', 'TypoScript'), ("typoscript", "TypoScript"),
# ('typoscriptcssdata', 'TypoScriptCssData'), # ('typoscriptcssdata', 'TypoScriptCssData'),
# ('typoscripthtmldata', 'TypoScriptHtmlData'), # ('typoscripthtmldata', 'TypoScriptHtmlData'),
# ('urbiscript', 'UrbiScript'), # ('urbiscript', 'UrbiScript'),
@ -579,11 +579,11 @@ class dpasteAppConfig(AppConfig):
# ('verilog', 'verilog'), # ('verilog', 'verilog'),
# ('vgl', 'VGL'), # ('vgl', 'VGL'),
# ('vhdl', 'vhdl'), # ('vhdl', 'vhdl'),
('vim', 'VimL'), ("vim", "VimL"),
# ('wdiff', 'WDiff'), # ('wdiff', 'WDiff'),
# ('whiley', 'Whiley'), # ('whiley', 'Whiley'),
# ('x10', 'X10'), # ('x10', 'X10'),
('xml', 'XML'), ("xml", "XML"),
# ('xml+cheetah', 'XML+Cheetah'), # ('xml+cheetah', 'XML+Cheetah'),
# ('xml+django', 'XML+Django/Jinja'), # ('xml+django', 'XML+Django/Jinja'),
# ('xml+erb', 'XML+Ruby'), # ('xml+erb', 'XML+Ruby'),
@ -596,10 +596,10 @@ class dpasteAppConfig(AppConfig):
# ('xml+velocity', 'XML+Velocity'), # ('xml+velocity', 'XML+Velocity'),
# ('xorg.conf', 'Xorg'), # ('xorg.conf', 'Xorg'),
# ('xquery', 'XQuery'), # ('xquery', 'XQuery'),
('xslt', 'XSLT'), ("xslt", "XSLT"),
# ('xtend', 'Xtend'), # ('xtend', 'Xtend'),
# ('xul+mozpreproc', 'XUL+mozpreproc'), # ('xul+mozpreproc', 'XUL+mozpreproc'),
('yaml', 'YAML'), ("yaml", "YAML"),
# ('yaml+jinja', 'YAML+Jinja'), # ('yaml+jinja', 'YAML+Jinja'),
# ('zephir', 'Zephir') # ('zephir', 'Zephir')
] ]
@ -612,13 +612,13 @@ class dpasteAppConfig(AppConfig):
framework is installed, it uses the current Site domain. Otherwise framework is installed, it uses the current Site domain. Otherwise
it falls back to 'https://dpaste.de' it falls back to 'https://dpaste.de'
""" """
if apps.is_installed('django.contrib.sites'): if apps.is_installed("django.contrib.sites"):
from django.contrib.sites.shortcuts import get_current_site from django.contrib.sites.shortcuts import get_current_site
site = get_current_site(request) site = get_current_site(request)
if site: if site:
return 'https://{0}'.format(site.domain) return f"https://{site.domain}"
return 'https://dpaste.de' return "https://dpaste.de"
@property @property
def extra_template_context(self): def extra_template_context(self):
@ -627,6 +627,6 @@ class dpasteAppConfig(AppConfig):
all Template Views. all Template Views.
""" """
return { return {
'dpaste_application_name': self.APPLICATION_NAME, "dpaste_application_name": self.APPLICATION_NAME,
'dpaste_extra_head_html': mark_safe(self.EXTRA_HEAD_HTML), "dpaste_extra_head_html": mark_safe(self.EXTRA_HEAD_HTML),
} }

View file

@ -2,19 +2,19 @@ import datetime
from django import forms from django import forms
from django.apps import apps from django.apps import apps
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from .highlight import LEXER_CHOICES, LEXER_DEFAULT, LEXER_KEYS from .highlight import LEXER_CHOICES, LEXER_DEFAULT, LEXER_KEYS
from .models import Snippet from .models import Snippet
config = apps.get_app_config('dpaste') config = apps.get_app_config("dpaste")
def get_expire_values(expires): def get_expire_values(expires):
if expires == 'never': if expires == "never":
expire_type = Snippet.EXPIRE_KEEP expire_type = Snippet.EXPIRE_KEEP
expires = None expires = None
elif expires == 'onetime': elif expires == "onetime":
expire_type = Snippet.EXPIRE_ONETIME expire_type = Snippet.EXPIRE_ONETIME
expires = None expires = None
else: else:
@ -28,63 +28,63 @@ def get_expire_values(expires):
class SnippetForm(forms.ModelForm): class SnippetForm(forms.ModelForm):
content = forms.CharField( content = forms.CharField(
label=_('Content'), label=_("Content"),
widget=forms.Textarea( widget=forms.Textarea(
attrs={'placeholder': _('Awesome code goes here...')} attrs={"placeholder": _("Awesome code goes here...")}
), ),
max_length=config.MAX_CONTENT_LENGTH, max_length=config.MAX_CONTENT_LENGTH,
strip=False, strip=False,
) )
lexer = forms.ChoiceField( lexer = forms.ChoiceField(
label=_('Lexer'), initial=LEXER_DEFAULT, choices=LEXER_CHOICES label=_("Lexer"), initial=LEXER_DEFAULT, choices=LEXER_CHOICES
) )
expires = forms.ChoiceField( expires = forms.ChoiceField(
label=_('Expires'), label=_("Expires"),
choices=config.EXPIRE_CHOICES, choices=config.EXPIRE_CHOICES,
initial=config.EXPIRE_DEFAULT, initial=config.EXPIRE_DEFAULT,
) )
rtl = forms.BooleanField(label=_('Right to Left'), required=False) rtl = forms.BooleanField(label=_("Right to Left"), required=False)
# Honeypot field # Honeypot field
title = forms.CharField( title = forms.CharField(
label=_('Title'), label=_("Title"),
required=False, required=False,
widget=forms.TextInput(attrs={'autocomplete': 'off'}), widget=forms.TextInput(attrs={"autocomplete": "off"}),
) )
class Meta: class Meta:
model = Snippet model = Snippet
fields = ('content', 'lexer', 'rtl') fields = ("content", "lexer", "rtl")
def __init__(self, request, *args, **kwargs): def __init__(self, request, *args, **kwargs):
super(SnippetForm, self).__init__(*args, **kwargs) super(SnippetForm, self).__init__(*args, **kwargs)
self.request = request self.request = request
# Set the recently used lexer if we have any # Set the recently used lexer if we have any
session_lexer = self.request.session.get('lexer') session_lexer = self.request.session.get("lexer")
if session_lexer and session_lexer in LEXER_KEYS: if session_lexer and session_lexer in LEXER_KEYS:
self.fields['lexer'].initial = session_lexer self.fields["lexer"].initial = session_lexer
# if the lexer is given via GET, set it # if the lexer is given via GET, set it
if 'l' in request.GET and request.GET['l'] in LEXER_KEYS: if "l" in request.GET and request.GET["l"] in LEXER_KEYS:
self.fields['lexer'].initial = request.GET['l'] self.fields["lexer"].initial = request.GET["l"]
def clean_content(self): def clean_content(self):
content = self.cleaned_data.get('content', '') content = self.cleaned_data.get("content", "")
if not content.strip(): if not content.strip():
raise forms.ValidationError(_('This field is required.')) raise forms.ValidationError(_("This field is required."))
return content return content
def clean_expires(self): def clean_expires(self):
""" """
Extract the 'expire_type' from the choice of expire choices. Extract the 'expire_type' from the choice of expire choices.
""" """
expires = self.cleaned_data['expires'] expires = self.cleaned_data["expires"]
expires, expire_type = get_expire_values(expires) expires, expire_type = get_expire_values(expires)
self.cleaned_data['expire_type'] = expire_type self.cleaned_data["expire_type"] = expire_type
return expires return expires
def clean(self): def clean(self):
@ -92,8 +92,8 @@ class SnippetForm(forms.ModelForm):
The `title` field is a hidden honeypot field. If its filled, The `title` field is a hidden honeypot field. If its filled,
this is likely spam. this is likely spam.
""" """
if self.cleaned_data.get('title'): if self.cleaned_data.get("title"):
raise forms.ValidationError('This snippet was identified as Spam.') raise forms.ValidationError("This snippet was identified as Spam.")
return self.cleaned_data return self.cleaned_data
def save(self, parent=None, *args, **kwargs): def save(self, parent=None, *args, **kwargs):
@ -102,17 +102,17 @@ class SnippetForm(forms.ModelForm):
# Add expire timestamp. None indicates 'keep forever', use the default # Add expire timestamp. None indicates 'keep forever', use the default
# null state of the db column for that. # null state of the db column for that.
self.instance.expires = self.cleaned_data['expires'] self.instance.expires = self.cleaned_data["expires"]
self.instance.expire_type = self.cleaned_data['expire_type'] self.instance.expire_type = self.cleaned_data["expire_type"]
# Save snippet in the db # Save snippet in the db
super(SnippetForm, self).save(*args, **kwargs) super(SnippetForm, self).save(*args, **kwargs)
# Add the snippet to the user session list # Add the snippet to the user session list
self.request.session.setdefault('snippet_list', []) self.request.session.setdefault("snippet_list", [])
self.request.session['snippet_list'] += [self.instance.pk] self.request.session["snippet_list"] += [self.instance.pk]
# Save the lexer in the session so we can use it later again # Save the lexer in the session so we can use it later again
self.request.session['lexer'] = self.cleaned_data['lexer'] self.request.session["lexer"] = self.cleaned_data["lexer"]
return self.instance return self.instance

View file

@ -4,7 +4,7 @@ from django.apps import apps
from django.template.defaultfilters import escape, linebreaksbr from django.template.defaultfilters import escape, linebreaksbr
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from pygments import highlight from pygments import highlight
from pygments.formatters.html import HtmlFormatter from pygments.formatters.html import HtmlFormatter
from pygments.lexers import get_lexer_by_name from pygments.lexers import get_lexer_by_name
@ -12,7 +12,7 @@ from pygments.lexers.python import PythonLexer
from pygments.util import ClassNotFound from pygments.util import ClassNotFound
logger = getLogger(__file__) logger = getLogger(__file__)
config = apps.get_app_config('dpaste') config = apps.get_app_config("dpaste")
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
@ -21,14 +21,14 @@ config = apps.get_app_config('dpaste')
class Highlighter(object): class Highlighter(object):
template_name = 'dpaste/highlight/code.html' template_name = "dpaste/highlight/code.html"
def highlight(self, code_string, lexer_name=None): def highlight(self, code_string, lexer_name=None):
"""Subclasses need to override this.""" """Subclasses need to override this."""
return code_string return code_string
@staticmethod @staticmethod
def get_lexer_display_name(lexer_name, fallback=_('(Deprecated Lexer)')): def get_lexer_display_name(lexer_name, fallback=_("(Deprecated Lexer)")):
for l in config.TEXT_FORMATTER + config.CODE_FORMATTER: for l in config.TEXT_FORMATTER + config.CODE_FORMATTER:
if l[0] == lexer_name: if l[0] == lexer_name:
return l[1] return l[1]
@ -37,11 +37,11 @@ class Highlighter(object):
def render(self, code_string, lexer_name, direction=None, **kwargs): def render(self, code_string, lexer_name, direction=None, **kwargs):
highlighted_string = self.highlight(code_string, lexer_name=lexer_name) highlighted_string = self.highlight(code_string, lexer_name=lexer_name)
context = { context = {
'highlighted': highlighted_string, "highlighted": highlighted_string,
'highlighted_splitted': highlighted_string.splitlines(), "highlighted_splitted": highlighted_string.splitlines(),
'lexer_name': lexer_name, "lexer_name": lexer_name,
'lexer_display_name': self.get_lexer_display_name(lexer_name), "lexer_display_name": self.get_lexer_display_name(lexer_name),
'direction': direction, "direction": direction,
} }
context.update(kwargs) context.update(kwargs)
return render_to_string(self.template_name, context) return render_to_string(self.template_name, context)
@ -50,7 +50,7 @@ class Highlighter(object):
class PlainTextHighlighter(Highlighter): class PlainTextHighlighter(Highlighter):
"""Plain Text. Just replace linebreaks.""" """Plain Text. Just replace linebreaks."""
template_name = 'dpaste/highlight/text.html' template_name = "dpaste/highlight/text.html"
def highlight(self, code_string, **kwargs): def highlight(self, code_string, **kwargs):
return linebreaksbr(code_string) return linebreaksbr(code_string)
@ -60,17 +60,17 @@ class MarkdownHighlighter(PlainTextHighlighter):
"""Markdown""" """Markdown"""
extensions = ( extensions = (
'tables', "tables",
'fenced-code', "fenced-code",
'footnotes', "footnotes",
'autolink,', "autolink,",
'strikethrough', "strikethrough",
'underline', "underline",
'quote', "quote",
'superscript', "superscript",
'math', "math",
) )
render_flags = ('skip-html',) render_flags = ("skip-html",)
def highlight(self, code_string, **kwargs): def highlight(self, code_string, **kwargs):
import misaka import misaka
@ -87,22 +87,22 @@ class MarkdownHighlighter(PlainTextHighlighter):
class RestructuredTextHighlighter(PlainTextHighlighter): class RestructuredTextHighlighter(PlainTextHighlighter):
"""Restructured Text""" """Restructured Text"""
rst_part_name = 'html_body' rst_part_name = "html_body"
publish_args = { publish_args = {
'writer_name': 'html5_polyglot', "writer_name": "html5_polyglot",
'settings_overrides': { "settings_overrides": {
'raw_enabled': False, "raw_enabled": False,
'file_insertion_enabled': False, "file_insertion_enabled": False,
'halt_level': 5, "halt_level": 5,
'report_level': 2, "report_level": 2,
'warning_stream': '/dev/null', "warning_stream": "/dev/null",
}, },
} }
def highlight(self, code_string, **kwargs): def highlight(self, code_string, **kwargs):
from docutils.core import publish_parts from docutils.core import publish_parts
self.publish_args['source'] = code_string self.publish_args["source"] = code_string
parts = publish_parts(**self.publish_args) parts = publish_parts(**self.publish_args)
return mark_safe(parts[self.rst_part_name]) return mark_safe(parts[self.rst_part_name])
@ -126,9 +126,9 @@ class PlainCodeHighlighter(Highlighter):
""" """
def highlight(self, code_string, **kwargs): def highlight(self, code_string, **kwargs):
return '\n'.join( return "\n".join(
[ [
'<span class="plain">{}</span>'.format(escape(l) or '&#8203;') '<span class="plain">{}</span>'.format(escape(l) or "&#8203;")
for l in code_string.splitlines() for l in code_string.splitlines()
] ]
) )
@ -149,7 +149,7 @@ class PygmentsHighlighter(Highlighter):
try: try:
self.lexer = get_lexer_by_name(lexer_name) self.lexer = get_lexer_by_name(lexer_name)
except ClassNotFound: except ClassNotFound:
logger.warning('Lexer for given name %s not found', lexer_name) logger.warning("Lexer for given name %s not found", lexer_name)
self.lexer = self.lexer_fallback self.lexer = self.lexer_fallback
return highlight(code_string, self.lexer, self.formatter) return highlight(code_string, self.lexer, self.formatter)
@ -190,8 +190,8 @@ def get_highlighter_class(lexer_name):
# Generate a list of Form choices of all lexer. # Generate a list of Form choices of all lexer.
LEXER_CHOICES = ( LEXER_CHOICES = (
(_('Text'), [i[:2] for i in config.TEXT_FORMATTER]), (_("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 # List of all Lexer Keys

View file

@ -9,10 +9,10 @@ class Command(BaseCommand):
def add_arguments(self, parser): def add_arguments(self, parser):
parser.add_argument( parser.add_argument(
'--dry-run', "--dry-run",
action='store_true', action="store_true",
dest='dry_run', dest="dry_run",
help='Don\'t do anything.', help="Don't do anything.",
), ),
def handle(self, *args, **options): def handle(self, *args, **options):
@ -29,7 +29,7 @@ class Command(BaseCommand):
) )
for d in deleteable_snippets: for d in deleteable_snippets:
self.stdout.write(u"- %s (%s)\n" % (d.secret_id, d.expires)) self.stdout.write(u"- %s (%s)\n" % (d.secret_id, d.expires))
if options.get('dry_run'): if options.get("dry_run"):
self.stdout.write('Dry run - Not actually deleting snippets!\n') self.stdout.write("Dry run - Not actually deleting snippets!\n")
else: else:
deleteable_snippets.delete() deleteable_snippets.delete()

View file

@ -4,12 +4,12 @@ from random import SystemRandom
from django.apps import apps from django.apps import apps
from django.db import models from django.db import models
from django.urls import reverse from django.urls import reverse
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from six import python_2_unicode_compatible from six import python_2_unicode_compatible
from dpaste import highlight from dpaste import highlight
config = apps.get_app_config('dpaste') config = apps.get_app_config("dpaste")
logger = getLogger(__file__) logger = getLogger(__file__)
R = SystemRandom() R = SystemRandom()
@ -17,11 +17,11 @@ R = SystemRandom()
def generate_secret_id(length): def generate_secret_id(length):
if length > config.SLUG_LENGTH: if length > config.SLUG_LENGTH:
logger.warning( logger.warning(
'Slug creation triggered a duplicate, ' "Slug creation triggered a duplicate, "
'consider increasing the SLUG_LENGTH.' "consider increasing the SLUG_LENGTH."
) )
secret_id = ''.join( secret_id = "".join(
[ [
R.choice(config.SLUG_CHOICES) R.choice(config.SLUG_CHOICES)
for i in range(length or config.SLUG_LENGTH) for i in range(length or config.SLUG_LENGTH)
@ -45,37 +45,37 @@ class Snippet(models.Model):
EXPIRE_KEEP = 2 EXPIRE_KEEP = 2
EXPIRE_ONETIME = 3 EXPIRE_ONETIME = 3
EXPIRE_CHOICES = ( EXPIRE_CHOICES = (
(EXPIRE_TIME, _('Expire by timestamp')), (EXPIRE_TIME, _("Expire by timestamp")),
(EXPIRE_KEEP, _('Keep Forever')), (EXPIRE_KEEP, _("Keep Forever")),
(EXPIRE_ONETIME, _('One-Time snippet')), (EXPIRE_ONETIME, _("One-Time snippet")),
) )
secret_id = models.CharField( 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')) content = models.TextField(_("Content"))
lexer = models.CharField( lexer = models.CharField(
_('Lexer'), max_length=30, default=highlight.LEXER_DEFAULT _("Lexer"), max_length=30, default=highlight.LEXER_DEFAULT
) )
published = models.DateTimeField(_('Published'), auto_now_add=True) published = models.DateTimeField(_("Published"), auto_now_add=True)
expire_type = models.PositiveSmallIntegerField( expire_type = models.PositiveSmallIntegerField(
_('Expire Type'), choices=EXPIRE_CHOICES, default=EXPIRE_CHOICES[0][0] _("Expire Type"), choices=EXPIRE_CHOICES, default=EXPIRE_CHOICES[0][0]
) )
expires = models.DateTimeField(_('Expires'), blank=True, null=True) expires = models.DateTimeField(_("Expires"), blank=True, null=True)
view_count = models.PositiveIntegerField(_('View count'), default=0) view_count = models.PositiveIntegerField(_("View count"), default=0)
rtl = models.BooleanField(_('Right-to-left'), default=False) rtl = models.BooleanField(_("Right-to-left"), default=False)
parent = models.ForeignKey( parent = models.ForeignKey(
'self', "self",
null=True, null=True,
blank=True, blank=True,
verbose_name=_('Parent Snippet'), verbose_name=_("Parent Snippet"),
related_name='children', related_name="children",
on_delete=models.CASCADE, on_delete=models.CASCADE,
) )
class Meta: class Meta:
ordering = ('-published',) ordering = ("-published",)
db_table = 'dpaste_snippet' db_table = "dpaste_snippet"
def __str__(self): def __str__(self):
return self.secret_id return self.secret_id
@ -86,14 +86,14 @@ class Snippet(models.Model):
super(Snippet, self).save(*args, **kwargs) super(Snippet, self).save(*args, **kwargs)
def get_absolute_url(self): def get_absolute_url(self):
return reverse('snippet_details', kwargs={'snippet_id': self.secret_id}) return reverse("snippet_details", kwargs={"snippet_id": self.secret_id})
def highlight(self): def highlight(self):
HighlighterClass = highlight.get_highlighter_class(self.lexer) HighlighterClass = highlight.get_highlighter_class(self.lexer)
return HighlighterClass().render( return HighlighterClass().render(
code_string=self.content, code_string=self.content,
lexer_name=self.lexer, lexer_name=self.lexer,
direction='rtl' if self.rtl else 'ltr', direction="rtl" if self.rtl else "ltr",
) )
@property @property

View file

@ -14,17 +14,17 @@ PROJECT_DIR, PROJECT_MODULE_NAME = os.path.split(
) )
PYTHON_BIN = os.path.dirname(sys.executable) PYTHON_BIN = os.path.dirname(sys.executable)
if os.path.exists(os.path.join(PYTHON_BIN, 'activate_this.py')): if os.path.exists(os.path.join(PYTHON_BIN, "activate_this.py")):
# Assume that the presence of 'activate_this.py' in the python bin/ # Assume that the presence of 'activate_this.py' in the python bin/
# directory means that we're running in a virtual environment. Set the # directory means that we're running in a virtual environment. Set the
# variable root to $VIRTUALENV/var. # variable root to $VIRTUALENV/var.
VAR_ROOT = os.path.join(os.path.dirname(PYTHON_BIN), 'var') VAR_ROOT = os.path.join(os.path.dirname(PYTHON_BIN), "var")
if not os.path.exists(VAR_ROOT): if not os.path.exists(VAR_ROOT):
os.mkdir(VAR_ROOT) os.mkdir(VAR_ROOT)
else: else:
# Set the variable root to the local configuration location (which is # Set the variable root to the local configuration location (which is
# ignored by the repository). # ignored by the repository).
VAR_ROOT = os.path.join(PROJECT_DIR, PROJECT_MODULE_NAME, 'conf', 'local') VAR_ROOT = os.path.join(PROJECT_DIR, PROJECT_MODULE_NAME, "conf", "local")
# ============================================================================== # ==============================================================================
# Generic Django project settings # Generic Django project settings
@ -34,13 +34,13 @@ DEBUG = False
# Local time zone for this installation. Choices can be found here: # Local time zone for this installation. Choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
TIME_ZONE = 'UTC' TIME_ZONE = "UTC"
SITE_ID = 1 SITE_ID = 1
# Make this unique, and don't share it with anybody. # Make this unique, and don't share it with anybody.
SECRET_KEY = '' SECRET_KEY = ""
ALLOWED_HOSTS = ['*'] ALLOWED_HOSTS = ["*"]
# ============================================================================== # ==============================================================================
# I18N # I18N
@ -49,8 +49,8 @@ ALLOWED_HOSTS = ['*']
USE_I18N = True USE_I18N = True
USE_L10N = False USE_L10N = False
LANGUAGE_CODE = 'en' LANGUAGE_CODE = "en"
LANGUAGES = (('en', 'English'),) LANGUAGES = (("en", "English"),)
# LOCALE_PATHS = ( # LOCALE_PATHS = (
# os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'locale')), # os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'locale')),
@ -61,59 +61,59 @@ LANGUAGES = (('en', 'English'),)
# ============================================================================== # ==============================================================================
STATICFILES_STORAGE = ( STATICFILES_STORAGE = (
'django.contrib.staticfiles.storage.ManifestStaticFilesStorage' "django.contrib.staticfiles.storage.ManifestStaticFilesStorage"
) )
STATICFILES_FINDERS = ( STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder', "django.contrib.staticfiles.finders.FileSystemFinder",
'django.contrib.staticfiles.finders.AppDirectoriesFinder', "django.contrib.staticfiles.finders.AppDirectoriesFinder",
) )
STATIC_ROOT = os.path.join(VAR_ROOT, 'static') STATIC_ROOT = os.path.join(VAR_ROOT, "static")
STATIC_URL = '/static/' STATIC_URL = "/static/"
ADMIN_MEDIA_PREFIX = '/static/admin/' ADMIN_MEDIA_PREFIX = "/static/admin/"
ROOT_URLCONF = 'dpaste.urls' ROOT_URLCONF = "dpaste.urls"
LOGIN_URL = '/accounts/login/' LOGIN_URL = "/accounts/login/"
LOGOUT_URL = '/accounts/logout/' LOGOUT_URL = "/accounts/logout/"
LOGIN_REDIRECT_URL = '/' LOGIN_REDIRECT_URL = "/"
# ============================================================================== # ==============================================================================
# Templates # Templates
# ============================================================================== # ==============================================================================
MIDDLEWARE = [ MIDDLEWARE = [
'django.middleware.csrf.CsrfViewMiddleware', "django.middleware.csrf.CsrfViewMiddleware",
'django.contrib.sessions.middleware.SessionMiddleware', "django.contrib.sessions.middleware.SessionMiddleware",
'django.middleware.common.CommonMiddleware', "django.middleware.common.CommonMiddleware",
'django.middleware.locale.LocaleMiddleware', "django.middleware.locale.LocaleMiddleware",
'django.middleware.clickjacking.XFrameOptionsMiddleware', "django.middleware.clickjacking.XFrameOptionsMiddleware",
'django.middleware.security.SecurityMiddleware', "django.middleware.security.SecurityMiddleware",
'csp.middleware.CSPMiddleware', "csp.middleware.CSPMiddleware",
] ]
TEMPLATES = [ TEMPLATES = [
{ {
'BACKEND': 'django.template.backends.django.DjangoTemplates', "BACKEND": "django.template.backends.django.DjangoTemplates",
'DIRS': [], "DIRS": [],
'APP_DIRS': True, "APP_DIRS": True,
'OPTIONS': { "OPTIONS": {
'context_processors': [ "context_processors": [
'django.template.context_processors.debug', "django.template.context_processors.debug",
'django.template.context_processors.request', "django.template.context_processors.request",
'django.template.context_processors.i18n', "django.template.context_processors.i18n",
] ]
}, },
} }
] ]
INSTALLED_APPS = [ INSTALLED_APPS = [
'django.contrib.staticfiles', "django.contrib.staticfiles",
'django.contrib.sessions', "django.contrib.sessions",
'staticinline.apps.StaticInlineAppConfig', "staticinline.apps.StaticInlineAppConfig",
'dpaste.apps.dpasteAppConfig', "dpaste.apps.dpasteAppConfig",
] ]
# DATABASES = { # DATABASES = {
@ -132,7 +132,7 @@ INSTALLED_APPS = [
SESSION_COOKIE_SECURE = True SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True CSRF_COOKIE_SECURE = True
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
SECURE_BROWSER_XSS_FILTER = True SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True SECURE_CONTENT_TYPE_NOSNIFF = True
@ -141,23 +141,23 @@ CSP_SCRIPT_SRC = ("'self'", "'unsafe-inline'")
CSP_STYLE_SRC = ("'self'", "'unsafe-inline'") CSP_STYLE_SRC = ("'self'", "'unsafe-inline'")
LOGGING = { LOGGING = {
'version': 1, "version": 1,
'disable_existing_loggers': False, "disable_existing_loggers": False,
'filters': { "filters": {
'require_debug_false': {'()': 'django.utils.log.RequireDebugFalse'} "require_debug_false": {"()": "django.utils.log.RequireDebugFalse"}
}, },
'handlers': { "handlers": {
'mail_admins': { "mail_admins": {
'level': 'ERROR', "level": "ERROR",
'filters': ['require_debug_false'], "filters": ["require_debug_false"],
'class': 'django.utils.log.AdminEmailHandler', "class": "django.utils.log.AdminEmailHandler",
} }
}, },
'loggers': { "loggers": {
'django.request': { "django.request": {
'handlers': ['mail_admins'], "handlers": ["mail_admins"],
'level': 'ERROR', "level": "ERROR",
'propagate': True, "propagate": True,
} }
}, },
} }

View file

@ -26,7 +26,7 @@ if not 'runsslserver' in sys.argv:
# #
# from dpaste.apps import dpasteAppConfig # from dpaste.apps import dpasteAppConfig
# from django.utils.translation import ugettext_lazy as _ # from django.utils.translation import gettext_lazy as _
# #
# class ProductionDpasteAppConfig(dpasteAppConfig): # class ProductionDpasteAppConfig(dpasteAppConfig):
# SLUG_LENGTH = 8 # SLUG_LENGTH = 8

View file

@ -5,13 +5,13 @@ import django
from .base import * from .base import *
SECRET_KEY = 'test-key' SECRET_KEY = "test-key"
DATABASES = { DATABASES = {
'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': ':memory:'} "default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}
} }
# Drop CSP middleware for Django 3.0 until it was fixed upstream # Drop CSP middleware for Django 3.0 until it was fixed upstream
# https://github.com/mozilla/django-csp/issues/129 # https://github.com/mozilla/django-csp/issues/129
if django.get_version().startswith('3.'): if django.get_version().startswith("3."):
MIDDLEWARE.remove('csp.middleware.CSPMiddleware') MIDDLEWARE.remove("csp.middleware.CSPMiddleware")

View file

@ -7,12 +7,12 @@ from django.urls import reverse
from ..models import Snippet from ..models import Snippet
config = apps.get_app_config('dpaste') config = apps.get_app_config("dpaste")
class SnippetAPITestCase(TestCase): class SnippetAPITestCase(TestCase):
def setUp(self): def setUp(self):
self.api_url = reverse('dpaste_api_create_snippet') self.api_url = reverse("dpaste_api_create_snippet")
self.client = Client(enforce_csrf_checks=True) self.client = Client(enforce_csrf_checks=True)
def test_empty(self): def test_empty(self):
@ -27,19 +27,19 @@ class SnippetAPITestCase(TestCase):
self.assertEqual(Snippet.objects.count(), 0) self.assertEqual(Snippet.objects.count(), 0)
# No content # No content
data['content'] = '' data["content"] = ""
response = self.client.post(self.api_url, data) response = self.client.post(self.api_url, data)
self.assertEqual(response.status_code, 400) self.assertEqual(response.status_code, 400)
self.assertEqual(Snippet.objects.count(), 0) self.assertEqual(Snippet.objects.count(), 0)
# Just some spaces # Just some spaces
data['content'] = ' ' data["content"] = " "
response = self.client.post(self.api_url, data) response = self.client.post(self.api_url, data)
self.assertEqual(response.status_code, 400) self.assertEqual(response.status_code, 400)
self.assertEqual(Snippet.objects.count(), 0) self.assertEqual(Snippet.objects.count(), 0)
# Linebreaks or tabs only are not valid either # Linebreaks or tabs only are not valid either
data['content'] = '\n\t ' data["content"] = "\n\t "
response = self.client.post(self.api_url, data) response = self.client.post(self.api_url, data)
self.assertEqual(response.status_code, 400) self.assertEqual(response.status_code, 400)
self.assertEqual(Snippet.objects.count(), 0) self.assertEqual(Snippet.objects.count(), 0)
@ -48,10 +48,10 @@ class SnippetAPITestCase(TestCase):
""" """
A valid snippet, contains Unicode, tabs, spaces, linebreaks etc. A valid snippet, contains Unicode, tabs, spaces, linebreaks etc.
""" """
data = {'content': u"Hello Wörld.\n\tGood Bye"} data = {"content": u"Hello Wörld.\n\tGood Bye"}
response = self.client.post(self.api_url, data) response = self.client.post(self.api_url, data)
content = response.content.decode('utf-8') content = response.content.decode("utf-8")
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(Snippet.objects.count(), 1) self.assertEqual(Snippet.objects.count(), 1)
@ -65,36 +65,36 @@ class SnippetAPITestCase(TestCase):
response = self.client.get(content[1:-1]) response = self.client.get(content[1:-1])
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertContains(response, data['content']) self.assertContains(response, data["content"])
def test_new_url_format(self): def test_new_url_format(self):
""" """
The 'new' url format is just the link with a linebreak. The 'new' url format is just the link with a linebreak.
""" """
data = {'content': u"Hello Wörld.\n\tGood Bye", 'format': 'url'} data = {"content": u"Hello Wörld.\n\tGood Bye", "format": "url"}
response = self.client.post(self.api_url, data) response = self.client.post(self.api_url, data)
content = response.content.decode('utf-8') content = response.content.decode("utf-8")
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(Snippet.objects.count(), 1) self.assertEqual(Snippet.objects.count(), 1)
# Response is just the link starting with http(s) and ends with a linebreak # Response is just the link starting with http(s) and ends with a linebreak
self.assertTrue(content.startswith('http')) self.assertTrue(content.startswith("http"))
self.assertTrue(content.endswith('\n')) self.assertTrue(content.endswith("\n"))
def test_json_format(self): def test_json_format(self):
""" """
The 'new' url format is just the link with a linebreak. The 'new' url format is just the link with a linebreak.
""" """
data = { data = {
'content': u"Hello Wörld.\n\tGood Bye", "content": u"Hello Wörld.\n\tGood Bye",
'format': 'json', "format": "json",
'lexer': 'haskell', "lexer": "haskell",
} }
response = self.client.post(self.api_url, data) response = self.client.post(self.api_url, data)
content = response.content.decode('utf-8') content = response.content.decode("utf-8")
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(Snippet.objects.count(), 1) self.assertEqual(Snippet.objects.count(), 1)
@ -104,9 +104,9 @@ class SnippetAPITestCase(TestCase):
json_data = loads(content) json_data = loads(content)
# Response is valid json, containing, content, lexer and url # Response is valid json, containing, content, lexer and url
self.assertEqual(json_data['content'], data['content']) self.assertEqual(json_data["content"], data["content"])
self.assertEqual(json_data['lexer'], data['lexer']) self.assertEqual(json_data["lexer"], data["lexer"])
self.assertTrue(json_data['url'].startswith('http')) self.assertTrue(json_data["url"].startswith("http"))
def test_invalid_format(self): def test_invalid_format(self):
""" """
@ -115,9 +115,9 @@ class SnippetAPITestCase(TestCase):
""" """
data = { data = {
'content': u"Hello Wörld.\n\tGood Bye", "content": u"Hello Wörld.\n\tGood Bye",
'format': 'broken-format', "format": "broken-format",
'lexer': 'haskell', "lexer": "haskell",
} }
response = self.client.post(self.api_url, data) response = self.client.post(self.api_url, data)
@ -128,7 +128,7 @@ class SnippetAPITestCase(TestCase):
""" """
A broken lexer will fail loudly. 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) response = self.client.post(self.api_url, data)
self.assertEqual(response.status_code, 400) self.assertEqual(response.status_code, 400)
self.assertEqual(Snippet.objects.count(), 0) self.assertEqual(Snippet.objects.count(), 0)
@ -136,7 +136,7 @@ class SnippetAPITestCase(TestCase):
def test_expire_choices_none_given(self): def test_expire_choices_none_given(self):
# No expire choice given will set a default expiration of one month # No expire choice given will set a default expiration of one month
response = self.client.post( response = self.client.post(
self.api_url, {'content': u"Hello Wörld.\n\tGood Bye"} self.api_url, {"content": u"Hello Wörld.\n\tGood Bye"}
) )
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(Snippet.objects.count(), 1) self.assertEqual(Snippet.objects.count(), 1)
@ -146,7 +146,7 @@ class SnippetAPITestCase(TestCase):
# A expire choice that does not exist returns a BadRequest # A expire choice that does not exist returns a BadRequest
response = self.client.post( response = self.client.post(
self.api_url, self.api_url,
{'content': u"Hello Wörld.\n\tGood Bye", 'expires': 'foobar'}, {"content": u"Hello Wörld.\n\tGood Bye", "expires": "foobar"},
) )
self.assertEqual(response.status_code, 400) self.assertEqual(response.status_code, 400)
self.assertEqual(Snippet.objects.count(), 0) self.assertEqual(Snippet.objects.count(), 0)
@ -159,7 +159,7 @@ class SnippetAPITestCase(TestCase):
def test_valid_expiration_choices_onetime(self): def test_valid_expiration_choices_onetime(self):
response = self.client.post( response = self.client.post(
self.api_url, self.api_url,
{'content': u"Hello Wörld.\n\tGood Bye", 'expires': 'onetime'}, {"content": u"Hello Wörld.\n\tGood Bye", "expires": "onetime"},
) )
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(Snippet.objects.count(), 1) self.assertEqual(Snippet.objects.count(), 1)
@ -170,7 +170,7 @@ class SnippetAPITestCase(TestCase):
def test_valid_expiration_choices_never(self): def test_valid_expiration_choices_never(self):
response = self.client.post( response = self.client.post(
self.api_url, self.api_url,
{'content': u"Hello Wörld.\n\tGood Bye", 'expires': 'never'}, {"content": u"Hello Wörld.\n\tGood Bye", "expires": "never"},
) )
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(Snippet.objects.count(), 1) self.assertEqual(Snippet.objects.count(), 1)
@ -181,7 +181,7 @@ class SnippetAPITestCase(TestCase):
def test_valid_expiration_choices_hour(self): def test_valid_expiration_choices_hour(self):
response = self.client.post( response = self.client.post(
self.api_url, self.api_url,
{'content': u"Hello Wörld.\n\tGood Bye", 'expires': 3600}, {"content": u"Hello Wörld.\n\tGood Bye", "expires": 3600},
) )
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(Snippet.objects.count(), 1) self.assertEqual(Snippet.objects.count(), 1)
@ -190,7 +190,7 @@ class SnippetAPITestCase(TestCase):
def test_valid_expiration_choices_week(self): def test_valid_expiration_choices_week(self):
response = self.client.post( response = self.client.post(
self.api_url, self.api_url,
{'content': u"Hello Wörld.\n\tGood Bye", 'expires': 3600 * 24 * 7}, {"content": u"Hello Wörld.\n\tGood Bye", "expires": 3600 * 24 * 7},
) )
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(Snippet.objects.count(), 1) self.assertEqual(Snippet.objects.count(), 1)
@ -199,7 +199,7 @@ class SnippetAPITestCase(TestCase):
def test_valid_expiration_choices_month(self): def test_valid_expiration_choices_month(self):
response = self.client.post( response = self.client.post(
self.api_url, self.api_url,
{'content': u"Hello Wörld.\n\tGood Bye", 'expires': 3600 * 24 * 30}, {"content": u"Hello Wörld.\n\tGood Bye", "expires": 3600 * 24 * 30},
) )
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(Snippet.objects.count(), 1) self.assertEqual(Snippet.objects.count(), 1)
@ -212,9 +212,9 @@ class SnippetAPITestCase(TestCase):
response = self.client.post( response = self.client.post(
self.api_url, self.api_url,
{ {
'content': u"Hello Wörld.\n\tGood Bye", "content": u"Hello Wörld.\n\tGood Bye",
'lexer': '', "lexer": "",
'filename': '', "filename": "",
}, },
) )
self.assertEqual(response.status_code, 400) self.assertEqual(response.status_code, 400)
@ -226,14 +226,14 @@ class SnippetAPITestCase(TestCase):
response = self.client.post( response = self.client.post(
self.api_url, self.api_url,
{ {
'content': u"Hello Wörld.\n\tGood Bye", "content": u"Hello Wörld.\n\tGood Bye",
'lexer': '', "lexer": "",
'filename': 'helloworld.py', "filename": "helloworld.py",
}, },
) )
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(Snippet.objects.count(), 1) self.assertEqual(Snippet.objects.count(), 1)
self.assertEqual(Snippet.objects.all()[0].lexer, 'python') self.assertEqual(Snippet.objects.all()[0].lexer, "python")
def test_awkward_filename_given(self): def test_awkward_filename_given(self):
""" """
@ -242,9 +242,9 @@ class SnippetAPITestCase(TestCase):
response = self.client.post( response = self.client.post(
self.api_url, self.api_url,
{ {
'content': u"Hello Wörld.\n\tGood Bye", "content": u"Hello Wörld.\n\tGood Bye",
'lexer': '', "lexer": "",
'filename': 'helloworld.helloworld', "filename": "helloworld.helloworld",
}, },
) )
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
@ -260,19 +260,19 @@ class SnippetAPITestCase(TestCase):
response = self.client.post( response = self.client.post(
self.api_url, self.api_url,
{ {
'content': u"Hello Wörld.\n\tGood Bye", "content": u"Hello Wörld.\n\tGood Bye",
'lexer': 'php', "lexer": "php",
'filename': 'helloworld.py', "filename": "helloworld.py",
}, },
) )
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(Snippet.objects.count(), 1) self.assertEqual(Snippet.objects.count(), 1)
self.assertEqual(Snippet.objects.all()[0].lexer, 'php') self.assertEqual(Snippet.objects.all()[0].lexer, "php")
def test_leading_white_is_retained(self): def test_leading_white_is_retained(self):
""" """
Leading Whitespace is retained in the db. Leading Whitespace is retained in the db.
""" """
content = ' one\n two\n three\n four' content = " one\n two\n three\n four"
self.client.post(self.api_url, {'content': content}) self.client.post(self.api_url, {"content": content})
self.assertEqual(Snippet.objects.all()[0].content, content) self.assertEqual(Snippet.objects.all()[0].content, content)

View file

@ -16,7 +16,7 @@ class HighlightAPITestCase(TestCase):
""" """
PLAIN_CODE is not run through Pygments, test it separately. PLAIN_CODE is not run through Pygments, test it separately.
""" """
input = 'vär' input = "vär"
expected = '<span class="plain">vär</span>' expected = '<span class="plain">vär</span>'
value = PlainCodeHighlighter().highlight(input) value = PlainCodeHighlighter().highlight(input)
self.assertEqual(value, expected) self.assertEqual(value, expected)
@ -25,7 +25,7 @@ class HighlightAPITestCase(TestCase):
""" """
Whitespace on the first line is retained. Whitespace on the first line is retained.
""" """
input = ' vär=1' input = " vär=1"
expected = '<span class="plain"> vär=1</span>' expected = '<span class="plain"> vär=1</span>'
value = PlainCodeHighlighter().highlight(input) value = PlainCodeHighlighter().highlight(input)
self.assertEqual(value, expected) self.assertEqual(value, expected)
@ -34,7 +34,7 @@ class HighlightAPITestCase(TestCase):
""" """
Whitespace on the first line is retained, also on subsequent lines. 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 = ( expected = (
'<span class="plain"> vär=1</span>\n' '<span class="plain"> vär=1</span>\n'
'<span class="plain"> vär=2</span>\n' '<span class="plain"> vär=2</span>\n'
@ -49,32 +49,32 @@ class HighlightAPITestCase(TestCase):
Pygemnts highlights the variable name, and also generally adds Pygemnts highlights the variable name, and also generally adds
a trailing \n to all its result. a trailing \n to all its result.
""" """
input = 'var' input = "var"
expected = '<span class="n">var</span>\n' expected = '<span class="n">var</span>\n'
value = PygmentsHighlighter().highlight(input, 'python') value = PygmentsHighlighter().highlight(input, "python")
self.assertEqual(value, expected) self.assertEqual(value, expected)
def test_pygments_leading_whitespace(self): def test_pygments_leading_whitespace(self):
""" """
Whitespace on the first line is retained. Whitespace on the first line is retained.
""" """
input = ' var' input = " var"
expected = ' <span class="n">var</span>\n' expected = ' <span class="n">var</span>\n'
value = PygmentsHighlighter().highlight(input, 'python') value = PygmentsHighlighter().highlight(input, "python")
self.assertEqual(value, expected) self.assertEqual(value, expected)
def test_pygments_leading_whitespace_multiline(self): def test_pygments_leading_whitespace_multiline(self):
""" """
Whitespace on the first line is retained, also on subsequent lines. 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 = ( 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' ' <span class="n">var</span>\n'
' <span class="n">var</span>\n' ' <span class="n">var</span>\n'
) )
value = PygmentsHighlighter().highlight(input, 'python') value = PygmentsHighlighter().highlight(input, "python")
self.assertEqual(value, expected) self.assertEqual(value, expected)
def test_broken_rst_syntax(self): def test_broken_rst_syntax(self):
@ -93,4 +93,4 @@ class HighlightAPITestCase(TestCase):
try: try:
RestructuredTextHighlighter().highlight(input) RestructuredTextHighlighter().highlight(input)
except Exception as e: except Exception as e:
self.fail('rst syntax raised unexpected exception: {}'.format(e)) self.fail(f"rst syntax raised unexpected exception: {e}")

View file

@ -11,26 +11,26 @@ from django.urls import reverse
from ..highlight import PygmentsHighlighter from ..highlight import PygmentsHighlighter
from ..models import Snippet from ..models import Snippet
config = apps.get_app_config('dpaste') config = apps.get_app_config("dpaste")
class SnippetTestCase(TestCase): class SnippetTestCase(TestCase):
def setUp(self): def setUp(self):
self.client = Client() self.client = Client()
self.new_url = reverse('snippet_new') self.new_url = reverse("snippet_new")
def valid_form_data(self, **kwargs): def valid_form_data(self, **kwargs):
data = { data = {
'content': u"Hello Wörld.\n\tGood Bye", "content": u"Hello Wörld.\n\tGood Bye",
'lexer': config.LEXER_DEFAULT, "lexer": config.LEXER_DEFAULT,
'expires': config.EXPIRE_DEFAULT, "expires": config.EXPIRE_DEFAULT,
} }
if kwargs: if kwargs:
data.update(kwargs) data.update(kwargs)
return data return data
def test_about(self): def test_about(self):
response = self.client.get(reverse('dpaste_about')) response = self.client.get(reverse("dpaste_about"))
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
@ -47,17 +47,17 @@ class SnippetTestCase(TestCase):
data = self.valid_form_data() data = self.valid_form_data()
# No content # No content
data['content'] = '' data["content"] = ""
self.client.post(self.new_url, data) self.client.post(self.new_url, data)
self.assertEqual(Snippet.objects.count(), 0) self.assertEqual(Snippet.objects.count(), 0)
# Just some spaces # Just some spaces
data['content'] = ' ' data["content"] = " "
self.client.post(self.new_url, data) self.client.post(self.new_url, data)
self.assertEqual(Snippet.objects.count(), 0) self.assertEqual(Snippet.objects.count(), 0)
# Linebreaks or tabs only are not valid either # Linebreaks or tabs only are not valid either
data['content'] = '\n\t ' data["content"] = "\n\t "
self.client.post(self.new_url, data) self.client.post(self.new_url, data)
self.assertEqual(Snippet.objects.count(), 0) self.assertEqual(Snippet.objects.count(), 0)
@ -70,7 +70,7 @@ class SnippetTestCase(TestCase):
response = self.client.post(self.new_url, data, follow=True) response = self.client.post(self.new_url, data, follow=True)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(Snippet.objects.count(), 1) self.assertEqual(Snippet.objects.count(), 1)
self.assertContains(response, data['content']) self.assertContains(response, data["content"])
# The unicode method contains the snippet id so we can easily print # The unicode method contains the snippet id so we can easily print
# the id using {{ snippet }} # the id using {{ snippet }}
@ -80,7 +80,7 @@ class SnippetTestCase(TestCase):
def test_new_snippet_custom_lexer(self): def test_new_snippet_custom_lexer(self):
# You can pass a lexer key in GET.l # You can pass a lexer key in GET.l
data = self.valid_form_data() data = self.valid_form_data()
url = '%s?l=haskell' % self.new_url url = "%s?l=haskell" % self.new_url
response = self.client.post(url, data, follow=True) response = self.client.post(url, data, follow=True)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
@ -89,7 +89,7 @@ class SnippetTestCase(TestCase):
# If you pass an invalid key it wont fail and just fallback # If you pass an invalid key it wont fail and just fallback
# to the default lexer. # to the default lexer.
data = self.valid_form_data() data = self.valid_form_data()
url = '%s?l=invalid-lexer' % self.new_url url = "%s?l=invalid-lexer" % self.new_url
response = self.client.post(url, data, follow=True) response = self.client.post(url, data, follow=True)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
@ -101,7 +101,7 @@ class SnippetTestCase(TestCase):
the snippet is considered as spam. We let the user know its spam. the snippet is considered as spam. We let the user know its spam.
""" """
data = self.valid_form_data() data = self.valid_form_data()
data['title'] = 'Any content' data["title"] = "Any content"
response = self.client.post(self.new_url, data, follow=True) response = self.client.post(self.new_url, data, follow=True)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(Snippet.objects.count(), 0) self.assertEqual(Snippet.objects.count(), 0)
@ -112,27 +112,27 @@ class SnippetTestCase(TestCase):
""" """
# POST data # POST data
data = self.valid_form_data() data = self.valid_form_data()
data['expires'] = 'onetime' data["expires"] = "onetime"
# First view, the author gets redirected after posting # First view, the author gets redirected after posting
response = self.client.post(self.new_url, data, follow=True) response = self.client.post(self.new_url, data, follow=True)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(Snippet.objects.count(), 1) self.assertEqual(Snippet.objects.count(), 1)
self.assertContains(response, data['content']) self.assertContains(response, data["content"])
# Second View, another user looks at the snippet # Second View, another user looks at the snippet
response = self.client.get(response.request['PATH_INFO'], follow=True) response = self.client.get(response.request["PATH_INFO"], follow=True)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(Snippet.objects.count(), 1) self.assertEqual(Snippet.objects.count(), 1)
self.assertContains(response, data['content']) self.assertContains(response, data["content"])
# Third/Further View, another user looks at the snippet but it was deleted # Third/Further View, another user looks at the snippet but it was deleted
response = self.client.get(response.request['PATH_INFO'], follow=True) response = self.client.get(response.request["PATH_INFO"], follow=True)
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
self.assertEqual(Snippet.objects.count(), 0) self.assertEqual(Snippet.objects.count(), 0)
def test_snippet_notfound(self): def test_snippet_notfound(self):
url = reverse('snippet_details', kwargs={'snippet_id': 'abcd'}) url = reverse("snippet_details", kwargs={"snippet_id": "abcd"})
response = self.client.get(url, follow=True) response = self.client.get(url, follow=True)
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
@ -143,18 +143,18 @@ class SnippetTestCase(TestCase):
data = self.valid_form_data() data = self.valid_form_data()
response = self.client.post(self.new_url, data, follow=True) response = self.client.post(self.new_url, data, follow=True)
response = self.client.post( response = self.client.post(
response.request['PATH_INFO'], data, follow=True response.request["PATH_INFO"], data, follow=True
) )
self.assertContains(response, data['content']) self.assertContains(response, data["content"])
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(Snippet.objects.count(), 2) self.assertEqual(Snippet.objects.count(), 2)
def test_reply_invalid(self): def test_reply_invalid(self):
data = self.valid_form_data() data = self.valid_form_data()
response = self.client.post(self.new_url, data, follow=True) response = self.client.post(self.new_url, data, follow=True)
del data['content'] del data["content"]
response = self.client.post( response = self.client.post(
response.request['PATH_INFO'], data, follow=True response.request["PATH_INFO"], data, follow=True
) )
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(Snippet.objects.count(), 1) self.assertEqual(Snippet.objects.count(), 1)
@ -170,8 +170,8 @@ class SnippetTestCase(TestCase):
self.client.post(self.new_url, data, follow=True) self.client.post(self.new_url, data, follow=True)
snippet_id = Snippet.objects.all()[0].secret_id snippet_id = Snippet.objects.all()[0].secret_id
url = reverse('snippet_details', kwargs={'snippet_id': snippet_id}) url = reverse("snippet_details", kwargs={"snippet_id": snippet_id})
response = self.client.post(url, {'delete': 1}, follow=True) response = self.client.post(url, {"delete": 1}, follow=True)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(Snippet.objects.count(), 0) self.assertEqual(Snippet.objects.count(), 0)
@ -180,8 +180,8 @@ class SnippetTestCase(TestCase):
data = self.valid_form_data() data = self.valid_form_data()
self.client.post(self.new_url, data, follow=True) self.client.post(self.new_url, data, follow=True)
url = reverse('snippet_details', kwargs={'snippet_id': 'doesnotexist'}) url = reverse("snippet_details", kwargs={"snippet_id": "doesnotexist"})
response = self.client.post(url, {'delete': 1}, follow=True) response = self.client.post(url, {"delete": 1}, follow=True)
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
self.assertEqual(Snippet.objects.count(), 1) self.assertEqual(Snippet.objects.count(), 1)
@ -192,7 +192,7 @@ class SnippetTestCase(TestCase):
# Do not pass delete=1 # Do not pass delete=1
snippet_id = Snippet.objects.all()[0].secret_id snippet_id = Snippet.objects.all()[0].secret_id
url = reverse('snippet_details', kwargs={'snippet_id': snippet_id}) url = reverse("snippet_details", kwargs={"snippet_id": snippet_id})
response = self.client.post(url, {}, follow=True) response = self.client.post(url, {}, follow=True)
# Returns regular snippet details page # Returns regular snippet details page
@ -210,7 +210,7 @@ class SnippetTestCase(TestCase):
# Next time its fetched its automatically deleted. # Next time its fetched its automatically deleted.
snippet_id = Snippet.objects.all()[0].secret_id snippet_id = Snippet.objects.all()[0].secret_id
url = reverse('snippet_details', kwargs={'snippet_id': snippet_id}) url = reverse("snippet_details", kwargs={"snippet_id": snippet_id})
response = self.client.get(url, follow=True) response = self.client.get(url, follow=True)
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
@ -224,19 +224,19 @@ class SnippetTestCase(TestCase):
self.client.post(self.new_url, data, follow=True) self.client.post(self.new_url, data, follow=True)
response = self.client.get( response = self.client.get(
reverse( reverse(
'snippet_details_raw', "snippet_details_raw",
kwargs={'snippet_id': Snippet.objects.all()[0].secret_id}, kwargs={"snippet_id": Snippet.objects.all()[0].secret_id},
) )
) )
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertContains(response, data['content']) self.assertContains(response, data["content"])
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
# XSS and correct escaping # XSS and correct escaping
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
XSS_ORIGINAL = '<script>hello</script>' XSS_ORIGINAL = "<script>hello</script>"
XSS_ESCAPED = '&lt;script&gt;hello&lt;/script&gt;' XSS_ESCAPED = "&lt;script&gt;hello&lt;/script&gt;"
def test_xss_text_lexer(self): def test_xss_text_lexer(self):
# Simple 'text' lexer # Simple 'text' lexer
@ -256,7 +256,7 @@ class SnippetTestCase(TestCase):
def test_xss_pygments_lexer(self): def test_xss_pygments_lexer(self):
# Pygments based lexer # 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) response = self.client.post(self.new_url, data, follow=True)
self.assertContains(response, self.XSS_ESCAPED) self.assertContains(response, self.XSS_ESCAPED)
@ -264,13 +264,13 @@ class SnippetTestCase(TestCase):
# History # History
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
def test_snippet_history(self): def test_snippet_history(self):
response = self.client.get(reverse('snippet_history')) response = self.client.get(reverse("snippet_history"))
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(Snippet.objects.count(), 0) self.assertEqual(Snippet.objects.count(), 0)
data = self.valid_form_data() data = self.valid_form_data()
self.client.post(self.new_url, data, follow=True) self.client.post(self.new_url, data, follow=True)
response = self.client.get(reverse('snippet_history')) response = self.client.get(reverse("snippet_history"))
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(Snippet.objects.count(), 1) self.assertEqual(Snippet.objects.count(), 1)
@ -278,7 +278,7 @@ class SnippetTestCase(TestCase):
def test_snippet_history_delete_all(self): def test_snippet_history_delete_all(self):
# Empty list, delete all raises no error # Empty list, delete all raises no error
response = self.client.post( response = self.client.post(
reverse('snippet_history'), {'delete': 1}, follow=True reverse("snippet_history"), {"delete": 1}, follow=True
) )
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(Snippet.objects.count(), 0) self.assertEqual(Snippet.objects.count(), 0)
@ -292,7 +292,7 @@ class SnippetTestCase(TestCase):
# Delete all of them # Delete all of them
response = self.client.post( response = self.client.post(
reverse('snippet_history'), {'delete': 1}, follow=True reverse("snippet_history"), {"delete": 1}, follow=True
) )
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(Snippet.objects.count(), 0) self.assertEqual(Snippet.objects.count(), 0)
@ -316,11 +316,11 @@ class SnippetTestCase(TestCase):
# You can call the management command with --dry-run which will # You can call the management command with --dry-run which will
# list snippets to delete, but wont actually do. # list snippets to delete, but wont actually do.
management.call_command('cleanup_snippets', dry_run=True) management.call_command("cleanup_snippets", dry_run=True)
self.assertEqual(Snippet.objects.count(), 2) self.assertEqual(Snippet.objects.count(), 2)
# Calling the management command will delete this one # Calling the management command will delete this one
management.call_command('cleanup_snippets') management.call_command("cleanup_snippets")
self.assertEqual(Snippet.objects.count(), 1) self.assertEqual(Snippet.objects.count(), 1)
def test_delete_management_snippet_that_never_expires_will_not_get_deleted( def test_delete_management_snippet_that_never_expires_will_not_get_deleted(
@ -330,18 +330,18 @@ class SnippetTestCase(TestCase):
Snippets without an expiration date wont get deleted automatically. Snippets without an expiration date wont get deleted automatically.
""" """
data = self.valid_form_data() data = self.valid_form_data()
data['expires'] = 'never' data["expires"] = "never"
self.client.post(self.new_url, data, follow=True) self.client.post(self.new_url, data, follow=True)
self.assertEqual(Snippet.objects.count(), 1) self.assertEqual(Snippet.objects.count(), 1)
management.call_command('cleanup_snippets') management.call_command("cleanup_snippets")
self.assertEqual(Snippet.objects.count(), 1) self.assertEqual(Snippet.objects.count(), 1)
def test_highlighting(self): def test_highlighting(self):
# You can pass any lexer to the pygmentize function and it will # You can pass any lexer to the pygmentize function and it will
# never fail loudly. # never fail loudly.
PygmentsHighlighter().highlight('code', 'python') PygmentsHighlighter().highlight("code", "python")
PygmentsHighlighter().highlight('code', 'doesnotexist') PygmentsHighlighter().highlight("code", "doesnotexist")
def test_random_slug_generation(self): def test_random_slug_generation(self):
""" """
@ -351,17 +351,17 @@ class SnippetTestCase(TestCase):
slugs are extended now. slugs are extended now.
""" """
for i in range(0, 100): for i in range(0, 100):
Snippet.objects.create(content='foobar') Snippet.objects.create(content="foobar")
slug_list = Snippet.objects.values_list( slug_list = Snippet.objects.values_list(
'secret_id', flat=True "secret_id", flat=True
).order_by('published') ).order_by("published")
self.assertEqual(len(set(slug_list)), 100) self.assertEqual(len(set(slug_list)), 100)
def test_leading_white_is_retained_in_db(self): def test_leading_white_is_retained_in_db(self):
""" """
Leading Whitespace is retained in the db. Leading Whitespace is retained in the db.
""" """
content = ' one\n two\n three\n four' content = " one\n two\n three\n four"
data = self.valid_form_data(content=content) data = self.valid_form_data(content=content)
self.client.post(self.new_url, data, follow=True) self.client.post(self.new_url, data, follow=True)
self.assertEqual(Snippet.objects.all()[0].content, content) self.assertEqual(Snippet.objects.all()[0].content, content)

View file

@ -1,11 +1,11 @@
from django.conf.urls import include, url from django.conf.urls import include, url
urlpatterns = [ urlpatterns = [
url(r'^', include('dpaste.urls.dpaste_api')), url(r"^", include("dpaste.urls.dpaste_api")),
url(r'^', include('dpaste.urls.dpaste')), url(r"^", include("dpaste.urls.dpaste")),
url(r'^i18n/', include('django.conf.urls.i18n')), url(r"^i18n/", include("django.conf.urls.i18n")),
] ]
# Custom error handlers which load `dpaste/<code>.html` instead of `<code>.html` # Custom error handlers which load `dpaste/<code>.html` instead of `<code>.html`
handler404 = 'dpaste.views.page_not_found' handler404 = "dpaste.views.page_not_found"
handler500 = 'dpaste.views.server_error' handler500 = "dpaste.views.server_error"

View file

@ -6,37 +6,37 @@ from django.views.generic import TemplateView
from .. import views from .. import views
L = getattr(settings, 'DPASTE_SLUG_LENGTH', 4) L = getattr(settings, "DPASTE_SLUG_LENGTH", 4)
config = apps.get_app_config('dpaste') config = apps.get_app_config("dpaste")
urlpatterns = [ urlpatterns = [
url(r'^$', views.SnippetView.as_view(), name='snippet_new'), url(r"^$", views.SnippetView.as_view(), name="snippet_new"),
url( url(
r'^about/$', r"^about/$",
TemplateView.as_view( TemplateView.as_view(
template_name='dpaste/about.html', template_name="dpaste/about.html",
extra_context=config.extra_template_context, extra_context=config.extra_template_context,
), ),
name='dpaste_about', name="dpaste_about",
), ),
url(r'^history/$', views.SnippetHistory.as_view(), name='snippet_history'), url(r"^history/$", views.SnippetHistory.as_view(), name="snippet_history"),
url( url(
r'^(?P<snippet_id>[a-zA-Z0-9]{%d,})/?$' % L, r"^(?P<snippet_id>[a-zA-Z0-9]{%d,})/?$" % L,
views.SnippetDetailView.as_view(), views.SnippetDetailView.as_view(),
name='snippet_details', name="snippet_details",
), ),
url( url(
r'^(?P<snippet_id>[a-zA-Z0-9]{%d,})/raw/?$' % L, r"^(?P<snippet_id>[a-zA-Z0-9]{%d,})/raw/?$" % L,
views.SnippetRawView.as_view(), views.SnippetRawView.as_view(),
name='snippet_details_raw', name="snippet_details_raw",
), ),
url( url(
r'^(?P<snippet_id>[a-zA-Z0-9]{%d,})/slim/?$' % L, r"^(?P<snippet_id>[a-zA-Z0-9]{%d,})/slim/?$" % L,
xframe_options_exempt( xframe_options_exempt(
views.SnippetDetailView.as_view( views.SnippetDetailView.as_view(
template_name='dpaste/details_slim.html' template_name="dpaste/details_slim.html"
) )
), ),
name='snippet_details_slim', name="snippet_details_slim",
), ),
] ]

View file

@ -5,8 +5,8 @@ from ..views import APIView
urlpatterns = [ urlpatterns = [
url( url(
r'^api/$', r"^api/$",
csrf_exempt(APIView.as_view()), csrf_exempt(APIView.as_view()),
name='dpaste_api_create_snippet', name="dpaste_api_create_snippet",
) )
] ]

View file

@ -27,7 +27,7 @@ from dpaste.forms import SnippetForm, get_expire_values
from dpaste.highlight import PygmentsHighlighter from dpaste.highlight import PygmentsHighlighter
from dpaste.models import Snippet from dpaste.models import Snippet
config = apps.get_app_config('dpaste') config = apps.get_app_config("dpaste")
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
@ -41,11 +41,11 @@ class SnippetView(FormView):
""" """
form_class = SnippetForm form_class = SnippetForm
template_name = 'dpaste/new.html' template_name = "dpaste/new.html"
def get_form_kwargs(self): def get_form_kwargs(self):
kwargs = super(SnippetView, self).get_form_kwargs() kwargs = super(SnippetView, self).get_form_kwargs()
kwargs.update({'request': self.request}) kwargs.update({"request": self.request})
return kwargs return kwargs
def form_valid(self, form): def form_valid(self, form):
@ -65,9 +65,9 @@ class SnippetDetailView(SnippetView, DetailView):
""" """
queryset = Snippet.objects.all() queryset = Snippet.objects.all()
template_name = 'dpaste/details.html' template_name = "dpaste/details.html"
slug_url_kwarg = 'snippet_id' slug_url_kwarg = "snippet_id"
slug_field = 'secret_id' slug_field = "secret_id"
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
""" """
@ -76,14 +76,14 @@ class SnippetDetailView(SnippetView, DetailView):
reasons and the chance to abuse this is not given anyway, since snippets reasons and the chance to abuse this is not given anyway, since snippets
always expire. always expire.
""" """
if 'delete' in self.request.POST: if "delete" in self.request.POST:
snippet = get_object_or_404( snippet = get_object_or_404(
Snippet, secret_id=self.kwargs['snippet_id'] Snippet, secret_id=self.kwargs["snippet_id"]
) )
snippet.delete() snippet.delete()
# Append `#` so #delete goes away in Firefox # Append `#` so #delete goes away in Firefox
url = '{0}#'.format(reverse('snippet_new')) url = "{0}#".format(reverse("snippet_new"))
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
return super(SnippetDetailView, self).post(request, *args, **kwargs) return super(SnippetDetailView, self).post(request, *args, **kwargs)
@ -104,16 +104,16 @@ class SnippetDetailView(SnippetView, DetailView):
# Increase the view count of the snippet # Increase the view count of the snippet
snippet.view_count += 1 snippet.view_count += 1
snippet.save(update_fields=['view_count']) snippet.save(update_fields=["view_count"])
return super(SnippetDetailView, self).get(request, *args, **kwargs) return super(SnippetDetailView, self).get(request, *args, **kwargs)
def get_initial(self): def get_initial(self):
snippet = self.get_object() snippet = self.get_object()
return { return {
'content': snippet.content, "content": snippet.content,
'lexer': snippet.lexer, "lexer": snippet.lexer,
'rtl': snippet.rtl, "rtl": snippet.rtl,
} }
def form_valid(self, form): def form_valid(self, form):
@ -132,12 +132,12 @@ class SnippetDetailView(SnippetView, DetailView):
d = difflib.unified_diff( d = difflib.unified_diff(
snippet.parent.content.splitlines(), snippet.parent.content.splitlines(),
snippet.content.splitlines(), snippet.content.splitlines(),
ugettext('Previous Snippet'), ugettext("Previous Snippet"),
ugettext('Current Snippet'), ugettext("Current Snippet"),
n=1, n=1,
) )
diff_code = '\n'.join(d).strip() diff_code = "\n".join(d).strip()
highlighted = PygmentsHighlighter().render(diff_code, 'diff') highlighted = PygmentsHighlighter().render(diff_code, "diff")
# Remove blank lines # Remove blank lines
return highlighted return highlighted
@ -148,9 +148,9 @@ class SnippetDetailView(SnippetView, DetailView):
ctx = super(SnippetDetailView, self).get_context_data(**kwargs) ctx = super(SnippetDetailView, self).get_context_data(**kwargs)
ctx.update( ctx.update(
{ {
'wordwrap': self.object.lexer in highlight.LEXER_WORDWRAP, "wordwrap": self.object.lexer in highlight.LEXER_WORDWRAP,
'diff': self.get_snippet_diff(), "diff": self.get_snippet_diff(),
'raw_mode': config.RAW_MODE_ENABLED, "raw_mode": config.RAW_MODE_ENABLED,
} }
) )
ctx.update(config.extra_template_context) ctx.update(config.extra_template_context)
@ -162,22 +162,20 @@ class SnippetRawView(SnippetDetailView):
Display the raw content of a snippet Display the raw content of a snippet
""" """
template_name = 'dpaste/raw.html' template_name = "dpaste/raw.html"
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
if not config.RAW_MODE_ENABLED: if not config.RAW_MODE_ENABLED:
return HttpResponseForbidden( return HttpResponseForbidden(
ugettext( ugettext("This dpaste installation has Raw view mode disabled.")
'This dpaste installation has Raw view mode disabled.'
)
) )
return super(SnippetRawView, self).dispatch(request, *args, **kwargs) return super(SnippetRawView, self).dispatch(request, *args, **kwargs)
def render_plain_text(self, context, **response_kwargs): def render_plain_text(self, context, **response_kwargs):
snippet = self.get_object() snippet = self.get_object()
response = HttpResponse(snippet.content) response = HttpResponse(snippet.content)
response['Content-Type'] = 'text/plain;charset=UTF-8' response["Content-Type"] = "text/plain;charset=UTF-8"
response['X-Content-Type-Options'] = 'nosniff' response["X-Content-Type-Options"] = "nosniff"
return response return response
def render_to_response(self, context, **response_kwargs): def render_to_response(self, context, **response_kwargs):
@ -199,26 +197,26 @@ class SnippetHistory(TemplateView):
session). session).
""" """
template_name = 'dpaste/history.html' template_name = "dpaste/history.html"
def get_user_snippets(self): def get_user_snippets(self):
snippet_id_list = self.request.session.get('snippet_list', []) snippet_id_list = self.request.session.get("snippet_list", [])
return Snippet.objects.filter(pk__in=snippet_id_list) return Snippet.objects.filter(pk__in=snippet_id_list)
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
""" """
Delete all user snippets at once. Delete all user snippets at once.
""" """
if 'delete' in self.request.POST: if "delete" in self.request.POST:
self.get_user_snippets().delete() self.get_user_snippets().delete()
# Append `#` so #delete goes away in Firefox # Append `#` so #delete goes away in Firefox
url = '{0}#'.format(reverse('snippet_history')) url = "{0}#".format(reverse("snippet_history"))
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
ctx = super(SnippetHistory, self).get_context_data(**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()})
ctx.update(config.extra_template_context) ctx.update(config.extra_template_context)
return ctx return ctx
@ -238,7 +236,7 @@ class APIView(View):
The default response is the snippet URL wrapped in quotes. The default response is the snippet URL wrapped in quotes.
""" """
base_url = config.get_base_url(request=self.request) base_url = config.get_base_url(request=self.request)
return '"{url}{path}"'.format(url=base_url, path=s.get_absolute_url()) return f'"{base_url}{s.get_absolute_url()}"'
def _format_url(self, s): def _format_url(self, s):
""" """
@ -246,7 +244,7 @@ class APIView(View):
no quotes, but a linebreak at the end. no quotes, but a linebreak at the end.
""" """
base_url = config.get_base_url(request=self.request) base_url = config.get_base_url(request=self.request)
return '{url}{path}\n'.format(url=base_url, path=s.get_absolute_url()) return f"{base_url}{s.get_absolute_url()}\n"
def _format_json(self, s): def _format_json(self, s):
""" """
@ -255,37 +253,35 @@ class APIView(View):
base_url = config.get_base_url(request=self.request) base_url = config.get_base_url(request=self.request)
return json.dumps( return json.dumps(
{ {
'url': '{url}{path}'.format( "url": f"{base_url}{s.get_absolute_url()}",
url=base_url, path=s.get_absolute_url() "content": s.content,
), "lexer": s.lexer,
'content': s.content,
'lexer': s.lexer,
} }
) )
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
content = request.POST.get('content', '') content = request.POST.get("content", "")
lexer = request.POST.get('lexer', highlight.LEXER_DEFAULT).strip() lexer = request.POST.get("lexer", highlight.LEXER_DEFAULT).strip()
filename = request.POST.get('filename', '').strip() filename = request.POST.get("filename", "").strip()
expires = request.POST.get('expires', '').strip() expires = request.POST.get("expires", "").strip()
response_format = request.POST.get('format', 'default').strip() response_format = request.POST.get("format", "default").strip()
if not content.strip(): if not content.strip():
return HttpResponseBadRequest('No content given') return HttpResponseBadRequest("No content given")
# We need at least a lexer or a filename # We need at least a lexer or a filename
if not lexer and not filename: if not lexer and not filename:
return HttpResponseBadRequest( return HttpResponseBadRequest(
'No lexer or filename given. Unable to ' "No lexer or filename given. Unable to "
'determine a highlight. Valid lexers are: %s' "determine a highlight. Valid lexers are: %s"
% ', '.join(highlight.LEXER_KEYS) % ", ".join(highlight.LEXER_KEYS)
) )
# A lexer is given, check if its valid at all # A lexer is given, check if its valid at all
if lexer and lexer not in highlight.LEXER_KEYS: if lexer and lexer not in highlight.LEXER_KEYS:
return HttpResponseBadRequest( return HttpResponseBadRequest(
'Invalid lexer "%s" given. Valid lexers are: %s' 'Invalid lexer "%s" given. Valid lexers are: %s'
% (lexer, ', '.join(highlight.LEXER_KEYS)) % (lexer, ", ".join(highlight.LEXER_KEYS))
) )
# No lexer is given, but we have a filename, try to get the lexer # No lexer is given, but we have a filename, try to get the lexer
@ -303,7 +299,7 @@ class APIView(View):
if expires not in expire_options: if expires not in expire_options:
return HttpResponseBadRequest( return HttpResponseBadRequest(
'Invalid expire choice "{}" given. Valid values are: {}'.format( 'Invalid expire choice "{}" given. Valid values are: {}'.format(
expires, ', '.join(expire_options) expires, ", ".join(expire_options)
) )
) )
expires, expire_type = get_expire_values(expires) expires, expire_type = get_expire_values(expires)
@ -321,7 +317,7 @@ class APIView(View):
) )
# Custom formatter for the API response # Custom formatter for the API response
formatter = getattr(self, '_format_{0}'.format(response_format), None) formatter = getattr(self, f"_format_{response_format}", None)
if callable(formatter): if callable(formatter):
return HttpResponse(formatter(snippet)) return HttpResponse(formatter(snippet))
@ -335,13 +331,13 @@ class APIView(View):
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
def page_not_found(request, exception=None, template_name='dpaste/404.html'): def page_not_found(request, exception=None, template_name="dpaste/404.html"):
return django_page_not_found( return django_page_not_found(
request, exception, template_name=template_name request, exception, template_name=template_name
) )
def server_error(request, template_name='dpaste/500.html'): def server_error(request, template_name="dpaste/500.html"):
return django_server_error( return django_server_error(
request, template_name=template_name request, template_name=template_name
) # pragma: no cover ) # pragma: no cover