pypage


Namepypage JSON
Version 2.0.8 PyPI version JSON
download
home_pagehttps://github.com/arjun-menon/pypage
SummaryLight-weight Python Templating Engine
upload_time2023-09-24 17:12:53
maintainer
docs_urlNone
authorArjun G. Menon
requires_python
licenseApache
keywords templating enigne text processing static generator
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            pypage |pypi| |docs|
====================

pypage is a document template engine for Python programmers with a
short learning curve.

**Why use pypage?**

-  Easy to pick up. Syntax similar to Python's.
-  You need an eval-based template engine.

pypage supports Python 3.x and 2.7, and has been on CPython and PyPy.

**What does it look like?**

.. code:: html

    Some fruits with the character `r` in their name:
    <ul>
      {% for fruit in ['Apple', 'Strawberry', 'Orange', 'Raspberry'] if 'r' in fruit %}
        <li>
          {{ fruit }}
        </li>
      {% endfor %}
    </ul>

**Installation**

You can `install <https://docs.python.org/3/installing/>`_ pypage easily with `pip <https://pip.pypa.io/en/stable/>`_:

.. code::

    pip install pypage

Try running ``pypage -h`` to see the command-line options available.

.. contents:: **Table of Contents**


Embedding Code
--------------

In order to embed code in a document, you wrap Python code with ``{{``
and ``}}``. The ``{{ ... }}`` constructs are called **code tags**. There
are two kinds of code tags: *inline* and *multiline*.

Inline Code Tags
^^^^^^^^^^^^^^^^

