Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
db6de0a
Fix reloading issue with multiple decorated functions
lruanova Jun 15, 2024
fb07929
Preserve signature of wrapped function.
nneskildsf Dec 14, 2024
b7e6b95
Add test case regarding preservation of function signature
nneskildsf Dec 14, 2024
d4eb21b
Fix typo
nneskildsf Dec 14, 2024
e875907
Merge pull request #1 from kirle/fix_decorators
nneskildsf Dec 15, 2024
0ae48c5
Configure testing workflow
eskildsf Dec 15, 2024
502533d
Specify exact version numbers of Python
eskildsf Dec 15, 2024
da57c51
Test python versions relevant for Ubuntu 22.04
eskildsf Dec 15, 2024
368f539
Test python versions relevant for Ubuntu 24.04
eskildsf Dec 15, 2024
6b7bdfa
Add pyright as developer dependency
eskildsf Dec 15, 2024
21a6e91
Merge pull request #3 from nneskildsf/2-implement-github-actions-for-…
nneskildsf Dec 15, 2024
0a0036c
Add typing. Fix tests. Add comments.
eskildsf Dec 17, 2024
21bf0c2
Fix typing for python 3.8
eskildsf Dec 17, 2024
4a2d898
Merge pull request #5 from nneskildsf/4-fix-tests-and-pyright-errors
nneskildsf Dec 17, 2024
d235053
Bump version number reflecting API change. Upadte list of supported p…
eskildsf Dec 17, 2024
c63fe0c
Bump version number reflecting API change. Upadte list of supported p…
eskildsf Dec 17, 2024
664c759
Merge pull request #8 from nneskildsf/4-fix-tests-and-pyright-errors
nneskildsf Dec 17, 2024
8507622
Merge remote-tracking branch 'refs/remotes/origin/master'
eskildsf Dec 17, 2024
ec3b4b2
Allow 'break' in loop.
eskildsf Dec 18, 2024
b84a4b7
Drop support for Python 3.8
eskildsf Dec 18, 2024
a28ad6a
Add logging.
eskildsf Dec 18, 2024
4aa5a8d
Fix typo
eskildsf Dec 18, 2024
e4b89fd
Update README.md
nneskildsf Dec 18, 2024
07d9d95
Add helpful log messages.
eskildsf Dec 19, 2024
c7e9104
Merge remote-tracking branch 'refs/remotes/origin/master'
eskildsf Dec 19, 2024
3923ef4
Update README.md
nneskildsf Dec 19, 2024
cb42029
Only give warning if relevant.
eskildsf Dec 19, 2024
8cda4da
Remove stale examples. Readme example of how to install without git.
eskildsf Dec 19, 2024
22d670d
Handle empty iterator.
eskildsf Dec 19, 2024
abc9ccd
Refactor replacement of break and continue
eskildsf Dec 19, 2024
afc10e5
Support more Python versions by avoiding ast.unparse
eskildsf Dec 19, 2024
6b64d94
Fix CI
eskildsf Dec 19, 2024
a9fd696
Fix possible race condition.
eskildsf Dec 19, 2024
4d6efa6
Add download URL.
eskildsf Dec 19, 2024
51a5d78
Update readme
eskildsf Dec 20, 2024
b1b45a5
Throw exception if used in interactive environment.
eskildsf Dec 20, 2024
3eed038
Fix regression.
eskildsf Dec 20, 2024
06e1a81
Rename CI action.
eskildsf Dec 20, 2024
a8bbfa4
Update README.md
nneskildsf Dec 20, 2024
5ad89a1
Add test cases.
eskildsf Dec 20, 2024
0ae1eac
Merge remote-tracking branch 'refs/remotes/origin/master'
eskildsf Dec 20, 2024
b790d90
Fix flake8.
eskildsf Dec 20, 2024
1e9d150
Fix pyright.
eskildsf Dec 20, 2024
a454748
Add more Python 3 versions to CI.
eskildsf Dec 21, 2024
d698f9c
Remove pip upgrade.
eskildsf Dec 21, 2024
176fbcd
Avoid SSL error for Python 3.5
eskildsf Dec 21, 2024
d0bdaa9
Only do tests on older Python versions.
eskildsf Dec 21, 2024
addc080
Drop Python 3.5 because of f-strings.
eskildsf Dec 21, 2024
9d1d258
Upgrade packages when installing.
eskildsf Dec 21, 2024
f493ac9
Upgrade packages eagerly when installing.
eskildsf Dec 21, 2024
1f89e8c
Upgrade pip
nneskildsf Dec 21, 2024
958003a
Only do unittest on 3.7.
nneskildsf Dec 21, 2024
7b32d25
Be less specific about Python versions.
nneskildsf Dec 21, 2024
976608a
Merge pull request #12 from nneskildsf/11-attempt-support-of-earlier-…
nneskildsf Dec 21, 2024
ff4aeb6
Support Jupyter Notebook
eskildsf Dec 21, 2024
5b2b4e3
Type check with mypy.
eskildsf Dec 21, 2024
e477a9e
Support wrapping functions.
eskildsf Dec 22, 2024
3a25a0e
Support multiple reloaded functions with identical names.
eskildsf Dec 22, 2024
56bfb94
Fix reload of imported function.
eskildsf Dec 23, 2024
a995891
Flake8 fixes
eskildsf Dec 23, 2024
f3a49cf
Silence reportMissingImport for temporary library
eskildsf Dec 23, 2024
b5ab557
Flake8...
eskildsf Dec 23, 2024
de0cb19
Add Windows to CI
eskildsf Dec 23, 2024
a7ef075
Force utf-8 encoding to please Windows.
eskildsf Dec 23, 2024
a50570c
Add mypy and ruff.
eskildsf Dec 23, 2024
edb40da
Merge pull request #14 from nneskildsf/13-ci-on-windows
nneskildsf Dec 23, 2024
d09e75e
Log code changes.
eskildsf Dec 23, 2024
fff005a
Satisfy mypy
eskildsf Dec 23, 2024
c9e3e8e
Fix mypy error.
eskildsf Dec 23, 2024
830fa9c
Support deep call stacks.
eskildsf Dec 24, 2024
6f7751b
Add option to disable interactive exception handling.
eskildsf Dec 26, 2024
495e061
Editorial changes.
eskildsf Jan 3, 2025
458dfa9
Avoid setting encoding when reloading.
eskildsf Jan 4, 2025
5065719
Added missing type overloads.
eskildsf Jan 5, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# git-archive: ignore .gitignore, .gitattributes, .github/ etc.
.git* export-ignore
53 changes: 53 additions & 0 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: CI

