diff --git a/peps/converters.py b/peps/converters.py index 5afd2a263..18e47ff4d 100644 --- a/peps/converters.py +++ b/peps/converters.py @@ -7,11 +7,21 @@ from django.core.exceptions import ImproperlyConfigured from django.core.files import File +from pygments import highlight +from pygments.lexers import PythonLexer +from pygments.formatters import HtmlFormatter + from pages.models import Page, Image PEP_TEMPLATE = 'pages/pep-page.html' pep_url = lambda num: 'dev/peps/pep-{}/'.format(num) +# To simplify syntax highlighting, all literal blocks (those produced by ::) +# in the following PEPs will be automatically highlighted using Python lexer. +# PEP editors/authors could make simple PRs extending this list. +# This will be not needed when PEPs are moved to RtD and all code blocks are +# formatted using .. code:: language. +PURE_PYTHON_PEPS = [483, 484, 526] def check_paths(): """ Checks to ensure our PEP_REPO_PATH is setup correctly """ @@ -160,6 +170,21 @@ def convert_pep_page(pep_number, content): # Fix PEP links pep_content = BeautifulSoup(data['content']) body_links = pep_content.find_all("a") + # Fix highlighting code + code_blocks = pep_content.find_all('pre', class_='code') + for cb in code_blocks: + del cb['class'] + div = pep_content.new_tag('div') + div['class'] = ['highlight'] + cb.wrap(div) + # Highlight existing pure-Python PEPs + if int(pep_number) in PURE_PYTHON_PEPS: + literal_blocks = pep_content.find_all('pre', class_='literal-block') + for lb in literal_blocks: + block = lb.string + if block: + highlighted = highlight(block, PythonLexer(), HtmlFormatter()) + lb.replace_with(BeautifulSoup(highlighted).html.body.div) pep_href_re = re.compile(r'pep-(\d+)\.html') diff --git a/peps/tests/fake_pep_repo/pep-0526.html b/peps/tests/fake_pep_repo/pep-0526.html new file mode 100644 index 000000000..ad80aa98b --- /dev/null +++ b/peps/tests/fake_pep_repo/pep-0526.html @@ -0,0 +1,655 @@ + + +++ + + + + + + + + + + + + + + + + + + + + + + + + + +
PEP:526
Title:Syntax for Variable Annotations
Version:$Revision$
Last-Modified:$Date$
Author:Ryan Gonzalez <rymg19 at gmail.com>, Philip House <phouse512 at gmail.com>, Ivan Levkivskyi <levkivskyi at gmail.com>, Lisa Roach <lisaroach14 at gmail.com>, Guido van Rossum <guido at python.org>
Status:Final
Type:Standards Track
Content-Type:text/x-rst
Created:09-Aug-2016
Python-Version:3.6
Post-History:30-Aug-2016, 02-Sep-2016
Resolution:https://mail.python.org/pipermail/python-dev/2016-September/146282.html
+
+
+

Contents

+ +
+
+

Status

+

This PEP has been provisionally accepted by the BDFL. +See the acceptance message for more color: +https://mail.python.org/pipermail/python-dev/2016-September/146282.html

+
+
+

Notice for Reviewers

+

This PEP was drafted in a separate repo: +https://github.com/phouse512/peps/tree/pep-0526.

+

There was preliminary discussion on python-ideas and at +https://github.com/python/typing/issues/258.

+

Before you bring up an objection in a public forum please at least +read the summary of rejected ideas listed at the end of this PEP.

+
+
+

Abstract

+

PEP 484 introduced type hints, a.k.a. type annotations. While its +main focus was function annotations, it also introduced the notion of +type comments to annotate variables:

+
+# 'primes' is a list of integers
+primes = []  # type: List[int]
+
+# 'captain' is a string (Note: initial value is a problem)
+captain = ...  # type: str
+
+class Starship:
+    # 'stats' is a class variable
+    stats = {}  # type: Dict[str, int]
+
+

