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.
@@ -1,9 +1,9 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: pypage
3
- Version: 2.0.9
4
- Summary: Light-weight Python Templating Engine
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.9.tar.gz
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.41.2)
2
+ Generator: setuptools (78.1.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
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.9'
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 PypageSyntaxError(Exception):
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
- print("File %s does not exist." % repr(filepath), file=sys.stderr)
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
@@ -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,,