Inline code tags occur entirely on the same line, i.e. the closing
``}}`` appears on the same line as the opening ``{{``. Here is an
example of an inline code tag:

.. code:: python

    There are {{ 5 + 2 }} days in a week.

The above, when processed by pypage, yields:

::

    There are 7 days in a week.

The Python ``eval`` statement is used to execute the code in an inline
code tag. The result of the expression evaluation is converted into a
string (with ``str``) and the code tag is replaced with it.

Multiline Code Tags
^^^^^^^^^^^^^^^^^^^

Multiline code tags span multiple lines. The presence of one or more
newline (``\n``) characters between the ``{{`` and ``}}`` distinguishes
it from an inline code tag. Here's an example:

.. code:: python

    {{
        x = 5
        y = 2

        write("There are", x + y, "days in a week.")
    }}

The Python ``exec`` function is used to execute the code in a multiline
code tag.

Why have distinct inline code tags? It's easier to write ``{{x}}`` than
to write ``{{ write(x) }}``. Many a time, all we need to do is inject
the value of a variable at a specific location in the document.

Execution Environment
^^^^^^^^^^^^^^^^^^^^^

All code is executed in a shared common environment. I.e., the ``locals`` and
``globals`` passed into ``eval`` and ``exec`` is a single shared dictionary,
for all code tags in the same file.

As such, a variable instantiated in a code tag at the
beginning of the document, will be available to all other code tags in
the document. When pypage is invoked as library, an initial seed
environment consisting of a Python dictionary mapping variable names to
values, can be provided.

The write function
''''''''''''''''''

A ``write`` function similar to the Python 3's ``print`` function
is accessible from both kinds of code tags. It writes text into
the document that substitutes/replaces the code tag it's used in.

.. code:: python

    write(*object, sep=' ', end='\n')

Objects passed to it are stringified with ``str``, concatenated together
with ``sep``, and terminated with ``end``. The outputs of multiple calls
to ``write`` in a code tag are concatenated together, and the resulting
final output is injected in place of the code tag.

If ``write`` is called from an inline code tag, the result of evaluating
the expression (a ``None``, since ``write`` will return a ``None``) is
ignored, and the output of the ``write`` call is used instead.

Block Tags
----------

Block tags simplify certain tasks that would otherwise be cumbersome and
ugly if done exclusively with code tags. One of the things it lets you
do is wrap part of your page in an `if/else
conditional <http://en.wikipedia.org/wiki/Conditional_(computer_programming)>`__,
or a `for/while
loop <http://en.wikipedia.org/wiki/Control_flow#Loops>`__.

Here's an example of the ``for`` block tag:

.. code:: python

    {% for i in range(10) %}
        The square of {{i}} is {{i*i}}.
    {% %}

A block tag begins with ``{% tag_name ... %}`` and ends with ``{% %}``.
Optionally, the end ``{% %}`` can be of the form ``{% endtag_name %}``
(i.e. prepend the ``tag_name`` with ``end``), which in the above example
would be ``{% endfor %}``).

Conditional Blocks
^^^^^^^^^^^^^^^^^^

It's best to explain this with an example:

.. code:: python

    Hey,
    {{
      import random
      # Randomly pick a greeting
      greeting = random.randint(1,4)
    }}
    {% if greeting == 1 %}
      Hello
    {% elif greeting == 2 %}
      Bonjour
    {% elif greeting == 3 %}
      Hey
    {% else %}
      Hi
    {% %}

When the above template is run, the resulting page will contain a
randomly chosen greeting. As is evident, pypage syntax for if/elif/else
conditions closely mirrors Python's. The terminal ``{% %}`` can be
replaced with an ``{% endif %}`` with no change in meaning (as with any
block tag).

For Loops
^^^^^^^^^

Let's start with a simple example:

.. code:: python

    {% for vowel in ['a', 'e', 'i', 'o', 'u'] %}{{vowel}} {% %}

This will print out the vowels with a space after every character.

Now that's an ordinary for loop. pypage permits for loops that are more
expressive than traditional Python for loops, by leveraging Python's
*generator expressions*.

Here's an example of something that would be impossible to do in Python
(with a regular for loop):

.. code:: python

    {% for x in [1,2,3] for y in ['a','b','c'] %}
        {{x}} ~ {{y}}
    {%%}

The above loop would result in:

::

    1 ~ a
    1 ~ b
    1 ~ c
    2 ~ a
    2 ~ b
    2 ~ c
    3 ~ a
    3 ~ b
    3 ~ c

*Internally*, pypage morphs the expression
``for x in [1,2,3] for y in ['a','b','c']`` into the generator
expression ``(x, y) for x in [1,2,3] for y in ['a','b','c']``. It
exposes the the loop variables ``x`` and ``y`` by injecting them into
your namespace.

*Note:* Injected loop variables replace variables with the same name for
the duration of the loop. After the loop, the old variables with the
identical names are restored (pypage backs them up).

While Loops
^^^^^^^^^^^

A while loops looks like ``{{% while condition %}} ... {{% %}``, where
``condition`` can be any Python expression. Here's an example:

.. code:: python

    {{
        i = 10
        j = 20
    }}
    Numbers from {{i}} to {{j}}:
    {% while i <= j %}
    {{
        write(str(i))
        i += 1
    }}
    {% %}

This would simply list the numbers from 10 to 20.

dofirst Loops
'''''''''''''

.. code:: python

    {% while dofirst False %}
    That's all, folks!
    {%%}

Adding a ``dofirst`` right after the ``while`` and before the expression
ensures that the loop is run *at least once*, before the condition is
evaluated.

Long Loops
''''''''''

If a loop runs *for more than 2 seconds*, pypage stops executing it, and
writes an error message to ``stdout`` saying that the loop had been
terminated. As pypage is mostly intended to be used as a templatig
language, loops generally shouldn't be running for longer than two
seconds, and this timeout was added to make it easier to catch accidental
infinite loops. If you actually need a loop to run for longer than 2
seoncds, you can add the keyword ``slow`` right after the condition expression
(``{{% while condition slow %}}``), and that would suppress this 2-second timeout.

Capture Blocks
^^^^^^^^^^^^^^

You can capture the output of part of your page using the ``capture``
tag:

.. code:: python

    {% capture x %}
      hello {{"bob"}}
    {% %}

The above tag will not yield any output, but rather a new variable ``x``
will be created that captures the output of everything enclosed by it
(which in this case is ``"hello bob"``).

Finer Details
-------------

Inheritance (with inject and exists)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The approach taken by pypage toward template inheritance is quite distinct from that of other
templating engines (`like Jinja's <http://jinja.pocoo.org/docs/2.10/templates/#template-inheritance>`_).
It's a lot simpler. You call a pypage-provided function ``inject`` with the path of a *pypage template* you want
to inject (i.e. "*extend*" in Jinja parlance), and pypage will process that template under the current scope (with all
previously defined variables being available to the injected template), and the ``inject`` function will return its output.

A base template could look like this:

.. code:: html

    <html>
    <head>
        <title>
            {% if exists('title') %}
            {{ title }}
            {% else %}
            No title
            {% %}
        </title>
    </head>
    <body>
    {{ body }}
    </body>
    </html>

A derived templates only needs to define ``body`` and optionally ``title``, to "extend" the template above.

.. code::

    {% capture body %}
    The HTML body content would go in here.
    {% %}
    {{ inject('...path to the base template...') }}

We didn't specify a ``title`` above, but if we wanted to, we'd just need to make sure it was defined before ``inject``
was called. The base template checks whether a ``title`` variable exists by calling the function ``exists``. As is obvious,
the ``exists`` function simply takes a variable name as a string, and returns a boolean indicating whether the variable
exists in the scope.

This approach to inheritance is explicit and easy-to-grasp. Rather than have complex inheritance rules, with a default
block definition that is optionally overridden by a derived template, we make things more explicit by using conditionals
for cases where we want to provide a default/fallback definition. We error out if a definition is expected to be provided,
and is not present. The output of the "dervied" template is clear and obvious, with this approach.

The include function
''''''''''''''''''''

If you want to include (as in, substitute) a file directly without processing it with pypage, you can use the
``include`` function. It functions like the ``inject`` function, taking the path to a file as argument, and
returning the contents of the file unprocessed.

Comments
^^^^^^^^

Comment Tags
''''''''''''

Anything bounded by ``{#`` and ``#}`` will be omitted from the output.
For example:

.. code:: html

    <p>
      Lorem ipsum dolor sit amet
      {#
        <ul>
            Non sequitur
        </ul>
      #}
      consectetur adipisicing elit
    </p>

Comment Blocks
''''''''''''''

You can also easily comment an existing block, by simply placing the word ``comment`` in front of it:

.. code:: html

    <p>
      Lorem ipsum dolor sit amet
        {% comment for i in range(10) %}
            N = {{i}}
        {% %}
      consectetur adipisicing elit
    </p>

The ``comment`` keyword before the ``for`` above results in the entire block
being commented out and omitted from the output.

Whitespace & Indentation
^^^^^^^^^^^^^^^^^^^^^^^^

Whitespace Removal
''''''''''''''''''

If a block tag is on a line by itself, surrounded only by whitespace,
then that whitespace is automatically excluded from the output. This
allows you indent your block tags without worrying about excess
whitespace in the generated document.

Automatic Indentation
'''''''''''''''''''''

pypage smartly handles indentation for you. In a multi-line code tag, if
you consistently indent your Python code with a specific amount of
whitespace, that indentation will be stripped off before executing the
code block (as Python is indentation-sensitive), and the resulting
output of that code block will be re-indented with same whitespace that
the initial code block was.

The whitespace preceding the second line of code determines the
peripheral indentation for the entiee block. All subsequent lines (after
second) must begin with exact same whitespace that preceded the second
line, or be an empty line.

For example:

.. code:: html

    <p>
      Lorem ipsum dolor sit amet
        <ul>
          {{
            def foo():
              write("Hello!")
            foo()
          }}
        </ul>
      consectetur adipisicing elit
    </p>

would produce the following output:

.. code:: html

    <p>
      Lorem ipsum dolor sit amet
        <ul>
            Hello!
        </ul>
      consectetur adipisicing elit
    </p>

Note that the ``Hello!`` was indented with same whitespace that the code
in the code block was.

pypage automatically intends the output of a multi-line tag to match the
indentation level of the code tag. The number of whitespace characters
at the beginning of the second line of the code block determines the
indentation level for the whole block. All lines of code following the
second line must at least have the same level of indentation as the
second line (or else, a PypageSyntaxError exception will be thrown).

License
^^^^^^^

`Apache License Version
2.0 <https://www.apache.org/licenses/LICENSE-2.0>`__

.. |pypi| image:: https://badge.fury.io/py/pypage.svg
   :target: https://pypi.python.org/pypi/pypage
.. |docs| image:: https://readthedocs.org/projects/pypage/badge/?version=latest&style=flat
   :target: https://pypage.readthedocs.io/en/latest/

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/arjun-menon/pypage",
    "name": "pypage",
    "maintainer": "",
    "docs_url": null,
    "requires_python": "",
    "maintainer_email": "",
    "keywords": "templating enigne text processing static generator",
    "author": "Arjun G. Menon",
    "author_email": "contact@arjungmenon.com",
    "download_url": "https://files.pythonhosted.org/packages/16/08/d718efffcc0ff8d476be03525cf47dcf5be42f182a69a53d73b7a268ac90/pypage-2.0.8.tar.gz",
    "platform": null,
    "description": "pypage |pypi| |docs|\n====================\n\npypage is a document template engine for Python programmers with a\nshort learning curve.\n\n**Why use pypage?**\n\n-  Easy to pick up. Syntax similar to Python's.\n-  You need an eval-based template engine.\n\npypage supports Python 3.x and 2.7, and has been on CPython and PyPy.\n\n**What does it look like?**\n\n.. code:: html\n\n    Some fruits with the character `r` in their name:\n    <ul>\n      {% for fruit in ['Apple', 'Strawberry', 'Orange', 'Raspberry'] if 'r' in fruit %}\n        <li>\n          {{ fruit }}\n        </li>\n      {% endfor %}\n    </ul>\n\n**Installation**\n\nYou can `install <https://docs.python.org/3/installing/>`_ pypage easily with `pip <https://pip.pypa.io/en/stable/>`_:\n\n.. code::\n\n    pip install pypage\n\nTry running ``pypage -h`` to see the command-line options available.\n\n.. contents:: **Table of Contents**\n\n\nEmbedding Code\n--------------\n\nIn order to embed code in a document, you wrap Python code with ``{{``\nand ``}}``. The ``{{ ... }}`` constructs are called **code tags**. There\nare two kinds of code tags: *inline* and *multiline*.\n\nInline Code Tags\n^^^^^^^^^^^^^^^^\n\nInline code tags occur entirely on the same line, i.e. the closing\n``}}`` appears on the same line as the opening ``{{``. Here is an\nexample of an inline code tag:\n\n.. code:: python\n\n    There are {{ 5 + 2 }} days in a week.\n\nThe above, when processed by pypage, yields:\n\n::\n\n    There are 7 days in a week.\n\nThe Python ``eval`` statement is used to execute the code in an inline\ncode tag. The result of the expression evaluation is converted into a\nstring (with ``str``) and the code tag is replaced with it.\n\nMultiline Code Tags\n^^^^^^^^^^^^^^^^^^^\n\nMultiline code tags span multiple lines. The presence of one or more\nnewline (``\\n``) characters between the ``{{`` and ``}}`` distinguishes\nit from an inline code tag. Here's an example:\n\n.. code:: python\n\n    {{\n        x = 5\n        y = 2\n\n        write(\"There are\", x + y, \"days in a week.\")\n    }}\n\nThe Python ``exec`` function is used to execute the code in a multiline\ncode tag.\n\nWhy have distinct inline code tags? It's easier to write ``{{x}}`` than\nto write ``{{ write(x) }}``. Many a time, all we need to do is inject\nthe value of a variable at a specific location in the document.\n\nExecution Environment\n^^^^^^^^^^^^^^^^^^^^^\n\nAll code is executed in a shared common environment. I.e., the ``locals`` and\n``globals`` passed into ``eval`` and ``exec`` is a single shared dictionary,\nfor all code tags in the same file.\n\nAs such, a variable instantiated in a code tag at the\nbeginning of the document, will be available to all other code tags in\nthe document. When pypage is invoked as library, an initial seed\nenvironment consisting of a Python dictionary mapping variable names to\nvalues, can be provided.\n\nThe write function\n''''''''''''''''''\n\nA ``write`` function similar to the Python 3's ``print`` function\nis accessible from both kinds of code tags. It writes text into\nthe document that substitutes/replaces the code tag it's used in.\n\n.. code:: python\n\n    write(*object, sep=' ', end='\\n')\n\nObjects passed to it are stringified with ``str``, concatenated together\nwith ``sep``, and terminated with ``end``. The outputs of multiple calls\nto ``write`` in a code tag are concatenated together, and the resulting\nfinal output is injected in place of the code tag.\n\nIf ``write`` is called from an inline code tag, the result of evaluating\nthe expression (a ``None``, since ``write`` will return a ``None``) is\nignored, and the output of the ``write`` call is used instead.\n\nBlock Tags\n----------\n\nBlock tags simplify certain tasks that would otherwise be cumbersome and\nugly if done exclusively with code tags. One of the things it lets you\ndo is wrap part of your page in an `if/else\nconditional <http://en.wikipedia.org/wiki/Conditional_(computer_programming)>`__,\nor a `for/while\nloop <http://en.wikipedia.org/wiki/Control_flow#Loops>`__.\n\nHere's an example of the ``for`` block tag:\n\n.. code:: python\n\n    {% for i in range(10) %}\n        The square of {{i}} is {{i*i}}.\n    {% %}\n\nA block tag begins with ``{% tag_name ... %}`` and ends with ``{% %}``.\nOptionally, the end ``{% %}`` can be of the form ``{% endtag_name %}``\n(i.e. prepend the ``tag_name`` with ``end``), which in the above example\nwould be ``{% endfor %}``).\n\nConditional Blocks\n^^^^^^^^^^^^^^^^^^\n\nIt's best to explain this with an example:\n\n.. code:: python\n\n    Hey,\n    {{\n      import random\n      # Randomly pick a greeting\n      greeting = random.randint(1,4)\n    }}\n    {% if greeting == 1 %}\n      Hello\n    {% elif greeting == 2 %}\n      Bonjour\n    {% elif greeting == 3 %}\n      Hey\n    {% else %}\n      Hi\n    {% %}\n\nWhen the above template is run, the resulting page will contain a\nrandomly chosen greeting. As is evident, pypage syntax for if/elif/else\nconditions closely mirrors Python's. The terminal ``{% %}`` can be\nreplaced with an ``{% endif %}`` with no change in meaning (as with any\nblock tag).\n\nFor Loops\n^^^^^^^^^\n\nLet's start with a simple example:\n\n.. code:: python\n\n    {% for vowel in ['a', 'e', 'i', 'o', 'u'] %}{{vowel}} {% %}\n\nThis will print out the vowels with a space after every character.\n\nNow that's an ordinary for loop. pypage permits for loops that are more\nexpressive than traditional Python for loops, by leveraging Python's\n*generator expressions*.\n\nHere's an example of something that would be impossible to do in Python\n(with a regular for loop):\n\n.. code:: python\n\n    {% for x in [1,2,3] for y in ['a','b','c'] %}\n        {{x}} ~ {{y}}\n    {%%}\n\nThe above loop would result in:\n\n::\n\n    1 ~ a\n    1 ~ b\n    1 ~ c\n    2 ~ a\n    2 ~ b\n    2 ~ c\n    3 ~ a\n    3 ~ b\n    3 ~ c\n\n*Internally*, pypage morphs the expression\n``for x in [1,2,3] for y in ['a','b','c']`` into the generator\nexpression ``(x, y) for x in [1,2,3] for y in ['a','b','c']``. It\nexposes the the loop variables ``x`` and ``y`` by injecting them into\nyour namespace.\n\n*Note:* Injected loop variables replace variables with the same name for\nthe duration of the loop. After the loop, the old variables with the\nidentical names are restored (pypage backs them up).\n\nWhile Loops\n^^^^^^^^^^^\n\nA while loops looks like ``{{% while condition %}} ... {{% %}``, where\n``condition`` can be any Python expression. Here's an example:\n\n.. code:: python\n\n    {{\n        i = 10\n        j = 20\n    }}\n    Numbers from {{i}} to {{j}}:\n    {% while i <= j %}\n    {{\n        write(str(i))\n        i += 1\n    }}\n    {% %}\n\nThis would simply list the numbers from 10 to 20.\n\ndofirst Loops\n'''''''''''''\n\n.. code:: python\n\n    {% while dofirst False %}\n    That's all, folks!\n    {%%}\n\nAdding a ``dofirst`` right after the ``while`` and before the expression\nensures that the loop is run *at least once*, before the condition is\nevaluated.\n\nLong Loops\n''''''''''\n\nIf a loop runs *for more than 2 seconds*, pypage stops executing it, and\nwrites an error message to ``stdout`` saying that the loop had been\nterminated. As pypage is mostly intended to be used as a templatig\nlanguage, loops generally shouldn't be running for longer than two\nseconds, and this timeout was added to make it easier to catch accidental\ninfinite loops. If you actually need a loop to run for longer than 2\nseoncds, you can add the keyword ``slow`` right after the condition expression\n(``{{% while condition slow %}}``), and that would suppress this 2-second timeout.\n\nCapture Blocks\n^^^^^^^^^^^^^^\n\nYou can capture the output of part of your page using the ``capture``\ntag:\n\n.. code:: python\n\n    {% capture x %}\n      hello {{\"bob\"}}\n    {% %}\n\nThe above tag will not yield any output, but rather a new variable ``x``\nwill be created that captures the output of everything enclosed by it\n(which in this case is ``\"hello bob\"``).\n\nFiner Details\n-------------\n\nInheritance (with inject and exists)\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe approach taken by pypage toward template inheritance is quite distinct from that of other\ntemplating engines (`like Jinja's <http://jinja.pocoo.org/docs/2.10/templates/#template-inheritance>`_).\nIt's a lot simpler. You call a pypage-provided function ``inject`` with the path of a *pypage template* you want\nto inject (i.e. \"*extend*\" in Jinja parlance), and pypage will process that template under the current scope (with all\npreviously defined variables being available to the injected template), and the ``inject`` function will return its output.\n\nA base template could look like this:\n\n.. code:: html\n\n    <html>\n    <head>\n        <title>\n            {% if exists('title') %}\n            {{ title }}\n            {% else %}\n            No title\n            {% %}\n        </title>\n    </head>\n    <body>\n    {{ body }}\n    </body>\n    </html>\n\nA derived templates only needs to define ``body`` and optionally ``title``, to \"extend\" the template above.\n\n.. code::\n\n    {% capture body %}\n    The HTML body content would go in here.\n    {% %}\n    {{ inject('...path to the base template...') }}\n\nWe didn't specify a ``title`` above, but if we wanted to, we'd just need to make sure it was defined before ``inject``\nwas called. The base template checks whether a ``title`` variable exists by calling the function ``exists``. As is obvious,\nthe ``exists`` function simply takes a variable name as a string, and returns a boolean indicating whether the variable\nexists in the scope.\n\nThis approach to inheritance is explicit and easy-to-grasp. Rather than have complex inheritance rules, with a default\nblock definition that is optionally overridden by a derived template, we make things more explicit by using conditionals\nfor cases where we want to provide a default/fallback definition. We error out if a definition is expected to be provided,\nand is not present. The output of the \"dervied\" template is clear and obvious, with this approach.\n\nThe include function\n''''''''''''''''''''\n\nIf you want to include (as in, substitute) a file directly without processing it with pypage, you can use the\n``include`` function. It functions like the ``inject`` function, taking the path to a file as argument, and\nreturning the contents of the file unprocessed.\n\nComments\n^^^^^^^^\n\nComment Tags\n''''''''''''\n\nAnything bounded by ``{#`` and ``#}`` will be omitted from the output.\nFor example:\n\n.. code:: html\n\n    <p>\n      Lorem ipsum dolor sit amet\n      {#\n        <ul>\n            Non sequitur\n        </ul>\n      #}\n      consectetur adipisicing elit\n    </p>\n\nComment Blocks\n''''''''''''''\n\nYou can also easily comment an existing block, by simply placing the word ``comment`` in front of it:\n\n.. code:: html\n\n    <p>\n      Lorem ipsum dolor sit amet\n        {% comment for i in range(10) %}\n            N = {{i}}\n        {% %}\n      consectetur adipisicing elit\n    </p>\n\nThe ``comment`` keyword before the ``for`` above results in the entire block\nbeing commented out and omitted from the output.\n\nWhitespace & Indentation\n^^^^^^^^^^^^^^^^^^^^^^^^\n\nWhitespace Removal\n''''''''''''''''''\n\nIf a block tag is on a line by itself, surrounded only by whitespace,\nthen that whitespace is automatically excluded from the output. This\nallows you indent your block tags without worrying about excess\nwhitespace in the generated document.\n\nAutomatic Indentation\n'''''''''''''''''''''\n\npypage smartly handles indentation for you. In a multi-line code tag, if\nyou consistently indent your Python code with a specific amount of\nwhitespace, that indentation will be stripped off before executing the\ncode block (as Python is indentation-sensitive), and the resulting\noutput of that code block will be re-indented with same whitespace that\nthe initial code block was.\n\nThe whitespace preceding the second line of code determines the\nperipheral indentation for the entiee block. All subsequent lines (after\nsecond) must begin with exact same whitespace that preceded the second\nline, or be an empty line.\n\nFor example:\n\n.. code:: html\n\n    <p>\n      Lorem ipsum dolor sit amet\n        <ul>\n          {{\n            def foo():\n              write(\"Hello!\")\n            foo()\n          }}\n        </ul>\n      consectetur adipisicing elit\n    </p>\n\nwould produce the following output:\n\n.. code:: html\n\n    <p>\n      Lorem ipsum dolor sit amet\n        <ul>\n            Hello!\n        </ul>\n      consectetur adipisicing elit\n    </p>\n\nNote that the ``Hello!`` was indented with same whitespace that the code\nin the code block was.\n\npypage automatically intends the output of a multi-line tag to match the\nindentation level of the code tag. The number of whitespace characters\nat the beginning of the second line of the code block determines the\nindentation level for the whole block. All lines of code following the\nsecond line must at least have the same level of indentation as the\nsecond line (or else, a PypageSyntaxError exception will be thrown).\n\nLicense\n^^^^^^^\n\n`Apache License Version\n2.0 <https://www.apache.org/licenses/LICENSE-2.0>`__\n\n.. |pypi| image:: https://badge.fury.io/py/pypage.svg\n   :target: https://pypi.python.org/pypi/pypage\n.. |docs| image:: https://readthedocs.org/projects/pypage/badge/?version=latest&style=flat\n   :target: https://pypage.readthedocs.io/en/latest/\n",
    "bugtrack_url": null,
    "license": "Apache",
    "summary": "Light-weight Python Templating Engine",
    "version": "2.0.8",
    "project_urls": {
        "Download": "https://github.com/arjun-menon/pypage/archive/v2.0.8.tar.gz",
        "Homepage": "https://github.com/arjun-menon/pypage"
    },
    "split_keywords": [
        "templating",
        "enigne",
        "text",
        "processing",
        "static",
        "generator"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "0d0a8ba2898e966714a93fd18f954f43d49957db7730db47bac217b4a49c2c8c",
                "md5": "d0f10f8fa3988603facbf78b27ceede1",
                "sha256": "91cf854856ef93f747520a3d471b4f815a50e1d60d0ba1155783b049a46a4444"
            },
            "downloads": -1,
            "filename": "pypage-2.0.8-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "d0f10f8fa3988603facbf78b27ceede1",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": null,
            "size": 18340,
            "upload_time": "2023-09-24T17:12:51",
            "upload_time_iso_8601": "2023-09-24T17:12:51.843411Z",
            "url": "https://files.pythonhosted.org/packages/0d/0a/8ba2898e966714a93fd18f954f43d49957db7730db47bac217b4a49c2c8c/pypage-2.0.8-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "1608d718efffcc0ff8d476be03525cf47dcf5be42f182a69a53d73b7a268ac90",
                "md5": "88af42196dc526140e7342094329194d",
                "sha256": "1ec4c3d4ba3a02f0996bc70aff0cc8dbfd26523942d7b0f2c8e775ea2ba8d267"
            },
            "downloads": -1,
            "filename": "pypage-2.0.8.tar.gz",
            "has_sig": false,
            "md5_digest": "88af42196dc526140e7342094329194d",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 18620,
            "upload_time": "2023-09-24T17:12:53",
            "upload_time_iso_8601": "2023-09-24T17:12:53.283463Z",
            "url": "https://files.pythonhosted.org/packages/16/08/d718efffcc0ff8d476be03525cf47dcf5be42f182a69a53d73b7a268ac90/pypage-2.0.8.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-09-24 17:12:53",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "arjun-menon",
    "github_project": "pypage",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "pypage"
}
        
Elapsed time: 0.13614s