mirror of
https://github.com/huggingface/knockknock.git
synced 2021-08-28 00:30:42 +03:00
commit lib
This commit is contained in:
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Hugging Face
|
||||
Copyright (c) 2019 Victor SANH and Hugging Face
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
55
README.md
55
README.md
@@ -1,2 +1,53 @@
|
||||
# knockknock
|
||||
🚪👋Knock Knock: Be notified when your training is finished with only two additional lines of code
|
||||
# Knock Knock
|
||||
|
||||
A small library to get a notification you when your training is complete or when it crashes during the process with two additional lines of code.
|
||||
|
||||
When training deep learning models, it is common to use early stopping. Apart from a rough estimate, it is difficult to predict when the training will finish. Then, it can be interesting to set up automatic notifications for your training. It is also interesting to be notified when your training crashes in the middle of the process for unexpected reasons.
|
||||
|
||||
## Installation
|
||||
|
||||
Install with `pip` or equivalent.
|
||||
```bash
|
||||
pip install .
|
||||
```
|
||||
|
||||
This code has only been tested with Python 3.6.
|
||||
|
||||
## Usage
|
||||
|
||||
The library is designed to be used in a seamless way, with minimal code modification: you only need to add a decorator on top your main function call.
|
||||
|
||||
There are currently two ways to setup notifications: email and Slack.
|
||||
|
||||
### Email
|
||||
|
||||
The service relies on [Yagmail](https://github.com/kootenpv/yagmail) a GMAIL/SMTP client. You'll need a gmail email address to use it (you can setup one [here](https://accounts.google.com), it's free). I recommend creating a new one (rather than your usual one) since you'll have to modify the account's security settings to allow the Python library to access it by [Turning on less secure apps](https://devanswers.co/allow-less-secure-apps-access-gmail-account/).
|
||||
|
||||
```python
|
||||
from knockknock import email_sender
|
||||
|
||||
@email_sender(recipient_email: "<your_email@address.com>", sender_email: "<grandma's_email@gmail.com>")
|
||||
def train_your_nicest_model(your_nicest_parameters):
|
||||
import time
|
||||
time.sleep(10000)
|
||||
```
|
||||
|
||||
If `sender_email` is not specified, then `recipient_email` will be also used for sending.
|
||||
|
||||
Note that launching this will asks you for the sender's email password. It will be safely stored in the system keyring service through the [`keyring` Python library](https://pypi.org/project/keyring/).
|
||||
|
||||
### Slack
|
||||
|
||||
Similarly, you can also use Slack to get notifications. You'll have to get your Slack room [weebhook URL](https://api.slack.com/incoming-webhooks#create_a_webhook) and optionally your [user id](https://api.slack.com/methods/users.identity) (if you want to tag yourself or someone else).
|
||||
|
||||
```python
|
||||
from knockknock import slack_sender
|
||||
|
||||
webhook_url = "<webhook_url_to_your_slack_room>"
|
||||
@slack_sender(webhook_url=webhook_url, channel="<your_favorite_slack_channel>")
|
||||
def train_your_nicest_model(your_nicest_parameters):
|
||||
import time
|
||||
time.sleep(10000)
|
||||
```
|
||||
|
||||
You can also specify an optional argument to tag specific people: `user_mentions=[<your_slack_id>, <grandma's_slack_id>]`.
|
||||
|
||||
BIN
knockknock/.DS_Store
vendored
Normal file
BIN
knockknock/.DS_Store
vendored
Normal file
Binary file not shown.
2
knockknock/__init__.py
Normal file
2
knockknock/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from knockknock.email_sender import email_sender
|
||||
from knockknock.slack_sender import slack_sender
|
||||
66
knockknock/email_sender.py
Normal file
66
knockknock/email_sender.py
Normal file
@@ -0,0 +1,66 @@
|
||||
import datetime
|
||||
import traceback
|
||||
import functools
|
||||
import socket
|
||||
import yagmail
|
||||
|
||||
DATE_FORMAT = "%Y-%m-%d %H:%M:%d"
|
||||
|
||||
def email_sender(recipient_email: str, sender_email: str = None):
|
||||
"""
|
||||
Email sender wrapper: execute func, send an email with the end status
|
||||
(sucessfully finished or crashed) at the end. Also send an email before
|
||||
executing func.
|
||||
|
||||
`recipient_email`: str
|
||||
The email address to notify.
|
||||
`sender_email`: str (default=None)
|
||||
The email adress to send the messages. If None, use the same
|
||||
address as `recipient_email`.
|
||||
"""
|
||||
if sender_email is None:
|
||||
sender_email = recipient_email
|
||||
yag_sender = yagmail.SMTP(sender_email)
|
||||
|
||||
def decorator_sender(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper_sender(*args, **kwargs):
|
||||
|
||||
start_time = datetime.datetime.now()
|
||||
contents = ['Your training has started.',
|
||||
'Machine name: %s' % socket.gethostname(),
|
||||
'Main call: %s' % func.__name__,
|
||||
'Starting date: %s' % start_time.strftime(DATE_FORMAT)]
|
||||
yag_sender.send(recipient_email, 'Training has started 🎬', contents)
|
||||
|
||||
try:
|
||||
value = func(*args, **kwargs)
|
||||
end_time = datetime.datetime.now()
|
||||
elapsed_time = end_time - start_time
|
||||
contents = ["Your training is complete.",
|
||||
'Machine name: %s' % socket.gethostname(),
|
||||
'Main call: %s' % func.__name__,
|
||||
'Starting date: %s' % start_time.strftime(DATE_FORMAT),
|
||||
'End date: %s' % end_time.strftime(DATE_FORMAT),
|
||||
'Training duration: %s' % str(elapsed_time)]
|
||||
yag_sender.send(recipient_email, 'Training has sucessfully finished 🎉', contents)
|
||||
return value
|
||||
|
||||
except Exception as ex:
|
||||
end_time = datetime.datetime.now()
|
||||
elapsed_time = end_time - start_time
|
||||
contents = ["Your training has crashed.",
|
||||
'Machine name: %s' % socket.gethostname(),
|
||||
'Main call: %s' % func.__name__,
|
||||
'Starting date: %s' % start_time.strftime(DATE_FORMAT),
|
||||
'Crash date: %s' % end_time.strftime(DATE_FORMAT),
|
||||
'Crashed training duration: %s\n\n' % str(elapsed_time),
|
||||
"Here's the error:",
|
||||
'%s\n\n' % ex,
|
||||
"Traceback",
|
||||
'%s' % traceback.format_exc()]
|
||||
yag_sender.send(recipient_email, 'Training has crashed ☠️', contents)
|
||||
|
||||
return wrapper_sender
|
||||
|
||||
return decorator_sender
|
||||
82
knockknock/slack_sender.py
Normal file
82
knockknock/slack_sender.py
Normal file
@@ -0,0 +1,82 @@
|
||||
from typing import List
|
||||
import datetime
|
||||
import traceback
|
||||
import functools
|
||||
import json
|
||||
import socket
|
||||
import requests
|
||||
|
||||
DATE_FORMAT = "%Y-%m-%d %H:%M:%d"
|
||||
|
||||
def slack_sender(webhook_url: str, channel: str, user_mentions: List[str] = []):
|
||||
"""
|
||||
Email sender wrapper: execute func, send an email with the end status
|
||||
(sucessfully finished or crashed) at the end. Also send an email before
|
||||
executing func.
|
||||
|
||||
`webhook_url`: str
|
||||
The webhook URL to access your slack room.
|
||||
Visit https://api.slack.com/incoming-webhooks#create_a_webhook for more details.
|
||||
`channel`: str
|
||||
The slack room to log.
|
||||
`user_mentions`: List[str] (default=[])
|
||||
Optional users ids to notify.
|
||||
Visit https://api.slack.com/methods/users.identity for more details.
|
||||
"""
|
||||
|
||||
dump = {
|
||||
"username": "Knock Knock",
|
||||
"channel": channel,
|
||||
"icon_emoji": ":clapper:",
|
||||
}
|
||||
def decorator_sender(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper_sender(*args, **kwargs):
|
||||
|
||||
start_time = datetime.datetime.now()
|
||||
contents = ['Your training has started 🎬',
|
||||
'Machine name: %s' % socket.gethostname(),
|
||||
'Main call: %s' % func.__name__,
|
||||
'Starting date: {start_time.strftime(DATE_FORMAT)}']
|
||||
contents.append(' '.join(user_mentions))
|
||||
dump['text'] = '\n'.join(contents)
|
||||
dump['icon_emoji'] = ':clapper:'
|
||||
requests.post(webhook_url, json.dumps(dump))
|
||||
|
||||
try:
|
||||
value = func(*args, **kwargs)
|
||||
end_time = datetime.datetime.now()
|
||||
elapsed_time = end_time - start_time
|
||||
contents = ["Your training is complete 🎉",
|
||||
'Machine name: %s' % socket.gethostname(),
|
||||
'Main call: %s' % func.__name__,
|
||||
'Starting date: %s' % start_time.strftime(DATE_FORMAT),
|
||||
'End date: %s' % end_time.strftime(DATE_FORMAT),
|
||||
'Training duration: %s' % str(elapsed_time)]
|
||||
contents.append(' '.join(user_mentions))
|
||||
dump['text'] = '\n'.join(contents)
|
||||
dump['icon_emoji'] = ':tada:'
|
||||
requests.post(webhook_url, json.dumps(dump))
|
||||
return value
|
||||
|
||||
except Exception as ex:
|
||||
end_time = datetime.datetime.now()
|
||||
elapsed_time = end_time - start_time
|
||||
contents = ["Your training has crashed ☠️",
|
||||
'Machine name: %s' % socket.gethostname(),
|
||||
'Main call: %s' % func.__name__,
|
||||
'Starting date: %s' % start_time.strftime(DATE_FORMAT),
|
||||
'Crash date: %s' % end_time.strftime(DATE_FORMAT),
|
||||
'Crashed training duration: %s\n\n' % str(elapsed_time),
|
||||
"Here's the error:",
|
||||
'%s\n\n' % ex,
|
||||
"Traceback",
|
||||
'%s' % traceback.format_exc()]
|
||||
contents.append(' '.join(user_mentions))
|
||||
dump['text'] = '\n'.join(contents)
|
||||
dump['icon_emoji'] = ':skull_and_crossbones:'
|
||||
requests.post(webhook_url, json.dumps(dump))
|
||||
|
||||
return wrapper_sender
|
||||
|
||||
return decorator_sender
|
||||
18
setup.py
Normal file
18
setup.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name='knockknock',
|
||||
version='0.1',
|
||||
description='Be notified when your training is finished with only two additional lines of code',
|
||||
url='http://github.com/huggingface/knockknock',
|
||||
author='Victor SANH',
|
||||
author_email='victorsanh@gmail.com',
|
||||
license='MIT',
|
||||
packages=find_packages(),
|
||||
zip_safe=False,
|
||||
python_requires='>=3.6',
|
||||
install_requires=[
|
||||
'yagmail>=0.11.214',
|
||||
'keyring'
|
||||
]
|
||||
)
|
||||
Reference in New Issue
Block a user