Пример #1
0
 def test_details(self):
     loader = DictLoader({"foo.html": "\n\n{{"})
     with self.assertRaises(ParseError) as cm:
         loader.load("foo.html")
     self.assertEqual("Missing end expression }} at foo.html:3", str(cm.exception))
     self.assertEqual("foo.html", cm.exception.filename)
     self.assertEqual(3, cm.exception.lineno)
Пример #2
0
 def test_include(self):
     loader = DictLoader({
         "index.html": '{% include "header.html" %}\nbody text',
         "header.html": "header text",
     })
     self.assertEqual(loader.load("index.html").generate(),
                      b"header text\nbody text")
Пример #3
0
 def test_error_line_number_include(self):
     loader = DictLoader({"base.html": "{% include 'sub.html' %}", "sub.html": "{{1/0}}"})
     try:
         loader.load("base.html").generate()
         self.fail("did not get expected exception")
     except ZeroDivisionError:
         self.assertTrue("# sub.html:1 (via base.html:1)" in traceback.format_exc())
Пример #4
0
 def test_unextended_block(self):
     loader = DictLoader(self.templates)
     name = "<script>"
     self.assertEqual(loader.load("escaped_block.html").generate(name=name),
                      b"base: &lt;script&gt;")
     self.assertEqual(loader.load("unescaped_block.html").generate(name=name),
                      b"base: <script>")
Пример #5
0
 def test_non_ascii_name(self):
     if PY3 and is_coverage_running():
         try:
             os.fsencode(u"t\u00e9st.html")
         except UnicodeEncodeError:
             self.skipTest("coverage tries to access unencodable filename")
     loader = DictLoader({u"t\u00e9st.html": "hello"})
     self.assertEqual(loader.load(u"t\u00e9st.html").generate(), b"hello")
Пример #6
0
 def test_error_line_number_extends_base_error(self):
     loader = DictLoader({"base.html": "{{1/0}}", "sub.html": "{% extends 'base.html' %}"})
     try:
         loader.load("sub.html").generate()
         self.fail("did not get expected exception")
     except ZeroDivisionError:
         exc_stack = traceback.format_exc()
     self.assertTrue("# base.html:1" in exc_stack)
Пример #7
0
 def test_relative_load(self):
     loader = DictLoader({
         "a/1.html": "{% include '2.html' %}",
         "a/2.html": "{% include '../b/3.html' %}",
         "b/3.html": "ok",
     })
     self.assertEqual(loader.load("a/1.html").generate(),
                      b"ok")
Пример #8
0
    def test_error_line_number_expression(self):
        loader = DictLoader({"test.html": """one
two{{1/0}}
three
        """})
        try:
            loader.load("test.html").generate()
        except ZeroDivisionError:
            self.assertTrue("# test.html:2" in traceback.format_exc())
Пример #9
0
 def test_multi_includes(self):
     loader = DictLoader(
         {"a.html": "{% include 'b.html' %}", "b.html": "{% include 'c.html' %}", "c.html": "{{1/0}}"}
     )
     try:
         loader.load("a.html").generate()
         self.fail("did not get expected exception")
     except ZeroDivisionError:
         self.assertTrue("# c.html:1 (via b.html:1, a.html:1)" in traceback.format_exc())
Пример #10
0
    def test_error_line_number_directive(self):
        loader = DictLoader({"test.html": """one
two{%if 1/0%}
three{%end%}
        """})
        try:
            loader.load("test.html").generate()
        except ZeroDivisionError:
            self.assertTrue("# test.html:2" in traceback.format_exc())
Пример #11
0
    def test_default_on(self):
        loader = DictLoader(self.templates, autoescape="xhtml_escape")
        name = "Bobby <table>s"
        self.assertEqual(loader.load("escaped.html").generate(name=name), b"Bobby &lt;table&gt;s")
        self.assertEqual(loader.load("unescaped.html").generate(name=name), b"Bobby <table>s")
        self.assertEqual(loader.load("default.html").generate(name=name), b"Bobby &lt;table&gt;s")

        self.assertEqual(
            loader.load("include.html").generate(name=name),
            b"escaped: Bobby &lt;table&gt;s\n" b"unescaped: Bobby <table>s\n" b"default: Bobby &lt;table&gt;s\n",
        )
