pymetacode.utils module

Auxiliary functionality used by other modules of the pymetacode package.

To avoid circular dependencies, this module does not depend on any other modules of the pymetacode package, but it can be imported into every other module.

As naturally, a utils module tends to be a bit messy, the different tools available are listed below according to more general categories.

Files and I/O

  • ensure_file_exists()

    Create an (empty) file if it does not exist already.

  • get_data_from_pkg_resources()

    Obtain contents from a non-code file stored within the package.

  • change_working_dir()

    Context manager for temporarily changing the working directory.

  • make_executable()

    Make a file executable, i.e., set its executable flag.

String manipulation

Manipulating file contents

Part of metacoding is to manipulate file contents after the files have been generated using templates. Hence, it is sometimes not easily possible to parse the entire file into a sensible structure. Therefore, rather complex search and insert operations need to be performed.

The following functions exist currenty:

  • add_to_toctree()

    Add entries to toctrees in documentation generated via Sphinx

Helper classes

  • ToDictMixin

    Mixin class for returning all public attributes as dict.

  • Template

    Wrapper for using the template engine (Jinja2).

Module documentation

pymetacode.utils.ensure_file_exists(name='')

Create an (empty) file if it does not exist already.

This is similar to the “touch” command from unixoid operating systems, although it does not change the timestamp of the file.

Parameters

name (str) – Name of the file

Raises

ValueError – Raised if no filename is given.

pymetacode.utils.get_package_data(name='', directory='templates')

Obtain contents from a non-code file (“package data”).

There are generally three places where package data can be stored:

  1. Within the package,

  2. In the site-wide data directory (with the package name as subdirectory),

  3. In the user-specific data directory (with the package name as subdirectory).

The location of the latter two is specific to the operating system used. Here, the platformdirs package is used, providing paths for all major platforms (Windows, macOS, Linux/Unix).

The given file is searched for in the user-specific data directory first, followed by the site-wide data directory. Only if it cannot be found in either place, as a fallback the package itself is queried. Thus, files under control of the individual user take precedence over site-wide files and files distributed with the package.

A note to obtaining data from the distributed package: Rather than manually playing around with paths relative to the package root directory, contents of non-code files need to be obtained in a way that works with different kinds of package installation.

Note that in Python, only files within the package, i.e. within the directory where all the modules are located, can be accessed, not files that reside on the root directory of the package.

Note

In case you would want to get package data from a package different than pymetacode, you can prefix the name of the file to retrieve with the package name, using the ‘@’ as a separator.

Suppose you would want to retrieve the file __main__.py file from the “pip” package (why should you?):

get_package_data('pip@__main__.py', directory='')

This would return the contents of this file. Of course, the sequence of directories as described above is used here as well (user data directory, site data directory, package directory).

Parameters
  • name (str) –

    Name of the file whose contents should be accessed.

    In case the file should be retrieved from a different package, the package name can be prefixed, using ‘@’ as a separator.

  • directory (str) –

    Directory within the package where the files are located.

    Default: “templates”

Returns

contents – String containing the contents of the non-code file.

Return type

str

Changed in version 0.3: Name can be prefixed with package using ‘@’ as separator

class pymetacode.utils.ToDictMixin

Bases: object

Mixin class for returning all public attributes as dict.

Sometimes there is the need to either exclude public attributes (in case of infinite loops created by trying to apply to_dict in this case) or to add (public) attributes, particularly those used by getters and setters that are otherwise not included.

To do so, there are two non_public attributes of this class each class inheriting from it will be able to set as well:

The names should be rather telling. For details, see below.

_exclude_from_to_dict

Names of (public) attributes to exclude from dictionary

Usually, the reason to exclude public attributes from being added to the dictionary is to avoid infinite loops, as sometimes an object may contain a reference to another object that in turn references back.

Type

list

_include_in_to_dict

Names of (public) attributes to include into dictionary

Usual reasons for actively including (public) attributes into the dictionary are those attributes accessed by getters and setters and hence not automatically included in the list otherwise.

Type

list

Changed in version 0.4: Return dict rather than collections.OrderedDict, as dicts are order-preserving since Python 3.7

to_dict()

Create dictionary containing public attributes of an object.

Returns

public_attributes – Ordered dictionary containing the public attributes of the object

The order of attribute definition is preserved

Return type

collections.OrderedDict

pymetacode.utils.change_working_dir(path='')

Context manager for temporarily changing the working directory.

Sometimes it is necessary to temporarily change the working directory, but one would like to ensure that the directory is reverted even in case an exception is raised.

