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.
- chapps.util.hash_password(password, encoding='utf-8')
- class chapps.util.VenvDetector
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.
Pathpointing at location where data files are installed
boolindicating whether a virtual environment is active
Pathpointing at the location of the Markdown
Pathpointing at the config file
Pathto the root of the active virtual environment, or None if none is active
- __init__(*, datapath=None)
Detect virtual environments and provide local package paths
Return the non-virtual base prefix
sys.real_prefix, so we check for both.
the base path prefix
- Return type
Compare prefixes to determine if a virtual environment is active.
true if a virtual environment is active, otherwise False
- Return type
Determine whether invoked by Sphinx
- property docpath: pathlib.Path
Memoizes the documentation location
- property confpath: pathlib.Path
Memoizes the config file’s full path
- property venvpath: Optional[pathlib.Path]
The virtual environment root, if any
None or the value of
- Return type
If no virtual environment is active, then
Noneis returned, otherwise a
Pathinstance is returned, containing the path to the virtual environment. This hasn’t been tested with all types of virtual environment.
- class chapps.util.AttrDict
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
CHAPPSConfigfor holding the configuration data.
The purpose of this class is to map all the keys of a fairly small
dictas attributes of the instance onto their values in the source dict. This class does not perform the same sort of lazy-loading as the
PostfixPolicyRequestclass below; it pre-maps all the elements in the source
dict. So be careful about passing large dictionaries to it.
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
- boolean_pattern = re.compile('^[Tt]rue|[Ff]alse$')
A regex to detect text-string boolean values
- __init__(data=None, **kwargs)
Populate an instance with attributes
If, and only if,
datais not provided, then the keyword arguments will be used in place of data provided as a
Henceforth whatever is rounded up to use shall be referred to as the
The initialization routine creates an attribute on the instance for each key in the
data, and then attempts to cast the value:
TypeErroris encountered, the unadulterated value is used.
if that causes another
ValueErrorthen it is matched against the
boolean_patternto 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:
If it does not test positive for truth, it is considered to be
But if it wasn’t a match for the
boolean_patternat 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
strings, because the
AttrDictis being initialized with a sub-block of a
ConfigParseras the source object, and its values will all be strings.
- keys() a set-like object providing a view on D's keys
- class chapps.util.PostfixPolicyRequest
Lazy-loading Policy Request Mapping Interface
An implementation of
Mappingwhich 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", "firstname.lastname@example.org", "email@example.com", "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", "firstname.lastname@example.org", "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 documentationfor more information.
As an example of the class’s utility, and using the above definition of
from chapps.util import PostfixPolicyRequest ppr = PostfixPolicyRequest(payload) # all the following are true: ppr.sender == 'email@example.com' ppr.sasl_username == 'firstname.lastname@example.org' ppr.client_address == '10.10.10.10' # demonstrating the pseudo-attribute: ppr.recipients == ['email@example.com']
Instance attributes (apart from Postfix payload parameters):
the Postfix policy delegation request payload in string-per-line format
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 a
recipient_countof 0 before the DATA phase, so we rely upon counting the email addresses directly.
memoization attribute for
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.
Overloaded in order to search for missing attributes in the payload
attr (str) – the attribute which triggered this call
the value found in the payload, or
- Return type
First, if the value of
attrstarts with an underscore,
Noneis 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
attragainst everything before the
=sign. When a line is found, the contents after the
=are stored as an attribute named
attr(and so memoized), and the value is returned. Future attempts to obtain the value will encounter the attribute and not invoke
DEBUGlevel message is currently produced if no lines in the payload matched the requested payload data. No errors are produced if a nonexistent
- __init__(payload, *args, **kwargs)
Store the payload.
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
- property recipients: List[str]
Memoize recipients as a list
a list of strings which are the email addresses of recipients
- Return type
A convenience method to split the ‘recipient’ datum into comma-separated tokens for easier counting.
Given an email address, return the domain part
Raises meaningful errors if nonconforming conditions are encountered.