Пример #12
0
 def test_error_line_number_module(self):
     loader = DictLoader({
         "base.html": "{% module Template('sub.html') %}",
         "sub.html": "{{1/0}}",
     }, namespace={"_modules": ObjectDict({"Template": lambda path, **kwargs: loader.load(path).generate(**kwargs)})})
     try:
         loader.load("base.html").generate()
     except ZeroDivisionError:
         exc_stack = traceback.format_exc()
         self.assertTrue('# base.html:1' in exc_stack)
         self.assertTrue('# sub.html:1' in exc_stack)
Пример #13
0
 def test_error_line_number_module(self):
     loader = DictLoader(
         {"base.html": "{% module Template('sub.html') %}", "sub.html": "{{1/0}}"},
         namespace={"_tt_modules": ObjectDict(Template=lambda path, **kwargs: loader.load(path).generate(**kwargs))},
     )
     try:
         loader.load("base.html").generate()
         self.fail("did not get expected exception")
     except ZeroDivisionError:
         exc_stack = traceback.format_exc()
         self.assertTrue("# base.html:1" in exc_stack)
         self.assertTrue("# sub.html:1" in exc_stack)
Пример #14
0
    def test_whitespace_directive(self):
        loader = DictLoader({
            "foo.html": """\
{% whitespace oneline %}
    {% for i in range(3) %}
        {{ i }}
    {% end %}
{% whitespace all %}
    pre\tformatted
"""})
        self.assertEqual(loader.load("foo.html").generate(),
                         b"  0  1  2  \n    pre\tformatted\n")
Пример #15
0
    def test_manual_minimize_whitespace(self):
        # Whitespace including newlines is allowed within template tags
        # and directives, and this is one way to avoid long lines while
        # keeping extra whitespace out of the rendered output.
        loader = DictLoader({'foo.txt': """\
{% for i in items
  %}{% if i > 0 %}, {% end %}{#
  #}{{i
  }}{% end
%}""",
                             })
        self.assertEqual(loader.load("foo.txt").generate(items=range(5)),
                         b"0, 1, 2, 3, 4")
Пример #16
0
    def test_extends(self):
        loader = DictLoader({
            "base.html": """\
<title>{% block title %}default title{% end %}</title>
<body>{% block body %}default body{% end %}</body>
""",
            "page.html": """\
{% extends "base.html" %}
{% block title %}page title{% end %}
{% block body %}page body{% end %}
""",
        })
        self.assertEqual(loader.load("page.html").generate(),
                         b"<title>page title</title>\n<body>page body</body>\n")
Пример #17
0
    def test_error_line_number_extends_sub_error(self):
        loader = DictLoader({
            "base.html": "{% block 'block' %}{% end %}",
            "sub.html": """
{% extends 'base.html' %}
{% block 'block' %}
{{1/0}}
{% end %}
            """})
        try:
            loader.load("sub.html").generate()
        except ZeroDivisionError:
            self.assertTrue("# sub.html:4 (via base.html:1)" in
                            traceback.format_exc())
Пример #18
0
    def test_default_off(self):
        loader = DictLoader(self.templates, autoescape=None)
        name = "Bobby <table>s"
        self.assertEqual(loader.load("escaped.html").generate(name=name),
                         b("Bobby &lt;table&gt;s"))
        self.assertEqual(loader.load("unescaped.html").generate(name=name),
                         b("Bobby <table>s"))
        self.assertEqual(loader.load("default.html").generate(name=name),
                         b("Bobby <table>s"))

        self.assertEqual(loader.load("include.html").generate(name=name),
                         b("escaped: Bobby &lt;table&gt;s\n"
                           "unescaped: Bobby <table>s\n"
                           "default: Bobby <table>s\n"))
Пример #19
0
    def test_whitespace_by_loader(self):
        templates = {"foo.html": "\t\tfoo\n\n", "bar.txt": "\t\tbar\n\n"}
        loader = DictLoader(templates, whitespace="all")
        self.assertEqual(loader.load("foo.html").generate(), b"\t\tfoo\n\n")
        self.assertEqual(loader.load("bar.txt").generate(), b"\t\tbar\n\n")

        loader = DictLoader(templates, whitespace="single")
        self.assertEqual(loader.load("foo.html").generate(), b" foo\n")
        self.assertEqual(loader.load("bar.txt").generate(), b" bar\n")

        loader = DictLoader(templates, whitespace="oneline")
        self.assertEqual(loader.load("foo.html").generate(), b" foo ")
        self.assertEqual(loader.load("bar.txt").generate(), b" bar ")