This PEP aims at adding syntax to Python for annotating the types of variables +(including class variables and instance variables), +instead of expressing them through comments:

+
+primes: List[int] = []
+
+captain: str  # Note: no initial value!
+
+class Starship:
+    stats: ClassVar[Dict[str, int]] = {}
+
+

PEP 484 explicitly states that type comments are intended to help with +type inference in complex cases, and this PEP does not change this +intention. However, since in practice type comments have also been +adopted for class variables and instance variables, this PEP also +discusses the use of type annotations for those variables.

+
+
+

Rationale

+

Although type comments work well enough, the fact that they're +expressed through comments has some downsides:

+ +

The majority of these issues can be alleviated by making the syntax +a core part of the language. Moreover, having a dedicated annotation syntax +for class and instance variables (in addition to method annotations) will +pave the way to static duck-typing as a complement to nominal typing defined +by PEP 484.

+
+

Non-goals

+

While the proposal is accompanied by an extension of the typing.get_type_hints +standard library function for runtime retrieval of annotations, variable +annotations are not designed for runtime type checking. Third party packages +will have to be developed to implement such functionality.

+

It should also be emphasized that Python will remain a dynamically typed +language, and the authors have no desire to ever make type hints mandatory, +even by convention. Type annotations should not be confused with variable +declarations in statically typed languages. The goal of annotation syntax is +to provide an easy way to specify structured type metadata +for third party tools.

+

This PEP does not require type checkers to change their type checking +rules. It merely provides a more readable syntax to replace type +comments.

+
+
+
+

Specification

+

Type annotation can be added to an assignment statement or to a single +expression indicating the desired type of the annotation target to a third +party type checker:

+
+my_var: int
+my_var = 5  # Passes type check.
+other_var: int  = 'a'  # Flagged as error by type checker,
+                       # but OK at runtime.
+
+

This syntax does not introduce any new semantics beyond PEP 484, so that +the following three statements are equivalent:

+
+var = value # type: annotation
+var: annotation; var = value
+var: annotation = value
+
+

Below we specify the syntax of type annotations +in different contexts and their runtime effects.

+

We also suggest how type checkers might interpret annotations, but +compliance to these suggestions is not mandatory. (This is in line +with the attitude towards compliance in PEP 484.)

+
+

Global and local variable annotations

+

The types of locals and globals can be annotated as follows:

+
+some_number: int           # variable without initial value
+some_list: List[int] = []  # variable with initial value
+
+

Being able to omit the initial value allows for easier typing of variables +assigned in conditional branches:

+
+sane_world: bool
+if 2+2 == 4:
+    sane_world = True
+else:
+    sane_world = False
+
+

Note that, although the syntax does allow tuple packing, it does not allow +one to annotate the types of variables when tuple unpacking is used:

+
+# Tuple packing with variable annotation syntax
+t: Tuple[int, ...] = (1, 2, 3)
+
+# Tuple unpacking with variable annotation syntax
+header: str
+kind: int
+body: Optional[List[str]]
+header, kind, body = message
+
+

Omitting the initial value leaves the variable uninitialized:

+
+a: int
+print(a)  # raises NameError
+
+

However, annotating a local variable will cause the interpreter to always make +it a local:

+
+def f():
+    a: int
+    print(a)  # raises UnboundLocalError
+    # Commenting out the a: int makes it a NameError.
+
+

as if the code were:

+
+def f():
+    if False: a = 0
+    print(a)  # raises UnboundLocalError
+
+

Duplicate type annotations will be ignored. However, static type +checkers may issue a warning for annotations of the same variable +by a different type:

+
+a: int
+a: str  # Static type checker may or may not warn about this.
+
+
+
+

Class and instance variable annotations

+

Type annotations can also be used to annotate class and instance variables +in class bodies and methods. In particular, the value-less notation a: int +allows one to annotate instance variables that should be initialized +in __init__ or __new__. The proposed syntax is as follows:

