pypage 2.0.9__py3-none-any.whl → 2.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pypage-2.0.9.dist-info → pypage-2.2.0.dist-info}/METADATA +37 -4
- pypage-2.2.0.dist-info/RECORD +7 -0
- {pypage-2.0.9.dist-info → pypage-2.2.0.dist-info}/WHEEL +1 -1
- pypage.py +65 -6
- pypage-2.0.9.dist-info/RECORD +0 -7
- {pypage-2.0.9.dist-info → pypage-2.2.0.dist-info}/entry_points.txt +0 -0
- {pypage-2.0.9.dist-info → pypage-2.2.0.dist-info/licenses}/LICENSE.txt +0 -0
- {pypage-2.0.9.dist-info → pypage-2.2.0.dist-info}/top_level.txt +0 -0
@@ -1,9 +1,9 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: pypage
|
3
|
-
Version: 2.0
|
4
|
-
Summary:
|
3
|
+
Version: 2.2.0
|
4
|
+
Summary: Light-weight Python Templating Engine
|
5
5
|
Home-page: https://github.com/arjun-menon/pypage
|
6
|
-
Download-URL: https://github.com/arjun-menon/pypage/archive/v2.0.
|
6
|
+
Download-URL: https://github.com/arjun-menon/pypage/archive/v2.2.0.tar.gz
|
7
7
|
Author: Arjun G. Menon
|
8
8
|
Author-email: contact@arjungmenon.com
|
9
9
|
License: Apache-2.0
|
@@ -24,6 +24,16 @@ Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
24
24
|
Classifier: Programming Language :: Python :: 2
|
25
25
|
Classifier: Programming Language :: Python :: 3
|
26
26
|
License-File: LICENSE.txt
|
27
|
+
Dynamic: author
|
28
|
+
Dynamic: author-email
|
29
|
+
Dynamic: classifier
|
30
|
+
Dynamic: description
|
31
|
+
Dynamic: download-url
|
32
|
+
Dynamic: home-page
|
33
|
+
Dynamic: keywords
|
34
|
+
Dynamic: license
|
35
|
+
Dynamic: license-file
|
36
|
+
Dynamic: summary
|
27
37
|
|
28
38
|
PyPage |pypi| |docs| |test|
|
29
39
|
===========================
|
@@ -62,6 +72,29 @@ You can `install <https://docs.python.org/3/installing/>`_ PyPage easily with `p
|
|
62
72
|
|
63
73
|
Try running ``pypage -h`` to see the command-line options available.
|
64
74
|
|
75
|
+
|
76
|
+
**Why another templating language?**
|
77
|
+
|
78
|
+
PyPage is lets you embed Python code easily and flexibly in textual documents (such as Markdown_, HTML, reStructuredText_, plain text, etc). It lets you construct powerful programmatically-generated documents by embedding Python code in an elegant and flexible manner. Its syntax is similar to and partially inspired by the templating languages Jinja_ and Liquid_.
|
79
|
+
|
80
|
+
While there are many templating engines out there, the primarily advantage of PyPage is the fact that its syntax is very close to Python's, and therefore the learning curve is very short for Python programmers.
|
81
|
+
|
82
|
+
Rather than create a new mini domain-specific language for constructs such as ``for`` and ``if``, PyPage does a teeny tiny bit of obvious string manipulation, and passes your logical directives unaltered to the Python interpreter. As such, PyPage inherits Python's syntax for the most part. For example, ``for`` loops in PyPage get converted into Python's generator expressions. The ``for`` loop in a Python generator expression (or list comprehension) is far more powerful than its regular ``for`` loop. This means that PyPage ``for`` loops are richer and more expressive than you'd otherwise expect, while the learning curve is nearly non-existent.
|
83
|
+
|
84
|
+
The primary disadvantage of using PyPage instead of a templating engine like Liquid is that PyPage does not operate on a restricted non-Turing-complete subset of programming languages, as Liquid for instance does. Liquid allows untrusted users to write and upload their own templates, because the expressiveness of Liquid is limited such that there is an implicit guarantee that the template will be processed in a reasonable (probably linear) amount of time using a reasonable amount of system resources. As such, Liquid's templating language is rather limited -- it offers a limited number of pre-defined functions/filters, and the overall flexibility of the language has been constrained in order to guarantee termination in a reasonable amount of time.
|
85
|
+
|
86
|
+
PyPage, on the other hand gives the template writer full unfettered access to the Python interpreter. As such, PyPage is meant only for use in trusted contexts (or containers), and in some ways it's similar to PHP in that a you're mixing a full-blown programming language (Python) and text that could be HTML.
|
87
|
+
|
88
|
+
This brings us to another topic: mixing code and UI. It is generally frowned upon to mix logic/code and the UI (or "view"). So it is good practise to not do any intelligent processing within your PyPage template. Instead, you can do it in a separate program, and pass an *environment* containing the results, to PyPage. An environment is a dictionary of variables that is passed to Python's ``exec``, and is therefore accessible from all of the code in the PyPage template. From within your template you can focus solely on how to transform these input variables into the HTML/rST/other page you're building.
|
89
|
+
|
90
|
+
A pleasant aspect of PyPage, in comparison to other templating languages is that you don't have to learn much new syntax. It's probably the easiest to learn and most *flexible* templating language out there. It is highly flexible because of the plethora of easy-to-use powerful constructs that PyPage offers.
|
91
|
+
|
92
|
+
.. _Markdown: https://en.wikipedia.org/wiki/Markdown
|
93
|
+
.. _reStructuredText: http://docutils.sourceforge.net/docs/user/rst/quickref.html
|
94
|
+
.. _Liquid: https://github.com/Shopify/liquid/wiki/Liquid-for-Designers
|
95
|
+
.. _Jinja: https://github.com/pallets/jinja
|
96
|
+
|
97
|
+
|
65
98
|
.. contents:: **Table of Contents**
|
66
99
|
|
67
100
|
|
@@ -0,0 +1,7 @@
|
|
1
|
+
pypage.py,sha256=s6OuUUo_nZjpS9aZFaXMBXlUvBUDSiF_3DzIoaE2uW4,30890
|
2
|
+
pypage-2.2.0.dist-info/licenses/LICENSE.txt,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
3
|
+
pypage-2.2.0.dist-info/METADATA,sha256=GDhXXdpQcCAnSziU-5_Z9P1QujNlnSFUAY9sXggefWU,18347
|
4
|
+
pypage-2.2.0.dist-info/WHEEL,sha256=lTU6B6eIfYoiQJTZNc-fyaR6BpL6ehTzU3xGYxn2n8k,91
|
5
|
+
pypage-2.2.0.dist-info/entry_points.txt,sha256=iHPN6EfIUdv-njh8GV04yDTzkcCLGNcXvdbA43s_8mo,39
|
6
|
+
pypage-2.2.0.dist-info/top_level.txt,sha256=8AAB15dVQEt9xcwH_eLcVUZEbFlc__81RA5mOrYtJiQ,7
|
7
|
+
pypage-2.2.0.dist-info/RECORD,,
|
pypage.py
CHANGED
@@ -18,7 +18,7 @@
|
|
18
18
|
from __future__ import print_function
|
19
19
|
import string, sys, time, os, json
|
20
20
|
|
21
|
-
pypage_version = '2.0
|
21
|
+
pypage_version = '2.2.0'
|
22
22
|
|
23
23
|
class RootNode(object):
|
24
24
|
"""
|
@@ -62,7 +62,7 @@ class TagNode(object):
|
|
62
62
|
open_delim: string containig the opening delimiter
|
63
63
|
close_delim: string containig the closing delimiter
|
64
64
|
"""
|
65
|
-
escape_delims = {'
|
65
|
+
escape_delims = {('\\' + '{'):'{', ('\\' + '}'):'}'}
|
66
66
|
|
67
67
|
def __init__(self, loc):
|
68
68
|
self.src = str()
|
@@ -303,6 +303,51 @@ class WhileBlock(BlockTag):
|
|
303
303
|
|
304
304
|
return output
|
305
305
|
|
306
|
+
class DefBlock(BlockTag):
|
307
|
+
"""
|
308
|
+
The function tag. {% def foo a b c %}
|
309
|
+
"""
|
310
|
+
tag_startswith = 'def '
|
311
|
+
|
312
|
+
@staticmethod
|
313
|
+
def identify(src):
|
314
|
+
return src.strip().startswith(DefBlock.tag_startswith)
|
315
|
+
|
316
|
+
def __init__(self, node):
|
317
|
+
super(DefBlock, self).__init__(node.loc)
|
318
|
+
self.src = node.src.strip()
|
319
|
+
|
320
|
+
name_and_args = self.src[len(self.tag_startswith):].strip().split()
|
321
|
+
for name in name_and_args:
|
322
|
+
if not isidentifier(name):
|
323
|
+
raise InvalidDefBlockFunctionOrArgName(name)
|
324
|
+
|
325
|
+
self.funcname = name_and_args[0]
|
326
|
+
self.argnames = name_and_args[1:]
|
327
|
+
|
328
|
+
def run(self, pe):
|
329
|
+
def invoke(args):
|
330
|
+
if len(args) != len(self.argnames):
|
331
|
+
raise InvalidDefBlockMismatchingArgCount(self.argnames, args)
|
332
|
+
|
333
|
+
conflicting = set(pe.env.keys()) & set(self.argnames)
|
334
|
+
backup = { x : pe.env[x] for x in conflicting }
|
335
|
+
|
336
|
+
local_vars = dict(zip(self.argnames, args))
|
337
|
+
pe.env.update(local_vars)
|
338
|
+
|
339
|
+
output = exec_tree(self, pe)
|
340
|
+
|
341
|
+
for argname in self.argnames:
|
342
|
+
if argname in pe.env:
|
343
|
+
del pe.env[argname]
|
344
|
+
|
345
|
+
pe.env.update(backup)
|
346
|
+
return output
|
347
|
+
|
348
|
+
pe.env[self.funcname] = lambda *args: invoke(args)
|
349
|
+
return ""
|
350
|
+
|
306
351
|
class CaptureBlock(BlockTag):
|
307
352
|
"""
|
308
353
|
Capture all content within this tag, and bind it to a variable.
|
@@ -383,7 +428,13 @@ class EndBlockTag(BlockTag):
|
|
383
428
|
def __repr__(self):
|
384
429
|
return 'EndBlockTag.\n'
|
385
430
|
|
386
|
-
class
|
431
|
+
class PypageError(Exception):
|
432
|
+
def __init__(self, description='undefined'):
|
433
|
+
self.description = description
|
434
|
+
def __str__(self):
|
435
|
+
return "Error: " + self.description
|
436
|
+
|
437
|
+
class PypageSyntaxError(PypageError):
|
387
438
|
def __init__(self, description='undefined'):
|
388
439
|
self.description = description
|
389
440
|
def __str__(self):
|
@@ -443,6 +494,15 @@ class InvalidCaptureBlockVariableName(PypageSyntaxError):
|
|
443
494
|
def __init__(self, varname):
|
444
495
|
self.description = "Incorrect CaptureBlock: '%s' is not a valid Python variable name." % varname
|
445
496
|
|
497
|
+
class InvalidDefBlockFunctionOrArgName(PypageSyntaxError):
|
498
|
+
def __init__(self, varname):
|
499
|
+
self.description = "Incorrect DefBlock: '%s' is not a valid function or argument name." % varname
|
500
|
+
|
501
|
+
class InvalidDefBlockMismatchingArgCount(PypageSyntaxError):
|
502
|
+
def __init__(self, argnames, args):
|
503
|
+
self.description = "Incorrect DefBlock function call: expected %d arguments (%s) but received %d arguments (%s) instead." % (
|
504
|
+
len(argnames), ', '.join(argnames), len(args), ', '.join(map(repr, args)))
|
505
|
+
|
446
506
|
class UnknownTag(PypageSyntaxError):
|
447
507
|
def __init__(self, node):
|
448
508
|
self.description = "Unknown tag '%s%s%s' at line %d, column %d." % (
|
@@ -832,10 +892,9 @@ def read_file(filepath):
|
|
832
892
|
with open(filepath, 'r') as source_file:
|
833
893
|
return source_file.read()
|
834
894
|
else:
|
835
|
-
|
836
|
-
sys.exit(1)
|
895
|
+
raise PypageError("File %s does not exist. CWD: %s" % (repr(filepath), os.getcwd()))
|
837
896
|
|
838
|
-
__all__ = ['pypage', 'pypage_version']
|
897
|
+
__all__ = ['pypage', 'pypage_version', PypageError, PypageSyntaxError]
|
839
898
|
|
840
899
|
def main():
|
841
900
|
import argparse
|
pypage-2.0.9.dist-info/RECORD
DELETED
@@ -1,7 +0,0 @@
|
|
1
|
-
pypage.py,sha256=qZnkMH_GC-05W-XlavDrT3B4xbkCm2i1T4nIJSMYMJU,28803
|
2
|
-
pypage-2.0.9.dist-info/LICENSE.txt,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
3
|
-
pypage-2.0.9.dist-info/METADATA,sha256=jdL5K1PuLp14Qt4jAyiVTsnx6SePwSS6t5SyeP4mNSA,14640
|
4
|
-
pypage-2.0.9.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
|
5
|
-
pypage-2.0.9.dist-info/entry_points.txt,sha256=iHPN6EfIUdv-njh8GV04yDTzkcCLGNcXvdbA43s_8mo,39
|
6
|
-
pypage-2.0.9.dist-info/top_level.txt,sha256=8AAB15dVQEt9xcwH_eLcVUZEbFlc__81RA5mOrYtJiQ,7
|
7
|
-
pypage-2.0.9.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|