Пример #20
0
    def test_whitespace_by_filename(self):
        # Default whitespace handling depends on the template filename.
        loader = DictLoader({
            "foo.html": "   \n\t\n asdf\t   ",
            "bar.js": " \n\n\n\t qwer     ",
            "baz.txt": "\t    zxcv\n\n",
            "include.html": "  {% include baz.txt %} \n ",
            "include.txt": "\t\t{% include foo.html %}    ",
        })

        # HTML and JS files have whitespace compressed by default.
        self.assertEqual(loader.load("foo.html").generate(),
                         b"\nasdf ")
        self.assertEqual(loader.load("bar.js").generate(),
                         b"\nqwer ")
        # TXT files do not.
        self.assertEqual(loader.load("baz.txt").generate(),
                         b"\t    zxcv\n\n")

        # Each file maintains its own status even when included in
        # a file of the other type.
        self.assertEqual(loader.load("include.html").generate(),
                         b" \t    zxcv\n\n\n")
        self.assertEqual(loader.load("include.txt").generate(),
                         b"\t\t\nasdf     ")
Пример #21
0
    def __init__(self, port=8080, url=None, debug=False, **args):
        # pylint: disable=super-init-not-called; Configurable classes use initialize() instead of
        #                                        __init__()
        url = url or 'http://*****:*****@' + urlparts.hostname,
                            render_email_auth_message=self._render_email_auth_message, **args)

        self._message_templates = DictLoader(meetling.server.templates.MESSAGE_TEMPLATES,
                                             autoescape=None)
Пример #22
0
 def test_non_ascii_name(self):
     loader = DictLoader({u"t\u00e9st.html": "hello"})
     self.assertEqual(loader.load(u"t\u00e9st.html").generate(), b"hello")
Пример #23
0
class MeetlingServer(HTTPServer):
    """Meetling server.

    .. attribute:: app

       Underlying :class:`meetling.Meetling` application.

    .. attribute:: port

       See ``--port`` command line option.

    .. attribute:: url

       See ``--url`` command line option.

    .. attribute:: debug

       See ``--debug`` command line option.

    Additional *args* are passed to the :class:`meetling.Meetling` constructor and any errors raised
    by it are passed through.
    """

    def __init__(self, port=8080, url=None, debug=False, **args):
        # pylint: disable=super-init-not-called; Configurable classes use initialize() instead of
        #                                        __init__()
        url = url or 'http://*****:*****@' + urlparts.hostname,
                            render_email_auth_message=self._render_email_auth_message, **args)

        self._message_templates = DictLoader(meetling.server.templates.MESSAGE_TEMPLATES,
                                             autoescape=None)

    def initialize(self, *args, **kwargs):
        # Configurable classes call initialize() instead of __init__()
        self.__init__(*args, **kwargs)

    def run(self):
        """Run the server."""
        self.app.update()
        self.listen(self.port)
        IOLoop.instance().start()

    def _render_email_auth_message(self, email, auth_request, auth):
        template = self._message_templates.load('email_auth')
        msg = template.generate(email=email, auth_request=auth_request, auth=auth, app=self.app,
                                server=self).decode()
        return '\n\n'.join([filter_whitespace('oneline', p.strip()) for p in
                            re.split(r'\n{2,}', msg)])
Пример #24
0
 def test_non_ascii_name(self):
     loader = DictLoader({u"t\u00e9st.html": "hello"})
     self.assertEqual(loader.load(u"t\u00e9st.html").generate(), b"hello")
Пример #25
0
    v = Column(String)


DCLR_BASE.metadata.create_all(SQLITE_ENGINE)
MAKE_SESSION = sessionmaker(bind=SQLITE_ENGINE)

TEMP_LOADER = DictLoader({
    'rss.xml':
    '''<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
 <title>{{ site }}</title>
 <link>http://wils519.herokuapp.com/rss/{{ site }}</link>
 <description>https://github.com/wanghan519/heroku_tornado</description>
 {% for i in soup %}
 <item>
  <title>{{ i[0] }}</title>
  <link>{{ i[1] }}</link>
  <pubDate>{{ i[2] }}</pubDate>
  <description>{{ i[3] }}</description>
 </item>
 {% end %}
</channel>
</rss>
'''
})


class MyHandler(RequestHandler):
    def initialize(self):
        self.db = MAKE_SESSION()
Пример #26
0
def test_include():
    loader = DictLoader({
            "index.html": '{% include "header.html" %}\nbody text',
            "header.html": "header text",
            })
    print loader.load("index.html").generate()
