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¶
-
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.
-
Context manager for temporarily changing the working directory.
-
Make a file executable, i.e., set its executable flag.
String manipulation¶
-
Change string from camel case to using underscores.
-
Change string from underscores to using camel case.
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 entries to toctrees in documentation generated via Sphinx
Helper classes¶
-
Mixin class for returning all public attributes as dict.
-
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:
Within the package,
In the site-wide data directory (with the package name as subdirectory),
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
- Returns
contents – String containing the contents of the non-code file.
- Return type
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
- _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
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
- 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.
- 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.
- 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 functionget_package_data()
.- 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
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
- 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
- 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 executablehttp (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 toTrue
.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 toctreeentries (
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.