on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]

jobs:
test_2004:
name: ${{ matrix.os }} - Python ${{ matrix.python-version }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-20.04, windows-latest]
python-version: ["3.6", "3.7"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
env:
PIP_TRUSTED_HOST: "pypi.python.org pypi.org files.pythonhosted.org"
- name: Install reloading
run: python -m pip install .
- name: Test with unittest
run: python -m unittest
test_2204:
name: ${{ matrix.os }} - Python ${{ matrix.python-version }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-22.04, windows-latest]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install reloading
run: python -m pip install ".[development]"
- name: Lint with flake8
run: flake8
- name: Lint with ruff
run: ruff check .
- name: Type check with pyright
run: pyright
- name: Type check with mypy
run: mypy .
- name: Test with unittest
run: python -m unittest
6 changes: 0 additions & 6 deletions MANIFEST

This file was deleted.

12 changes: 0 additions & 12 deletions Makefile

This file was deleted.

219 changes: 112 additions & 107 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,156 +1,161 @@
# reloading
[![pypi badge](https://img.shields.io/pypi/v/reloading?color=%230c0)](https://pypi.org/project/reloading/)
# Reloading
[![CI](https://github.com/nneskildsf/reloading/actions/workflows/CI.yml/badge.svg)](https://github.com/nneskildsf/reloading/actions/workflows/CI.yml)
[![Checked with mypy](https://www.mypy-lang.org/static/mypy_badge.svg)](https://mypy-lang.org/)
[![Linting: Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)

A Python utility to reload a loop body from source on each iteration without
losing state
A Python utility to reload a function or loop body from source on each iteration without losing state.

Useful for editing source code during training of deep learning models. This lets
you e.g. add logging, print statistics or save the model without restarting the
training and, therefore, without losing the training progress.
## Installing Reloading and Supported Versions
This fork of reloading is *not* available on PyPi. Install it from Github:
```console
$ pip install https://github.com/nneskildsf/reloading/archive/refs/heads/master.zip
```

![Demo](https://github.com/julvo/reloading/blob/master/examples/demo/demo.gif)
This fork of reloading supports Python 3.6+.

## Install
```
pip install reloading
```
## Supported Features
- Reload functions, `for` loops and `while` loops
- Works in Jupyter Notebook
- `break` and `continue` in loops
- Multiple reloading functions and loops in one file
- Reloaded functions preserve their original call signature
- Only reload source code when changed for faster performance
- Comprehensive exceptions and logging
- Exports locals of reloaded loops to parent locals (Python 3.13 and newer)

## Usage

To reload the body of a `for` loop from source before each iteration, simply
wrap the iterator with `reloading`, e.g.
### For Loop
To reload the body of a `for` loop from source before each iteration, wrap the iterator with `reloading`:
```python
from reloading import reloading

for i in reloading(range(10)):
# this code will be reloaded before each iteration
# This code will be reloaded before each iteration
print(i)
```

### While Loop
To reload the body and condition of a `while` loop from source before each iteration, wrap the condition with `reloading`:
```python
from reloading import reloading

i = 0
while reloading(i<10):
# This code and the condition (i<10) will be reloaded before each iteration
print(i)
i += 1
```

### Function
To reload a function from source before each execution, decorate the function
definition with `@reloading`, e.g.
definition with `@reloading`:
```python
from reloading import reloading

@reloading
def some_function():
# this code will be reloaded before each invocation
def function():
# This code will be reloaded before each function call
pass
```

## Additional Options

Pass the keyword argument `every` to reload only on every n-th invocation or iteration. E.g.
It is also possible to mark a function for reload after defining it:
```python
for i in reloading(range(1000), every=10):
# this code will only be reloaded before every 10th iteration
# this can help to speed-up tight loops
pass
from reloading import reloading

@reloading(every=10)
def some_function():
# this code with only be reloaded before every 10th invocation
def function():
# This function will be reloaded before each function call
pass
```

Pass `forever=True` instead of an iterable to create an endless reloading loop, e.g.
```python
for i in reloading(forever=True):
# this code will loop forever and reload from source before each iteration
pass
function = reloading(function)
```

## Examples

Here are the short snippets of how to use reloading with your favourite library.
For complete examples, check out the [examples folder](https://github.com/julvo/reloading/blob/master/examples).
## Additional Options

### PyTorch
### Interactive Exception Handling
Exceptions are handled interactively by default to avoid losing state.
When an exception occurs you will be notified and have the opportunity
to rectify the issue and continue. However, if reloading is
used in a setting where exceptions are better handled in the application
using reloading then it can be disabled by setting `interactive_exception`
to `False`. Example:
```python
for epoch in reloading(range(NB_EPOCHS)):
# the code inside this outer loop will be reloaded before each epoch

for images, targets in dataloader:
optimiser.zero_grad()
predictions = model(images)
loss = F.cross_entropy(predictions, targets)
loss.backward()
optimiser.step()
```
[Here](https://github.com/julvo/reloading/blob/master/examples/pytorch/train.py) is a full PyTorch example.
from reloading import reloading

### fastai
```python
@reloading
def update_learner(learner):
# this function will be reloaded from source before each epoch so that you
# can make changes to the learner while the training is running
@reloading(interactive_exception=False)
def reloading_function():
pass

class LearnerUpdater(LearnerCallback):
def on_epoch_begin(self, **kwargs):
update_learner(self.learn)
for i in reloading(range(10), interactive_exception=False):
pass

path = untar_data(URLs.MNIST_SAMPLE)
data = ImageDataBunch.from_folder(path)
learn = cnn_learner(data, models.resnet18, metrics=accuracy,
callback_fns=[LearnerUpdater])
learn.fit(10)
j = 0
while reloading(j<10, interactive_exception=False):
j += 1
```
[Here](https://github.com/julvo/reloading/blob/master/examples/fastai/train.py) is a full fastai example.

### Keras
### Iterate Forever in For Loop
To iterate forever in a `for` loop you can omit the argument:
```python
@reloading
def update_model(model):
# this function will be reloaded from source before each epoch so that you
# can make changes to the model while the training is running using
# K.set_value()
pass
from reloading import reloading

class ModelUpdater(Callback):
def on_epoch_begin(self, epoch, logs=None):
update_model(self.model)
for _ in reloading():
# This code will loop forever and reload from source before each iteration
pass
```

model = Sequential()
model.add(Dense(64, activation='relu', input_dim=20))
model.add(Dense(10, activation='softmax'))
### Code Changes Logged
On Python 3.9 and newer, a diff is logged when the source code is updated.
Consider the following code as an example.
```python
from reloading import reloading
from time import sleep
import logging

sgd = SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True)
model.compile(loss='categorical_crossentropy',
optimizer=sgd,
metrics=['accuracy'])
log = logging.getLogger("reloading")
log.setLevel(logging.DEBUG)

model.fit(x_train, y_train,
epochs=200,
batch_size=128,
callbacks=[ModelUpdater()])
for i in reloading(range(100)):
print(i)
sleep(1.0)
```
After some time the code is edited. `i = 2*i` is added before `print(i)`,
resulting in the following log output:
```console
INFO:reloading:For loop at line 10 of file "../example.py" has been reloaded.
DEBUG:reloading:Code changes:
+i = i * 2
print(i)
sleep(1.0)
```
[Here](https://github.com/julvo/reloading/blob/master/examples/keras/train.py) is a full Keras example.

### TensorFlow
## Known Issus

On Python version [less than 3.13](https://docs.python.org/3/reference/datamodel.html#frame.f_locals) it is not possible to properly export the local variables from a loop to parent locals. The following example demonstrates this:
```python
for epoch in reloading(range(NB_EPOCHS)):
# the code inside this outer loop will be reloaded from source
# before each epoch so that you can change it during training

train_loss.reset_states()
train_accuracy.reset_states()
test_loss.reset_states()
test_accuracy.reset_states()

for images, labels in tqdm(train_ds):
train_step(images, labels)

for test_images, test_labels in tqdm(test_ds):
test_step(test_images, test_labels)
```
[Here](https://github.com/julvo/reloading/blob/master/examples/tensorflow/train.py) is a full TensorFlow example.
from reloading import reloading

## Testing
def function():
i = 0
while reloading(i < 10):
i += 1
print(i)

Make sure you have `python` and `python3` available in your path, then run:
function() # Prints 0. Not 10 as expected. Fixed in Python 3.13.
```
A warning is emitted when the issue arises:
```console
WARNING:reloading:Variable(s) "i" in reloaded loop were not exported to the scope which called the reloaded loop at line...
```
$ python3 reloading/test_reloading.py

## Lint, Type Check and Testing

Run:
```console
$ pip install -e ".[development]"
$ ruff check .
$ flake8
$ pyright
$ mypy .
$ python -m unittest
```
Binary file removed examples/demo/demo.gif
Binary file not shown.
14 changes: 0 additions & 14 deletions examples/demo/demo.py

This file was deleted.

1 change: 0 additions & 1 deletion examples/fastai/requirements.txt

This file was deleted.

Loading