Пример #27
0
    def __init__(
            self, app: micro.Application, handlers: Sequence[Handler], port: int = 8080,
            url: str = None, client_path: str = 'client', client_modules_path: str = '.',
            client_service_path: str = None, debug: bool = False, *,
            client_config: ClientConfigArg = {}, client_shell: Sequence[str] = [],
            client_map_service_key: str = None) -> None:
        url = url or 'http://*****:*****@noyainrain/micro/service.js'),
            'map_service_key': None,
            'description': 'Social micro web app',
            'color': '#08f',
            **client_config, # type: ignore
            'shell': list(client_config.get('shell') or [])
        } # type: Server.ClientConfig

        # Compatibility with client attributes (deprecated since 0.40.0)
        self.client_path = self.client_config['path']
        self.client_modules_path = self.client_config['modules_path']
        self.client_service_path = self.client_config['service_path']
        self.client_shell = self.client_config['shell']
        self.client_map_service_key = self.client_config['map_service_key']

        self.app.email = 'bot@' + urlparts.hostname
        self.app.render_email_auth_message = self._render_email_auth_message

        def get_activity(*args: str) -> Activity:
            # pylint: disable=unused-argument; part of API
            return self.app.activity
        self.handlers = [
            # API
            (r'/api/login$', _LoginEndpoint),
            (r'/api/users/([^/]+)$', _UserEndpoint),
            (r'/api/users/([^/]+)/set-email$', _UserSetEmailEndpoint),
            (r'/api/users/([^/]+)/finish-set-email$', _UserFinishSetEmailEndpoint),
            (r'/api/users/([^/]+)/remove-email$', _UserRemoveEmailEndpoint),
            (r'/api/settings$', _SettingsEndpoint),
            # Compatibility with non-object Activity (deprecated since 0.14.0)
            make_activity_endpoint(r'/api/activity/v2', get_activity),
            *make_list_endpoints(r'/api/activity(?:/v1)?', get_activity),
            (r'/api/activity/stream', ActivityStreamEndpoint,
             {'get_activity': cast(object, get_activity)}),
            # Provide alias because /api/analytics triggers popular ad blocking filters
            (r'/api/(?:analytics|stats)/statistics/([^/]+)$', _StatisticEndpoint),
            (r'/api/(?:analytics|stats)/referrals$', _ReferralsEndpoint),
            (r'/files$', _FilesEndpoint), # type: ignore[misc]
            (r'/files/([^/]+)$', _FileEndpoint), # type: ignore[misc]
            *handlers,
            # UI
            (r'/log-client-error$', _LogClientErrorEndpoint),
            (r'/index.html$', _Index),
            (r'/manifest.webmanifest$', _WebManifest), # type: ignore
            (r'/manifest.js$', _BuildManifest), # type: ignore
            (r'/static/{}$'.format(self.client_service_path), _Service), # type: ignore
            (r'/static/(.*)$', _Static, {'path': self.client_path}), # type: ignore
            (r'/.*$', UI), # type: ignore
        ] # type: List[Handler]

        application = Application(
            self.handlers, compress_response=True, # type: ignore[arg-type]
            template_path=self.client_path, debug=self.debug, server=self)
        # Install static file handler manually to allow pre-processing
        cast(_ApplicationSettings, application.settings).update({'static_path': self.client_path})
        self._server = HTTPServer(application)

        self._garbage_collect_files_task = None # type: Optional[Task[None]]
        self._empty_trash_task = None # type: Optional[Task[None]]
        self._collect_statistics_task = None # type: Optional[Task[None]]
        self._message_templates = DictLoader(templates.MESSAGE_TEMPLATES, autoescape=None)
        self._micro_templates = Loader(os.path.join(self.client_path, self.client_modules_path,
                                                    '@noyainrain/micro'))
