granite.environment module

Provides utilities for setting a proper environment for testing.

class FileStat(st_mode, st_ino, st_dev, st_nlink, st_uid, st_gid, st_size, st_atime, st_mtime, st_ctime, md5)

Bases: tuple

Mimics the os.stat() stat result object except this also includes the md5 hash of the file.

md5

Alias for field number 10

st_atime

Alias for field number 7

st_ctime

Alias for field number 9

st_dev

Alias for field number 2

st_gid

Alias for field number 5

st_ino

Alias for field number 1

st_mode

Alias for field number 0

st_mtime

Alias for field number 8

Alias for field number 3

st_size

Alias for field number 6

st_uid

Alias for field number 4

class Renderable[source]

Bases: object

An interface for objects that can be rendered.

get_context()[source]

Gets the context (or scope) used when rendering this class’s template.

By default, the context is set to all of the available attributes on this class. This means that if self.foo is set to 'bar', then the variable {{ foo }} in the template will be rendered as the string bar.

Returns:
the context used for rendering. A mapping of variable name
to variable value as to be used in the template.
Return type:dict
get_environment(template_directories)[source]

Gets the jinja2.Environment instance needed for loading the templates.

Subclasses should override this if they need more customized power.

Parameters:template_directories (list) – a list of directories to search through

Returns:

get_template()[source]

Gets the template used by this class for rendering.

Subclasses can override this method is updating the template and template_dirs attributes is not sufficient.

Returns:the template used to render
Return type:jinja2.Template
render()[source]

Renders the template as found by get_template() using the context as found by get_context() and returns the rendered string.

Returns:the result of rendering the template with the context
Return type:str
template = None

The name of the template that instances of this class should use when rendering. This attribute is required and must be set by subclasses.

template_dirs = None

The template search path; a list of strings. When searching for a template by name, then the first file found on the path will be chosen. This attribute is required and must be set by subclasses.

class RenderedFile(filename='')[source]

Bases: granite.environment.Renderable

A file that will be rendered to disk.

This class represents a file that, upon rendering, will appear on disk. By default, any attribute (instance or class) will appear in the context of the template. E.g., having an attribute of self.foo will make the foo variable exist within the template.

This class provides a “content” variable and should serve as an area of the template that can be appended to on a per-test basis. Use the add_content() method in order to add more content to the template at the time of render. This allows for content to be added over a period of time either as subsequent calls in the same test, through the setUp, or setUp calls in multiple classes (via inheritance).

Subclasses can override the get_context() method in order to alter the context (variable scope) provided to the template on render. See the get_context() method’s documentation for more details.

While this class can be used by itself (note that the instances attributes template and template_dirs must be set before rendering!) the intended use is to subclass this class and define class-level attributes for template and template_dirs. This makes it so that a base class can point to a common template directory (through the template_dirs attribute) and all subclasses of it can supply the template attribute in order to determine which template to choose.

Parameters:filename (str) – the full path to where the file should exist on disk when rendered

Examples:

# tests/environment.py
# assume that the templates directory is:
# tests/templates
from granite.environment import RenderedFile
from granite.testcase import TestCase, TempProjectMixin


class MyRenderedFile(RenderedFile):
    template_dirs = [
        os.path.join(os.path.dirname(__file__), 'templates')]


# assume that tests/templates/template.py exists
# and looks something like this:
print('Hello!')
print(
    '''
     {{ content }}
    '''
)
print('This test is currently running: {{ id }}')

class PythonScript(MyRenderedFile):
    template = 'template.py'


class MyTestCase(TempProjectMixin, TestCase):
    def setUp(self):
        super(MyTestCase, self).setUp()
        self.script = PythonScript(
            os.path.join(self.temp_project.path, 'my_file.py'))
        self.script.add_content('My name is Aaron')

    def test_the_thing(self):
        # setting the `id` attribute provides the `id`
        # context variable in the template
        self.script.id = self.id()
        self.script.render()
        with open(self.script.full_name) as f:
            self.assertEqual(
                f.read(),
                """
                print('Hello!')
                print(
                    '''
                    My name is Aaron
                    '''
                )
                print('This test is currently running: test_the_thing')
                """
            )
ADD_NEWLINE = True

Determines whether a newline should be added at the end of the file or not. When generating C code to be compiled by the ARM/GCC compiler, this makes the compiler happy. Defaults to True. Set to False to disable.

DISABLE_ESCAPING = False

By default, the value in the “content” template variable is escaped. This makes adding content for script/language files (via add_content()) much easier to read and maintain as script/language files usually interpret the special esacped characters differently. Set this attribute to True in order to disable this functionality.

WRITE_MODE = 'w'

When rendering the file, defaults to non-binary mode. Set this to ‘wb’ or something similar for different behavior when writing the rendered contents to a file.

add_content(content)[source]

Adds a string to the “content” variable available to the template.

Parameters:content (str or List[str]) – a single string or a list of strings to add to the content variable.
filename = ''

The name of the rendered file (without the path)

full_name = ''

the full path to the file

get_context()[source]

Gets the context needed for rendering the file associated with this instance.

A context is simply the template’s scope of variables and functions.

Returns:
the variable names and their values; the scope to appear in
the template
Return type:dict
path = ''

The path to this rendered file (without the filename itself)

render()[source]

Renders the template and writes the contents to disk to this instance’s filename.

