pyramid icon indicating copy to clipboard operation
pyramid copied to clipboard

record the configuration uri and a hash of it's contents to settings when parsed

Open jvanasco opened this issue 1 year ago • 1 comments

Feature Request

When Pyramid is started with a configuration file - such as pserve example.ini or registered_script example.ini - the configuration file and an md5 of it's raw contents should be put into the global_config and/or settings. The global config currently has the filepath as a __file__ key, but I think this is an implementation detail of pserve with .ini's (I am not sure on this).

Having this information easily accessible has become important for both troubleshooting and ensuring an invoked utility script is compatible with a live Pyramid application if they interact.

For example, it allows this usage to be easily detected and fail:

pserve production.ini
cleanup_stale_data development.ini

Describe the solution you'd like I think this should be consistently available, however my understanding of Pyramid suggests it could happen in one or more of the functions in pyramid.paster (https://github.com/Pylons/pyramid/blob/main/src/pyramid/paster.py)

I would be happy to generate a PR for this, I just know that I am likely to miss multiple contexts of where these files are parsed. If the maintainers like this idea- if given an overview, I will generate a PR.

Describe alternatives you've considered I calculate this myself, but this seems to be possible based on my contexts - and things may change if other file formats/parsers are used.

Additional context This is somewhat inspired by poetry lockfiles.

Initially I thought of this to ensure I can coordinate a Server with a Script. The exact use case is that a live Pyramid Application responds to ACME challenges, but a Pyramid Script invoked by cron is used to check for expiring Certificates and will order renewals. In this usage, I need to ensure the script and application are both using the same configuration file - so they both calculate the hash, and the Pyramid application publishes it on a private url.

This was quick to implement on an application in developer-space, and then I realized I could easily integrate the hash into error logging to aid in post-deployment troubleshooting.

jvanasco avatar Jan 09 '25 17:01 jvanasco

In case anyone needs something similar, here is a version of the code I used:

import hashlib
import os
from typing import Dict
from typing import Optional
import uuid


def normalize_filepath(fpath: str) -> str:
    fpath = os.path.normpath(fpath)
    if fpath[-1] == "/":
        fpath = fpath[:-1]
    return fpath


def upgrade_settings(
    settings: Dict,
    config_uri: Optional[str] = None,
) -> Dict:
    """
    upgrades Pyramid Application settings to help identify the configuration.
    
    While `config_uri` is stashed into the settings dict for convenience,
    it should not be exposed.
    
    Instead, the following two items are safe to expose to clients:
    
    * `config_uri-path` - a hashed value of the filepath
    * `config_uri-contents` - a hashed value of the file contents
    
    Additionally, `mac_uuid` will contain a uuid identifying the machine, based
    on the network mac address.  There are more robust approaches to this, but
    it suffices for these needs.  see
    https://stackoverflow.com/questions/36235807/fixed-identifier-for-a-machine-uuid-getnode
    """
    if config_uri:
        settings["config_uri"] = config_uri
        _hash = hashlib.md5(config_uri.encode()).hexdigest()
        settings["config_uri-path"] = _hash
        with open(config_uri, "rb") as f:
            _contents = f.read()
            _hash = hashlib.md5(_contents).hexdigest()
            settings["config_uri-contents"] = _hash
    mac = uuid.getnode()
    settings["mac_uuid"] = str(uuid.UUID(int=mac))
    return settings

jvanasco avatar Mar 04 '25 18:03 jvanasco