+
+class BasicStarship:
+    captain: str = 'Picard'               # instance variable with default
+    damage: int                           # instance variable without default
+    stats: ClassVar[Dict[str, int]] = {}  # class variable
+
+

Here ClassVar is a special class defined by the typing module that +indicates to the static type checker that this variable should not be +set on instances.

+

This could be illustrated with a more detailed example. In this class:

+
+class Starship:
+    captain = 'Picard'
+    stats = {}
+
+    def __init__(self, damage, captain=None):
+        self.damage = damage
+        if captain:
+            self.captain = captain  # Else keep the default
+
+    def hit(self):
+        Starship.stats['hits'] = Starship.stats.get('hits', 0) + 1
+
+

stats is intended to be a class variable (keeping track of many different +per-game statistics), while captain is an instance variable with a default +value set in the class. This difference might not be seen by a type +checker: both get initialized in the class, but captain serves only +as a convenient default value for the instance variable, while stats +is truly a class variable -- it is intended to be shared by all instances.

+

Since both variables happen to be initialized at the class level, it is +useful to distinguish them by marking class variables as annotated with +types wrapped in ClassVar[...]. In this way a type checker may flag +accidental assignments to attributes with the same name on instances.

+

For example, annotating the discussed class:

+
+class Starship:
+    captain: str = 'Picard'
+    damage: int
+    stats: ClassVar[Dict[str, int]] = {}
+
+    def __init__(self, damage: int, captain: str = None):
+        self.damage = damage
+        if captain:
+            self.captain = captain  # Else keep the default
+
+    def hit(self):
+        Starship.stats['hits'] = Starship.stats.get('hits', 0) + 1
+
+enterprise_d = Starship(3000)
+enterprise_d.stats = {} # Flagged as error by a type checker
+Starship.stats = {} # This is OK
+
+

As a matter of convenience (and convention), instance variables can be +annotated in __init__ or other methods, rather than in the class:

+
+from typing import Generic, TypeVar
+T = TypeVar(’T’)
+
+class Box(Generic[T]):
+    def __init__(self, content):
+        self.content: T = content
+
+
+
+

Annotating expressions

+

The target of the annotation can be any valid single assignment +target, at least syntactically (it is up to the type checker what to +do with this):

+
+class Cls:
+    pass
+
+c = Cls()
+c.x: int = 0  # Annotates c.x with int.
+c.y: int      # Annotates c.y with int.
+
+d = {}
+d['a']: int = 0  # Annotates d['a'] with int.
+d['b']: int      # Annotates d['b'] with int.
+
+

Note that even a parenthesized name is considered an expression, +not a simple name:

+
+(x): int      # Annotates x with int, (x) treated as expression by compiler.
+(y): int = 0  # Same situation here.
+
+
+
+

Where annotations aren't allowed

+

It is illegal to attempt to annotate variables subject to global +or nonlocal in the same function scope:

+
+def f():
+    global x: int  # SyntaxError
+
+def g():
+    x: int  # Also a SyntaxError
+    global x
+
+

The reason is that global and nonlocal don't own variables; +therefore, the type annotations belong in the scope owning the variable.

+

Only single assignment targets and single right hand side values are allowed. +In addition, one cannot annotate variables used in a for or with +statement; they can be annotated ahead of time, in a similar manner to tuple +unpacking:

+
+a: int
+for a in my_iter:
+    ...
+
+f: MyFile
+with myfunc() as f:
+    ...
+
+
+
+
+

Changes to Standard Library and Documentation

+ +
+
+

Runtime Effects of Type Annotations

+

Annotating a local variable will cause +the interpreter to treat it as a local, even if it was never assigned to. +Annotations for local variables will not be evaluated:

+
+def f():
+    x: NonexistentName  # No error.
+
+

However, if it is at a module or class level, then the type will be +evaluated:

+
+x: NonexistentName  # Error!
+class X:
+    var: NonexistentName  # Error!
+
+

In addition, at the module or class level, if the item being annotated is a +simple name, then it and the annotation will be stored in the +__annotations__ attribute of that module or class (mangled if private) +as an ordered mapping from names to evaluated annotations. +Here is an example:

+
+from typing import Dict
+class Player:
+    ...
+players: Dict[str, Player]
+__points: int
+
+print(__annotations__)
+# prints: {'players': typing.Dict[str, __main__.Player],
+#          '_Player__points': <class 'int'>}
+
+

__annotations__ is writable, so this is permitted:

+
+__annotations__['s'] = str
+
+

But attempting to update __annotations__ to something other than an +ordered mapping may result in a TypeError:

+
+class C:
+    __annotations__ = 42
+    x: int = 5  # raises TypeError
+
+

(Note that the assignment to __annotations__, which is the +culprit, is accepted by the Python interpreter without questioning it +-- but the subsequent type annotation expects it to be a +MutableMapping and will fail.)

+

The recommended way of getting annotations at runtime is by using +typing.get_type_hints function; as with all dunder attributes, +any undocummented use of __annotations__ is subject to breakage +without warning:

+
+from typing import Dict, ClassVar, get_type_hints
+class Starship:
+    hitpoints: int = 50
+    stats: ClassVar[Dict[str, int]] = {}
+    shield: int = 100
+    captain: str
+    def __init__(self, captain: str) -> None:
+        ...
+
+assert get_type_hints(Starship) == {'hitpoints': int,
+                                    'stats': ClassVar[Dict[str, int]],
+                                    'shield': int,
+                                    'captain': str}
+
+assert get_type_hints(Starship.__init__) == {'captain': str,
+                                             'return': None}
+
+

Note that if annotations are not found statically, then the +__annotations__ dictionary is not created at all. Also the +value of having annotations available locally does not offset +the cost of having to create and populate the annotations dictionary +on every function call. Therefore annotations at function level are +not evaluated and not stored.

+
+

Other uses of annotations

+

While Python with this PEP will not object to:

+
+alice: 'well done' = 'A+'
+bob: 'what a shame' = 'F-'
+
+

since it will not care about the type annotation beyond "it evaluates +without raising", a type checker that encounters it will flag it, +unless disabled with # type: ignore or @no_type_check.

+

However, since Python won't care what the "type" is, +if the above snippet is at the global level or in a class, __annotations__ +will include {'alice': 'well done', 'bob': 'what a shame'}.

+

These stored annotations might be used for other purposes, +but with this PEP we explicitly recommend type hinting as the +preferred use of annotations.

+
+
+
+

Rejected/Postponed Proposals

+ +
+
+

Backwards Compatibility

+

This PEP is fully backwards compatible.

+
+
+

Implementation

+

An implementation for Python 3.6 is found on GitHub repo at +https://github.com/ilevkivskyi/cpython/tree/pep-526

