Beispiel #1
0
def test_raise(tmp_directory):
    p = Placeholder("{% raise 'some error message' %}")

    with pytest.raises(TemplateRuntimeError) as excinfo:
        p.render({})

    assert str(excinfo.value) == 'some error message'
Beispiel #2
0
def test_macros_with_template_environment(env_init, path_to_test_pkg):
    env = env_init(path_to_test_pkg)

    # this template contains a macro
    placeholder = Placeholder(env.get_template('query.sql'))
    placeholder.render({})

    assert str(placeholder) == 'SELECT * FROM table'
Beispiel #3
0
def test_error_if_missing_upstream():
    p = Placeholder('SELECT * FROM {{upstream["name"]}}')
    upstream = Upstream({'a': 1}, name='task')

    with pytest.raises(UpstreamKeyError) as excinfo:
        p.render({'upstream': upstream})

    assert ('Cannot obtain upstream dependency "name" for task "task"'
            in str(excinfo.value))
Beispiel #4
0
def test_error_if_no_upstream():
    p = Placeholder('SELECT * FROM {{upstream["name"]}}')
    upstream = Upstream({}, name='task')

    with pytest.raises(UpstreamKeyError) as excinfo:
        p.render({'upstream': upstream})

    msg = ('Cannot obtain upstream dependency "name". '
           'Task "task" has no upstream dependencies')
    assert msg == str(excinfo.value)
Beispiel #5
0
class GenericSource(PlaceholderSource):
    """
    Generic source, the simplest type of source, it does not perform any kind
    of parsing nor validation

    Prameters
    ---------
    value: str
        The value for this source


    Notes
    -----
    value is directly passed to ploomber.templates.Placeholder, which means
    pathlib.Path objects are read and str are converted to jinja2.Template
    objects, for more details see Placeholder documentation
    """
    def __init__(self, value, hot_reload=False, optional=None, required=None):
        self._primitive = value
        # rename, and make it private
        self._placeholder = Placeholder(value,
                                        hot_reload=hot_reload,
                                        required=required)
        self._post_init_validation(self._placeholder)
        self._optional = optional
        self._required = required

    def render(self, params):
        if params.get('upstream'):
            with params.get('upstream'):
                self._placeholder.render(params, optional=self._optional)
        else:
            self._placeholder.render(params, optional=self._optional)

        self._post_render_validation(str(self._placeholder), params)
        return self

    @property
    def doc(self):
        return None

    @property
    def extension(self):
        return None

    def _post_render_validation(self, rendered_value, params):
        pass

    def _post_init_validation(self, value):
        pass

    def extract_upstream(self):
        return StringExtractor(self._placeholder._raw).extract_upstream()
Beispiel #6
0
def test_hot_reload_with_with_path(tmp_directory):
    query_path = Path(tmp_directory, 'simple_query.sql')
    query_path.write_text('SELECT * FROM {{tag}}')

    placeholder = Placeholder(Path(query_path), hot_reload=True)
    placeholder.render({'tag': 'table'})

    assert str(placeholder) == 'SELECT * FROM table'

    query_path.write_text('SELECT * FROM {{tag}} WHERE x = 10')
    placeholder.render({'tag': 'table'})

    assert str(placeholder) == 'SELECT * FROM table WHERE x = 10'
Beispiel #7
0
class PlaceholderSource(abc.Source):
    """
    Source concrete class for the ones that use a Placeholder
    """
    def __init__(self, value, hot_reload=False):
        self._primitive = value
        # rename, and make it private
        self._placeholder = Placeholder(value, hot_reload=hot_reload)
        self._post_init_validation(self._placeholder)

    @property
    def primitive(self):
        return self._primitive

    # TODO: rename to params
    @property
    def variables(self):
        return self._placeholder.variables

    def render(self, params):
        if params.get('upstream'):
            with params.get('upstream'):
                self._placeholder.render(params)
        else:
            self._placeholder.render(params)

        self._post_render_validation(str(self._placeholder), params)
        return self

    @property
    def loc(self):
        return self._placeholder.path

    def __str__(self):
        return str(self._placeholder)

    def __repr__(self):
        repr_ = "{}({})".format(
            type(self).__name__, self._placeholder.best_repr(shorten=True))

        if self._placeholder.path:
            repr_ += ' Loaded from: {}'.format(self._placeholder.path)

        return repr_

    @property
    def name(self):
        if self._placeholder.path is not None:
            # filename without extension(e.g., plot.py -> plot)
            return self._placeholder.path.stem