Due to its nature as a context manager, this function can be used with a with statement. See below for an example.

Parameters

path (str) – Path the current working directory should be changed to.

Examples

To temporarily change the working directory:

with change_working_dir(os.path.join('some', 'path')):
    # Do something that may raise an exception

This can come in quite handy in case of tests.

pymetacode.utils.camel_case_to_underscore(name='')

Change string from camel case to using underscores.

According to PEP8, class names should follow camel case convention, whereas methods, functions, and variables should use underscores.

Parameters

name (str) – Name to be changed from camel case to using underscores.

Returns

name – Name changed from camel case to using underscores.

Return type

str

pymetacode.utils.underscore_to_camel_case(name='')

Change string from underscores to using camel case.

According to PEP8, class names should follow camel case convention, whereas methods, functions, and variables should use underscores.

Parameters

name (str) – Name to be changed from underscores to using camel case.

Returns

name – Name changed from underscores to using camel case.

Return type

str

class pymetacode.utils.Template(path='', template='', context=None, destination='')

Bases: object

Wrapper for using the template engine (Jinja2).

Dealing with templates requires a number of settings to be made, namely the source of the template, its name, the context (i.e., the dictionary containing the variables to be replaced within the template), and the destination the rendered template should be output to.

Note

Regarding the path of a template, three different places are looked up, using the function get_package_data(): first in the user data directory, then in the site data directory, and finally within the package (distribution). For details see the description of the function get_package_data().

path

Location where the template resides (see note above).

Type

str

template

Name of the template to be used.

Relative to the package_path.

In case you want to retrieve a template for a package different than pymetacode, prefix the template name with the package name, using ‘@’ as a separator. See get_package_data() for details.

Type

str

context

Key-value store of variables to be replaced within the template.

Type

dict

destination

Name of the file the rendered template should be output to.

Type

str

Examples

Probably the best way to use the class is to instantiate an object providing all necessary parameters:

template = Template(
    path='some/relative/path/to/the/template',
    template='name_of_the_template',
    context=dict(),
    destination='name_of_the_file_to_output_rendered_template_to',
)

Afterwards, the respective command can be issued on the template, depending on whether the rendered template should be written or appended to the destination:

template.create()
template.append()
property environment

Environment used by the template engine.

Read-only property that gets automatically set from the class properties.

Returns

env – Environment settings for the template engine.

Return type

jinja2.Environment

render()

Render the template.

Returns

content – Rendered template.

Return type

str

create()

Write rendered template to a file.

Note: If you need to append the rendered template to an existing file, you should use append() instead.

append()

Append rendered template to a file.

Note: If you need to output the rendered template to a file, removing all previous content of this file, you should use create() instead.

pymetacode.utils.package_version_from_file()

Obtain version of the given package by reading file “VERSION”.

The function attempts to read the version number from the file “VERSION” in the current working directory. Therefore, if this file does not exist, a FileNotFoundError will be raised.

Returns

version – Version string as contained in file “VERSION” in current directory

Return type

str

pymetacode.utils.make_executable(path='')

Make a file executable, i.e., set its executable flag.

Only for those allowed to read the file, the executable flag will be set.

Parameters
  • path (str) – Name of the path/file to make executable

  • http (Taken from) –

  • versionadded: (.) – 0.4:

pymetacode.utils.add_to_toctree(filename='', entries=None, sort=False, after='')

Add entries to toc tree.

Adding lines to toctrees in documentation generated with Sphinx is a frequent task for a metacode package. A toctree in Sphinx typically looks similar to the following:

.. toctree::
    :maxdepth: 1

    first_entry

The .. toctree:: directive is followed by (optional) parameters, the actual entries appear after a blank line and are indented.

This function allows to add arbitrary entries to a given toctree, as entries are given as list.

If you would like to have the toctree entries sorted alphabetically, make sure to set the sort parameter to True.

In case of several toctrees in one document, you may use the after parameter to provide a string as a marker. This string is searched for treating it as substring through all lines of the file, and the first matching line used as actual offset.

In case of the file not containing any toctree directive or the string provided by after not being found, exceptions will be thrown.

Parameters
  • filename (str) – Name of the file containing the toctree

  • entries (list) –

    lines to be added to the toctree

    Note that regardless of the leading whitespace, entries are left stripped and indented with four spaces.

  • sort (bool) –

    Whether to sort all toctree entries (alphabetically) after insert

    Default: False

  • after (str) –

    String used as marker below which we look for a toctree

    Useful particularly in case of several toctree directives in one document. Note that the string provided is used as substring: The first line containing it will be used as offset.

New in version 0.5.