Пример #28
0
class Server:
    """Server for micro apps.

    The server may optionally serve the client in :attr:`client_config` *path*. All files from
    *modules_path*, ``manifest.webmanifest``, ``manifest.js`` and the script at *service_path* are
    delivered, with a catch-all for ``index.html``.

    Also, the server may act as a dynamic build system for the client. ``index.html`` is rendered as
    template. A web app manifest ``manifest.webmanifest`` and a build manifest ``manifest.js`` (see
    *shell*) are generated.

    .. attribute:: app

       Underlying :class:`micro.Application`.

    .. attribute:: handlers

       Table of request handlers.

       It is a list of tuples, mapping a URL regular expression pattern to a
       :class:`tornado.web.RequestHandler` class.

    .. attribute:: port

       See ``--port`` command line option.

    .. attribute:: url

       See ``--url`` command line option.

    .. attributes:: client_path

       Client location from where static files and templates are delivered.

       .. deprecated:: 0.40.0

          Use :attr:`client_config` instead.

    .. attribute:: client_service_path

       Location of client service worker script. Defaults to the included micro service worker.

       .. deprecated:: 0.40.0

          Use :attr:`client_config` instead.

    .. attribute: client_shell

       Set of files that make up the client shell. See :func:`micro.util.look_up_files`.

       .. deprecated:: 0.40.0

          Use :attr:`client_config` instead.

    .. attribute:: client_map_service_key

       See ``--client-map-service-key`` command line option.

       .. deprecated:: 0.40.0

          Use :attr:`client_config` instead.

    .. attribute:: debug

       See ``--debug`` command line option.

    .. attribute:: client_config

       Client configuration:

       - ``path``: Location from where static files and templates are delivered
       - ``service_path``: Location of service worker script. Defaults to the included micro service
         worker.
       - ``shell``: Set of files that make up the client shell. See
         :func:`micro.util.look_up_files`.
       - ``map_service_key``: See ``--client-map-service-key`` command line option
       - ``description``: Short description of the service
       - ``color``: CSS primary color of the service

    .. deprecated:: 0.21.0

       Constructor options as positional arguments. Use keyword arguments instead.
    """

    ClientConfig = TypedDict('ClientConfig', {
        'path': str,
        'modules_path': str,
        'service_path': str,
        'shell': Sequence[str],
        'map_service_key': Optional[str],
        'description': str,
        'color': str
    })
    ClientConfigArg = TypedDict('ClientConfigArg', {
        'path': str,
        'modules_path': str,
        'service_path': str,
        'shell': Sequence[str],
        'map_service_key': Optional[str],
        'description': str,
        'color': str
    }, total=False)

    def __init__(
            self, app: micro.Application, handlers: Sequence[Handler], port: int = 8080,
            url: str = None, client_path: str = 'client', client_modules_path: str = '.',
            client_service_path: str = None, debug: bool = False, *,
            client_config: ClientConfigArg = {}, client_shell: Sequence[str] = [],
            client_map_service_key: str = None) -> None:
        url = url or 'http://*****:*****@noyainrain/micro/service.js'),
            'map_service_key': None,
            'description': 'Social micro web app',
            'color': '#08f',
            **client_config, # type: ignore
            'shell': list(client_config.get('shell') or [])
        } # type: Server.ClientConfig

        # Compatibility with client attributes (deprecated since 0.40.0)
        self.client_path = self.client_config['path']
        self.client_modules_path = self.client_config['modules_path']
        self.client_service_path = self.client_config['service_path']
        self.client_shell = self.client_config['shell']
        self.client_map_service_key = self.client_config['map_service_key']

        self.app.email = 'bot@' + urlparts.hostname
        self.app.render_email_auth_message = self._render_email_auth_message

        def get_activity(*args: str) -> Activity:
            # pylint: disable=unused-argument; part of API
            return self.app.activity
        self.handlers = [
            # API
            (r'/api/login$', _LoginEndpoint),
            (r'/api/users/([^/]+)$', _UserEndpoint),
            (r'/api/users/([^/]+)/set-email$', _UserSetEmailEndpoint),
            (r'/api/users/([^/]+)/finish-set-email$', _UserFinishSetEmailEndpoint),
            (r'/api/users/([^/]+)/remove-email$', _UserRemoveEmailEndpoint),
            (r'/api/settings$', _SettingsEndpoint),
            # Compatibility with non-object Activity (deprecated since 0.14.0)
            make_activity_endpoint(r'/api/activity/v2', get_activity),
            *make_list_endpoints(r'/api/activity(?:/v1)?', get_activity),
            (r'/api/activity/stream', ActivityStreamEndpoint,
             {'get_activity': cast(object, get_activity)}),
            # Provide alias because /api/analytics triggers popular ad blocking filters
            (r'/api/(?:analytics|stats)/statistics/([^/]+)$', _StatisticEndpoint),
            (r'/api/(?:analytics|stats)/referrals$', _ReferralsEndpoint),
            (r'/files$', _FilesEndpoint), # type: ignore[misc]
            (r'/files/([^/]+)$', _FileEndpoint), # type: ignore[misc]
            *handlers,
            # UI
            (r'/log-client-error$', _LogClientErrorEndpoint),
            (r'/index.html$', _Index),
            (r'/manifest.webmanifest$', _WebManifest), # type: ignore
            (r'/manifest.js$', _BuildManifest), # type: ignore
            (r'/static/{}$'.format(self.client_service_path), _Service), # type: ignore
            (r'/static/(.*)$', _Static, {'path': self.client_path}), # type: ignore
            (r'/.*$', UI), # type: ignore
        ] # type: List[Handler]

        application = Application(
            self.handlers, compress_response=True, # type: ignore[arg-type]
            template_path=self.client_path, debug=self.debug, server=self)
        # Install static file handler manually to allow pre-processing
        cast(_ApplicationSettings, application.settings).update({'static_path': self.client_path})
        self._server = HTTPServer(application)

        self._garbage_collect_files_task = None # type: Optional[Task[None]]
        self._empty_trash_task = None # type: Optional[Task[None]]
        self._collect_statistics_task = None # type: Optional[Task[None]]
        self._message_templates = DictLoader(templates.MESSAGE_TEMPLATES, autoescape=None)
        self._micro_templates = Loader(os.path.join(self.client_path, self.client_modules_path,
                                                    '@noyainrain/micro'))

    def start(self) -> None:
        """Start the server."""
        self.app.update() # type: ignore
        self._garbage_collect_files_task = self.app.start_garbage_collect_files()
        self._empty_trash_task = self.app.start_empty_trash()
        self._collect_statistics_task = self.app.analytics.start_collect_statistics()
        self._server.listen(self.port)

    async def stop(self) -> None:
        """Stop the server."""
        self._server.stop()
        if self._garbage_collect_files_task:
            await cancel(self._garbage_collect_files_task)
        if self._empty_trash_task:
            await cancel(self._empty_trash_task)
        if self._collect_statistics_task:
            await cancel(self._collect_statistics_task)

    def run(self) -> None:
        """Start the server and run it continuously."""
        self.start()
        loop = get_event_loop()
        def _on_sigint() -> None:
            async def _stop() -> None:
                await self.stop()
                loop.stop()
            ensure_future(_stop())
        loop.add_signal_handler(SIGINT, _on_sigint)
        getLogger(__name__).info('Started server at %s/', self.url)
        loop.run_forever()

    def rewrite(self, url: str, *, reverse: bool = False) -> str:
        """Rewrite an internal file *url* to a public URL.

        If *reverse* is ``True``, mapping is done in the opposite direction.
        """
        if reverse:
            prefix = f'{self.url}/files/'
            return f'file:/{url[len(prefix):]}' if url.startswith(prefix) else url
        return f'{self.url}/files/{url[6:]}' if url.startswith('file:/') else url

    def _render_email_auth_message(self, email, auth_request, auth):
        template = self._message_templates.load('email_auth')
        msg = template.generate(email=email, auth_request=auth_request, auth=auth, app=self.app,
                                server=self).decode()
        return '\n\n'.join([filter_whitespace('oneline', p.strip()) for p in
                            re.split(r'\n{2,}', msg)])