Beispiel #8
0
    def execute(self, code):
        """Run code
        """
        fd, path_to_tmp = tempfile.mkstemp()
        os.close(fd)
        Path(path_to_tmp).write_text(code)

        run_template = Placeholder(self.run_template)
        # we need quoting to make windows paths keep their \\ separators when
        # running shlex.split
        source = run_template.render(
            dict(path_to_code=shlex.quote(path_to_tmp)))

        res = subprocess.run(shlex.split(source), **self.subprocess_run_kwargs)
        Path(path_to_tmp).unlink()

        stdout = res.stdout.decode('utf-8')
        stderr = res.stderr.decode('utf-8')

        if res.returncode != 0:
            # log source code without expanded params
            self._logger.info(
                ('{} returned stdout: '
                 '{}\nstderr: {}\n'
                 'exit status {}').format(code, stdout, stderr,
                                          res.returncode))
            raise RuntimeError(
                ('Error executing code.\nReturned stdout: '
                 '{}\nstderr: {}\n'
                 'exit status {}').format(stdout, stderr, res.returncode))
        else:
            self._logger.info(('Finished running {}. stdout: {},'
                               ' stderr: {}').format(self, stdout, stderr))
Beispiel #9
0
def test_hot_reload_with_template_env(env_init, path_to_test_pkg):
    query_path = Path(path_to_test_pkg, 'templates', 'query.sql')
    query_original = query_path.read_text()

    env = env_init(path_to_test_pkg)

    placeholder = Placeholder(env.get_template('query.sql'), hot_reload=True)
    placeholder.render({})

    assert str(placeholder) == 'SELECT * FROM table'

    # use a macro to make sure the template loader is correctly initialized
    query = ('{% import "macros.sql" as m %}SELECT * FROM {{m.my_macro()}}'
             ' WHERE x = 10')
    query_path.write_text(query)
    placeholder.render({})

    assert str(placeholder) == 'SELECT * FROM table WHERE x = 10'

    # revert query to their original value
    query_path.write_text(query_original)
Beispiel #10
0
    def execute(self, code):
        """Run code
        """
        ftp = self.connection.open_sftp()
        path_remote = self.path_to_directory + self._random_name()

        fd, path_to_tmp = tempfile.mkstemp()
        os.close(fd)
        Path(path_to_tmp).write_text(code)

        ftp.put(path_to_tmp, path_remote)
        ftp.close()

        run_template = Placeholder(self.run_template)
        source = run_template.render(dict(path_to_code=path_remote))

        # stream stdout. related: https://stackoverflow.com/q/31834743
        # using pty is not ideal, fabric has a clean implementation for this
        # worth checking out
        stdin, stdout, stderr = self.connection.exec_command(source,
                                                             get_pty=True)

        for line in iter(stdout.readline, ""):
            self._logger.info('(STDOUT): {}'.format(line))

        returncode = stdout.channel.recv_exit_status()

        stdout = ''.join(stdout)
        stderr = ''.join(stderr)

        if returncode != 0:
            # log source code without expanded params
            self._logger.info(
                '%s returned stdout: '
                '%s and stderr: %s '
                'and exit status %s', code, stdout, stderr, returncode)
            raise subprocess.CalledProcessError(returncode, code)
        else:
            self._logger.info('Finished running %s. stdout: %s,'
                              ' stderr: %s', self, stdout, stderr)

        return {'returncode': returncode, 'stdout': stdout, 'stderr': stderr}
Beispiel #11
0
def test_strict_templates_initialized_from_jinja_template(path_to_assets):
    path = str(path_to_assets / 'templates')
    env = Environment(loader=FileSystemLoader(path), undefined=StrictUndefined)
    st = Placeholder(env.get_template('template.sql'))
    assert st.render({'file': 1})
Beispiel #12
0
def test_init_placeholder_with_placeholder():
    t = Placeholder('{{file}}')
    tt = Placeholder(t)

    assert tt.render({'file': 'some file'})