chapps.util module
Utility classes
Since CHAPPS mainly deals with policy requests coming from Postfix, there is a utility object for representing them in a way which makes the code easier to read while also providing access optimizations.
There is another utility class for providing the configuration data via an object which presents dictionary keys as attributes.
In order to create different defaults and access documentation files, an object is provided which detects whether the library is running within a virtual environment, and serves as a source of local paths to package resources.
- class chapps.util.VenvDetector[source]
Bases:
object
Detect use of a virtual environment and calculate local paths
The detector encapsulates the job of determining whether a virtual environment is activated, and assists in composing some important paths in order to locate certain files the application needs.
One of those files is Markdown source which is imported into the live API documentation. Another is the config file.
Instance attributes:
- datapath
Path
pointing at location where data files are installed- ve
bool
indicating whether a virtual environment is active- docpath
Path
pointing at the location of the Markdown- confpath
Path
pointing at the config file- venvpath
Path
to the root of the active virtual environment, or None if none is active
- get_base_prefix_compat()[source]
Return the non-virtual base prefix
Sometimes called
sys.real_prefix
, so we check for both.- Returns
the base path prefix
- Return type
- in_virtualenv()[source]
Compare prefixes to determine if a virtual environment is active.
- Returns
true if a virtual environment is active, otherwise False
- Return type
- property ve: bool
Property which memoizes
in_virtualenv()
- Return type
- property sb: bool
Property which memoizes
sphinx_build()
- Return type
- property docpath: pathlib.Path
Memoizes the documentation location
- Returns
a
Path
pointing at the Markdown files’ location- Return type
- property confpath: pathlib.Path
Memoizes the config file’s full path
- Returns
a
Path
pointing at the config file- Return type
- property venvpath: Optional[pathlib.Path]
The virtual environment root, if any
- Returns
None or the value of
sys.prefix
as aPath
- Return type
Optional[pathlib.Path]
If no virtual environment is active, then
None
is returned, otherwise aPath
instance is returned, containing the path to the virtual environment. This hasn’t been tested with all types of virtual environment.
- class chapps.util.AttrDict[source]
Bases:
collections.abc.Mapping
Attribute Dictionary
This simple class allows accessing the keys of a hash as attributes on an object. As a useful side effect it also casts floats, integers and booleans in advance.
This object is used in
CHAPPSConfig
for holding the configuration data.Note
The purpose of this class is to map all the keys of a fairly small
dict
as attributes of the instance onto their values in the source dict. This class does not perform the same sort of lazy-loading as thePostfixPolicyRequest
class below; it pre-maps all the elements in the sourcedict
. So be careful about passing large dictionaries to it.Subclassing
Given the stated purpose of the class, all internal instance attributes, i.e. ones not associated to a key-value pair in the source object, should begin with
_
(an underscore).- boolean_pattern = re.compile('^[Tt]rue|[Ff]alse$')
A regex to detect text-string boolean values
- __init__(data=None, **kwargs)[source]
Populate an instance with attributes
- Parameters
If, and only if,
data
is not provided, then the keyword arguments will be used in place of data provided as adict
.Henceforth whatever is rounded up to use shall be referred to as the
data
.The initialization routine creates an attribute on the instance for each key in the
data
, and then attempts to cast the value:to an
int
.If a
TypeError
is encountered, the unadulterated value is used.If only
ValueError
is raised, then it is casted tofloat
if that causes another
ValueError
then it is matched against theboolean_pattern
to see whether it matches, which is to say, whether it is a string containing “true” or “false”.If so, a simple check is conducted to determine whether the match was four characters long:
True
.If it does not test positive for truth, it is considered to be
False
.But if it wasn’t a match for the
boolean_pattern
at all, then its original value is preserved.
Generally, instances of this class are used to present a particular module, such as a policy manager, with its configuration in a form which can be dereferenced with dot notation. As such, values which cannot be casted to some other type are almost always left in their original form as
str
ings, because theAttrDict
is being initialized with a sub-block of aConfigParser
as the source object, and its values will all be strings.
- class chapps.util.PostfixPolicyRequest[source]
Bases:
collections.abc.Mapping
Lazy-loading Policy Request Mapping Interface
An implementation of
Mapping
which by default only processes and caches values from the data payload when they are accessed, to avoid a bunch of useless parsing. Instances may be dereferenced like hashes, but the keys are also attributes on the instance, so they can be accessed without brackets and quotation marks.Once parsed, results are memoized.
For example, a payload might look a bit like this, when it is first received from Postfix and turned into an array of one string per line:
payload = [ "request=smtpd_access_policy", "protocol_state=RCPT", "protocol_name=SMTP", "helo_name=helo.chapps.io", "queue_id=8045F2AB23", "sender=unauth@easydns.com", "recipient=bar@foo.tld", "recipient_count=0", "client_address=10.10.10.10", "client_name=mail.chapps.io", "reverse_client_name=mail.chapps.io", "instance=a483.61706bf9.17663.0", "sasl_method=plain", "sasl_username=somebody@chapps.io", "sasl_sender=", "size=12345", "ccert_subject=", "ccert_issuer=Caleb+20Cullen", "ccert_fingerprint=DE:AD:BE:EF:FE:ED:AD:DE:D0:A7:52:F3:C1:DA:6E:04", "encryption_protocol=TLSv1/SSLv3", "encryption_cipher=DHE-RSA-AES256-SHA", "encryption_keysize=256", "etrn_domain=", "stress=", "ccert_pubkey_fingerprint=68:B3:29:DA:98:93:E3:40:99:C7:D8:AD:5C:B9:C9:40", "client_port=1234", "policy_context=submission", "server_address=10.3.2.1", "server_port=54321", "", ]
Refer to the
Postfix policy delegation documentation
for more information.As an example of the class’s utility, and using the above definition of
payload
, consider:from chapps.util import PostfixPolicyRequest ppr = PostfixPolicyRequest(payload) # all the following are true: ppr.sender == 'unauth@easydns.com' ppr.sasl_username == 'somebody@chapps.io' ppr.client_address == '10.10.10.10' # demonstrating the pseudo-attribute: ppr.recipients == ['bar@foo.tld']
Instance attributes (apart from Postfix payload parameters):
- _payload
the Postfix policy delegation request payload in string-per-line format
- recipients
a pseudo-attribute of the policy request derived from the value of
recipient
, provided by Postfix, which may contain more than one comma-separated email address. For reasons unknown, Postfix always provides arecipient_count
of 0 before the DATA phase, so we rely upon counting the email addresses directly.- _recipients
memoization attribute for
recipients()
Subclassing
Because the purpose of the class is to present the contents of the initial payload as attributes, all internal attributes are prefaced with an underscore.
- __getattr__(attr)[source]
Overloaded in order to search for missing attributes in the payload
- Parameters
attr (str) – the attribute which triggered this call
- Returns
the value found in the payload, or
None
- Return type
Optional[str]
First, if the value of
attr
starts with an underscore,None
is returned. No lines of the payload start with an underscore. This ensures that references to internal attributes of the class are not snarled up with the payload searches.Next, the payload is searched for the requested key-value pair, attempting to match
attr
against everything before the=
sign. When a line is found, the contents after the=
are stored as an attribute namedattr
(and so memoized), and the value is returned. Future attempts to obtain the value will encounter the attribute and not invoke__getattr__()
again.A
DEBUG
level message is currently produced if no lines in the payload matched the requested payload data. No errors are produced if a nonexistentattr
starting with_
is encountered.
- __init__(payload, *args, **kwargs)[source]
Store the payload.
- Parameters
payload (List[str]) – strings which are formatted as ‘key=val’, including an empty entry at the end.
This routine discards the last element of the list and stores the rest as
self._payload
.
- property recipients: List[str]
Memoize recipients as a list
- Returns
a list of strings which are the email addresses of recipients
- Return type
List[str]
A convenience method to split the ‘recipient’ datum into comma-separated tokens for easier counting.