Skip to main content

a framework to help you stay on top of what data is flowing through your code.

Project description

:moyai: pyalert

pypi python code style code coverage pre-commit ci

pyalert is a lightweight framework that empowers you to stay on top of what data is flowing through your Python code.

pyalert is designed to:

  1. mimic the feeling of writing unit tests
  2. unobtrusively add runtime alerting to your Python code

:mag: Table of Contents


:gear: How Does It Work?

Suppose you are building software that involves processing data from a bike race...

class Biker:
    def __init__(self, name: str, age: int, avg_mph: int):
        self.name = name
        self.age = age
        self.avg_mph = avg_mph


def bike_race(bikers: List[Biker]) -> str:
    """A bike race that takes three Bikers and returns a winner announcement"""
    winner = max(bikers, key=lambda biker: biker.avg_mph)
    return f"{winner.name} wins with an avg mph of {winner.avg_mph}!"

Instead of cluttering your code with complex catching logic and log/print statements to indicate when certain occurances arise, pyalert facilitates an intuitive segregation of such concerns with a unit-test-like design pattern...

As is often done with unit tests, let's define a class that contains our alerts...

Note how we use the @alert_conf decorator to configure the runtime behavior of each alert method...

from pyalert import PyAlerts, alert_conf

# must inherit from PyAlerts
class BikeRaceAlerts(PyAlerts):
    """Alerts for the bike_race function"""

    # takes="input" configures the alert method to take the input args as its args
    @alert_conf(takes="input", raise_error=False)
    def suspiciously_fast_bikers(self, *args, **kwargs):
        bikers = args[0] if args else kwargs["bikers"]
        msg = "Suspicious speeds--implement compulsory drug testing after the race!"
        assert not any(b.avg_mph > 30), msg

    # raise_error=True will cause an error to be raised if the assertion fails
    @alert_conf(takes="input", raise_error=True)
    def impossibly_fast_bikers(self, *args, **kwargs):
        bikers = args[0] if args else kwargs["bikers"]
        msg = "Stop the race! we need to recalibrate our speedometers!"
        assert not any(b.avg_mph > 100 for b in bikers), msg

    # takes="output" configures the alert method to take the return value as its arg
    @alert_conf(takes="output", raise_error=False)
    def verbose_winner_announcement(self, return_value):
        msg = "The winner announcement came out unusually verbose!"
        assert len(return_value) < 120, msg

Lastly, we'll decorate our original bike_race function with the @pyalert decorator and pass BikeRaceAlerts as the argument:

from pyalert import pyalert

@pyalert(pyalerts=BikeRaceAlerts)
def bike_race(bikers: List[Biker]) -> str:
    ...

And voila, pyalerts will take care of our alerts at runtime!


:memo: In Summary

pyalert has 3 fundamental abstractions...
flowchart TD
    pkg("📦 pyalert")
    pa("1️⃣\n@pyalert\n(decorator)")
    PA("2️⃣\nPyAlerts\n(base class)")
    ac("3️⃣\n@alert_conf\n(decorator)")

    
    pkg --- pa
    pkg --- PA
    pkg --- ac
They are used as follows:
flowchart LR
    pa("1️⃣\n@pyalert\n(decorator)\n📦")
    yPA("PyAlerts\nchild\nclass\n👤")
    PA("2️⃣\nPyAlerts\n(base class)\n📦")
    am("alert\nmethods\n👤")
    yf("your\nfunction\n👤")
    as("assert\nstatements\n🐍")
    ac("3️⃣\n@alert_conf\n(decorator)\n📦")

    pa -. takes a --> yPA
    pa -. and\ndecorates --> yf
    yPA -. and which\ncontains --> am
    yPA -. which\ninherits\nfrom --> PA
    am -. that use --> as
    am -. and are\nconfigured\nby --> ac

:brain: Further Thinking

====== :thought_balloon: :one: ======

pyalerts is designed to enhance your project by:

  1. :desktop_computer: - helping drive development with quicker insights
  2. :eye: - helping with the monitoring of data phenomena in production

...all while introducing minimal boilerplate to your source code.

====== :thought_balloon: :two: ======

pyalerts can be used minimally as a mere seperation-of-tasks practice within a single file...

or

...since it feels so much like unit testing, why not create an alerts folder that mirrors your src code folder structure?

 📦project
  📂src
  📂tests
  📂alerts
  📜README.md
  📜requirements.txt

====== :thought_balloon: :three: ======

Are you inheriting a codebase that is already jam-packed with verbosity and complexity?

Since the only boilerplate required to use pyalerts is a simple @pyalert decorator, you can easily add it to your codebase without worrying about the repurcussions.

:construction: Roadmap

  • :exclamation: Conceive/implement integration strategy with logging
  • :exclamation: CI pipeline
  • :exclamation: Docs

:handshake: Contribution

If you think there is open-source merit to this idea, or have any thoughts on how its proposed utility might be nullified by other existing tools, please let me know!

Here is a single-question form where you can voice your enthusiasm about the idea of pyalert being developed/maintained on a scale of 1-10.

If you would like contribute and collaborate, please reach out to me on LinkedIn!

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyalert-0.0.2.tar.gz (5.8 kB view details)

Uploaded Source

Built Distribution

pyalert-0.0.2-py3-none-any.whl (5.9 kB view details)

Uploaded Python 3

File details

Details for the file pyalert-0.0.2.tar.gz.

File metadata

  • Download URL: pyalert-0.0.2.tar.gz
  • Upload date:
  • Size: 5.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.10.5

File hashes

Hashes for pyalert-0.0.2.tar.gz
Algorithm Hash digest
SHA256 bf1cbb17662f59403582502dbd3eff64bb1d186586c98994a094f07f924dc268
MD5 32843274698569d67e4777a2d226d18f
BLAKE2b-256 01ac9035742d920b73afb06a8e8720aa62c66c0e25c1cfe1d1e4dc71a9a6b48b

See more details on using hashes here.

File details

Details for the file pyalert-0.0.2-py3-none-any.whl.

File metadata

  • Download URL: pyalert-0.0.2-py3-none-any.whl
  • Upload date:
  • Size: 5.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.10.5

File hashes

Hashes for pyalert-0.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 9dab7e8253891d5e4399ca2dc69bbee4d881b30bb008cca876bf8ea691b485a2
MD5 01e4412ad1aada970ee4e3614e968118
BLAKE2b-256 cb62f34eb85827e9dc63491bf748d0c0fbcedb55079a9deb46a99f7d36a13f2f

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page