+
+ + diff --git a/peps/tests/test_converters.py b/peps/tests/test_converters.py index ec10ae886..d2c6832f0 100644 --- a/peps/tests/test_converters.py +++ b/peps/tests/test_converters.py @@ -27,3 +27,10 @@ def test_source_link(self): 'pep-0525.txt">https://github.com/python/peps/blob/master/pep-0525.txt', pep.content.rendered ) + + @override_settings(PEP_REPO_PATH=FAKE_PEP_REPO) + def test_highlighted_code(self): + pep_h = get_pep_page('0526') + self.assertIn('
', pep_h.content.rendered) + self.assertIn('# type:', pep_h.content.rendered) + self.assertIn('class', pep_h.content.rendered) diff --git a/requirements.txt b/requirements.txt index cd9e6d31b..2b6231c35 100644 --- a/requirements.txt +++ b/requirements.txt @@ -44,3 +44,5 @@ uWSGI==2.0.10 raven==5.2.0 django-waffle==0.11.1 sqlparse<0.2 #TODO: delete this when we switch to Django 1.8 and DDT 1.5 + +pygments==2.1.3 # This will be not needed when PEPs are moved to RtD. diff --git a/static/stylesheets/pygments_default.css b/static/stylesheets/pygments_default.css new file mode 100644 index 000000000..0af5bf6a2 --- /dev/null +++ b/static/stylesheets/pygments_default.css @@ -0,0 +1,123 @@ +.highlight .c { color: #408080; font-style: italic } /* Comment */ +.highlight .err { border: 1px solid #FF0000 } /* Error */ +.highlight .k { color: #008000; font-weight: bold } /* Keyword */ +.highlight .o { color: #666666 } /* Operator */ +.highlight .cm { color: #408080; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #BC7A00 } /* Comment.Preproc */ +.highlight .c1 { color: #408080; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #408080; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #A00000 } /* Generic.Deleted */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .gr { color: #FF0000 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #00A000 } /* Generic.Inserted */ +.highlight .go { color: #808080 } /* Generic.Output */ +.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #0040D0 } /* Generic.Traceback */ +.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #008000 } /* Keyword.Pseudo */ +.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #B00040 } /* Keyword.Type */ +.highlight .m { color: #666666 } /* Literal.Number */ +.highlight .s { color: #BA2121 } /* Literal.String */ +.highlight .na { color: #7D9029 } /* Name.Attribute */ +.highlight .nb { color: #008000 } /* Name.Builtin */ +.highlight .nc { color: #3333cc; font-weight: bold } /* Name.Class */ +.highlight .no { color: #880000 } /* Name.Constant */ +.highlight .nd { color: #AA22FF } /* Name.Decorator */ +.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */ +.highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #3333cc } /* Name.Function */ +.highlight .nl { color: #A0A000 } /* Name.Label */ +.highlight .nn { color: #3333cc; font-weight: bold } /* Name.Namespace */ +.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #19177C } /* Name.Variable */ +.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mf { color: #666666 } /* Literal.Number.Float */ +.highlight .mh { color: #666666 } /* Literal.Number.Hex */ +.highlight .mi { color: #666666 } /* Literal.Number.Integer */ +.highlight .mo { color: #666666 } /* Literal.Number.Oct */ +.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ +.highlight .sc { color: #BA2121 } /* Literal.String.Char */ +.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #BA2121 } /* Literal.String.Double */ +.highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ +.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ +.highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ +.highlight .sx { color: #008000 } /* Literal.String.Other */ +.highlight .sr { color: #BB6688 } /* Literal.String.Regex */ +.highlight .s1 { color: #BA2121 } /* Literal.String.Single */ +.highlight .ss { color: #19177C } /* Literal.String.Symbol */ +.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ +.highlight .vc { color: #19177C } /* Name.Variable.Class */ +.highlight .vg { color: #19177C } /* Name.Variable.Global */ +.highlight .vi { color: #19177C } /* Name.Variable.Instance */ +.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ +.highlight .lineno { color: #000000; background-color: #dddddd; } + +.highlight .comment { color: #408080; font-style: italic } /* Comment */ +.highlight .error { border: 1px solid #FF0000 } /* Error */ +.highlight .keyword { color: #008000; font-weight: bold } /* Keyword */ +.highlight .operator { color: #666666 } /* Operator */ +.highlight .comment.multiline { color: #408080; font-style: italic } /* Comment.Multiline */ +.highlight .comment.preproc { color: #BC7A00 } /* Comment.Preproc */ +.highlight .comment.single { color: #408080; font-style: italic } /* Comment.Single */ +.highlight .comment.special { color: #408080; font-style: italic } /* Comment.Special */ +.highlight .generic.deleted { color: #A00000 } /* Generic.Deleted */ +.highlight .generic.emph { font-style: italic } /* Generic.Emph */ +.highlight .generic.error { color: #FF0000 } /* Generic.Error */ +.highlight .generic.heading { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .generic.inserted { color: #00A000 } /* Generic.Inserted */ +.highlight .generic.output { color: #808080 } /* Generic.Output */ +.highlight .generic.prompt { color: #000080; font-weight: bold } /* Generic.Prompt */ +.highlight .generic.strong { font-weight: bold } /* Generic.Strong */ +.highlight .generic.subheading { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .generic.traceback { color: #0040D0 } /* Generic.Traceback */ +.highlight .keyword.constant { color: #008000; font-weight: bold } /* Keyword.Constant */ +.highlight .keyword.declaration { color: #008000; font-weight: bold } /* Keyword.Declaration */ +.highlight .keyword.namespace { color: #008000; font-weight: bold } /* Keyword.Namespace */ +.highlight .keyword.pseudo { color: #008000 } /* Keyword.Pseudo */ +.highlight .keyword.reserved { color: #008000; font-weight: bold } /* Keyword.Reserved */ +.highlight .keyword.type { color: #B00040 } /* Keyword.Type */ +.highlight .number { color: #666666 } /* Literal.Number */ +.highlight .string { color: #BA2121 } /* Literal.String */ +.highlight .name.attribute { color: #7D9029 } /* Name.Attribute */ +.highlight .name.builtin { color: #008000 } /* Name.Builtin */ +.highlight .name.class { color: #3333cc; font-weight: bold } /* Name.Class */ +.highlight .name.constant { color: #880000 } /* Name.Constant */ +.highlight .name.decorator { color: #AA22FF } /* Name.Decorator */ +.highlight .name.entity { color: #999999; font-weight: bold } /* Name.Entity */ +.highlight .name.exception { color: #D2413A; font-weight: bold } /* Name.Exception */ +.highlight .name.function { color: #3333cc } /* Name.Function */ +.highlight .name.label { color: #A0A000 } /* Name.Label */ +.highlight .name.namespace { color: #3333cc; font-weight: bold } /* Name.Namespace */ +.highlight .name.tag { color: #008000; font-weight: bold } /* Name.Tag */ +.highlight .name.variable { color: #19177C } /* Name.Variable */ +.highlight .operator.word { color: #AA22FF; font-weight: bold } /* Operator.Word */ +.highlight .whitespace { color: #bbbbbb } /* Text.Whitespace */ +.highlight .number.float { color: #666666 } /* Literal.Number.Float */ +.highlight .number.hex { color: #666666 } /* Literal.Number.Hex */ +.highlight .number.integer { color: #666666 } /* Literal.Number.Integer */ +.highlight .number.oct { color: #666666 } /* Literal.Number.Oct */ +.highlight .string.backtick { color: #BA2121 } /* Literal.String.Backtick */ +.highlight .string.char { color: #BA2121 } /* Literal.String.Char */ +.highlight .string.doc { color: #BA2121; font-style: italic } /* Literal.String.Doc */ +.highlight .string.double { color: #BA2121 } /* Literal.String.Double */ +.highlight .string.escape { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ +.highlight .string.heredoc { color: #BA2121 } /* Literal.String.Heredoc */ +.highlight .string.interpol { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ +.highlight .string.other { color: #008000 } /* Literal.String.Other */ +.highlight .string.regex { color: #BB6688 } /* Literal.String.Regex */ +.highlight .string.single { color: #BA2121 } /* Literal.String.Single */ +.highlight .string.symbol { color: #19177C } /* Literal.String.Symbol */ +.highlight .name.builtin.pseudo { color: #008000 } /* Name.Builtin.Pseudo */ +.highlight .name.variable.class { color: #19177C } /* Name.Variable.Class */ +.highlight .name.variable.global { color: #19177C } /* Name.Variable.Global */ +.highlight .name.variable.instance { color: #19177C } /* Name.Variable.Instance */ +.highlight .number.integer.long { color: #666666 } /* Literal.Number.Integer.Long */ +.highlight .lineno { color: #000000; background-color: #dddddd; } diff --git a/templates/pages/pep-page.html b/templates/pages/pep-page.html index 65e045612..e9b097498 100644 --- a/templates/pages/pep-page.html +++ b/templates/pages/pep-page.html @@ -25,6 +25,7 @@ {% endblock %} . {% block content %} +