class SimpleFile(filename='')[source]

Bases: granite.environment.RenderedFile

Follows the renderable interface and allows for building up a file with add_content() then rendering and writing to disk at a later time.

This class doesn’t provide any sort of templating functionality. It just makes it easier to incorporate simple file writing into a framework that expects a Renderable.

render()[source]

Renders all of the contents to the filename given.

class Snapshot(directory)[source]

Bases: object

A snapshot of the state of the given directory at the time called.

This will recursively traverse the given directory and note all of the files and directories within it. For the most part, a snapshot is useless by itself and is more useful when another snapshot is created and compared with the first.

For example:

some_dir = 'path/to/some/dir'
s1 = Snapshot(some_dir)
with open(os.path.join(some_dir, 'hello.txt'), 'w') as f:
    f.write('Hello, World!')
s2 = Snapshot(some_dir)

diff = s2 - s1

assert diff.added == ['hello.txt']

To see the difference between two snapshots, simply subtract one snapshot from the other. This creates a SnapshotDiff object with the attributes added, removed, modified, and touched. See the SnapshotDiff documentation for more information.

Note: this does not keep track of directory information, only files. Also Note: the paths stored in both the snapshot and in the diff are relative to the root of the snapshot directory.

Parameters:directory (str) – the directory to take a snapshot of
class SnapshotDiff(a, b)[source]

Bases: object

The difference between Snapshot a and Snapshot b`.

A difference object will have four attributes.

added

a collection of files that are new in a, but not in b

modified

a collection of files whose contents have changed between a and b

removed

a collection of files that are in b, but no longer in a

touched

a collection of files whose timestamps have changed between a and b, but their contents have not changed.

exception TemplateNotFoundError[source]

Bases: Exception

Raised when a template name was requested but not found in the available directories

class TemporaryProject(path='', preserve=False)[source]

Bases: object

An interface for interacting with a temporary directory.

A temp directory is created on instantiation and it is deleted (recursively) when this object is destroyed.

Keyword Arguments:
 
  • path (str) – path of a directory to use for the temporary directory if specified. If the directory already exists, it is recursively deleted and then created. Otherwise, if the directory doesn’t exist, it (and any intermediate directories) are created.
  • preserve (bool) – if set to True, this directory will not be destroyed. Useful for debugging tests.
TEMP_PREFIX = 'gprj_'

This is the prefix used for the new temp directory.

abspath(filename)[source]

Get the absolute path to the filename found in the temp dir.

Notes

  • Always use forward slashes in paths.
  • This method does not check if the path is valid. If the filename given doesn’t exist, an exception is not raised.
Parameters:filename (str) – the relative path to the file within this temp directory
Returns:the absolute path to the file within the temp directory.
Return type:str
copy_project(dest, overwrite=False, symlinks=False, ignore=None)[source]

Allows for a copying the temp project to the destination given.

This provides test authors with the ability to preserve a tes environment at any point during a test.

By default, if the given destination is a directory that already exists, an error will be raised (shutil.copytree’s error). Set the overwrite flag to True to overwrite an existing directory by first removing it and then copying the temp project.

Parameters:
  • dest (str) – the destination directory
  • overwrite (bool) – if the directory exists, this will remove it first before copying
  • symlinks (bool) – passed to shutil.copytree: should symlinks be traversed?
  • ignore (bool) – ignore errors during copy?
glob(pattern, start='', absolute=False)[source]

Recursively searches through the temp dir for a filename that matches the given pattern and returns the first one that matches.

Parameters:
  • pattern (str) – the glob pattern to match the filenames against. Uses the fnmatch module’s fnmatch() function to determine matches.
  • start (str) – a directory relative to the root of the temp dir to start searching from.
  • absolute (bool) – whether the returned path should be an absolute path; defaults to being relative to the temp project.
Returns:

the relative path to the first filename that matches pattern

unless the absolute flag is given. If a match is not found None is returned.

Return type:

str

read(filename, mode='r')[source]

Read the contents of the file found in the temp directory.

Parameters:
  • filename (str) – the path to the file in the temp dir.
  • mode (str) – a valid mode to open(). defaults to ‘r’
Returns:

The contents of the file.

remove(filename)[source]

Removes the filename found in the temp dir.

Parameters:filename (str) – the relative path to the file
snapshot()[source]

Creates a snapshot of the current state of this temp dir.

Returns:the snapshot.
Return type:Snapshot
teardown()[source]

Provides a public way to delete the directory that this temp project manages.

This allows for the temporary directory to be cleaned up on demand.

Ignores all errors.

touch(filename)[source]

Creates or updates timestamp on file given by filename.

Parameters:filename (str) – the filename to touch
write(filename, contents='', mode='w', dedent=True)[source]

Write the given contents to the file in the temp dir.

If the file or the directories to the file do not exist, they will be created.

If the file already exists, its contents will be overwritten with the new contents unless mode is set to some variant of append: (a, ab).

Specify the dedent flag to automatically call textwrap.dedent on the contents before writing. This is especially useful when writing contents that depend on whitespace being exact (e.g. writing a Python script). This defaults to True except when the mode contains 'b'

Parameters:
  • filename (str) – the relative path to a file in the temp dir
  • contents (any) – any data to write to the file
  • mode (str) – a valid open() mode
  • dedent (bool) – automatically dedent the contents (default: True)