Пример #29
0
    def test_whitespace_by_loader(self):
        templates = {
            "foo.html": "\t\tfoo\n\n",
            "bar.txt": "\t\tbar\n\n",
        }
        loader = DictLoader(templates, whitespace='all')
        self.assertEqual(loader.load("foo.html").generate(), b"\t\tfoo\n\n")
        self.assertEqual(loader.load("bar.txt").generate(), b"\t\tbar\n\n")

        loader = DictLoader(templates, whitespace='single')
        self.assertEqual(loader.load("foo.html").generate(), b" foo\n")
        self.assertEqual(loader.load("bar.txt").generate(), b" bar\n")

        loader = DictLoader(templates, whitespace='oneline')
        self.assertEqual(loader.load("foo.html").generate(), b" foo ")
        self.assertEqual(loader.load("bar.txt").generate(), b" bar ")
Пример #30
0
 def test_custom_namespace(self):
     loader = DictLoader({"test.html": "{{ inc(5) }}"},
                         namespace={"inc": lambda x: x + 1})
     self.assertEqual(loader.load("test.html").generate(), b"6")
Пример #31
0
 def test_custom_namespace(self):
     loader = DictLoader({"test.html": "{{ inc(5) }}"}, namespace={"inc": lambda x: x + 1})
     self.assertEqual(loader.load("test.html").generate(), b"6")
Пример #32
0
def test_include():
    loader = DictLoader({
        "index.html": '{% include "header.html" %}\nbody text',
        "header.html": "header text",
    })
    print loader.load("index.html").generate()