tmpdir = TmpDir()core
TmpDir
TmpDir ()
Create temporary workspaces.
The main way to use TmpDir is through the exported object:
We start out in our project dir:
d0 = Path.cwd(); d0Path('/Users/seem/code/sketch-tmpdir')
Switch to path foo/bar under a temp dir:
tmpdir.new('foo/bar')
d1 = tmpdir.dir
Path.cwd()Path('/private/var/folders/ft/0gnvc3ts5jz4ddqtttp6tjvm0000gn/T/tmp19y8l0h4/foo/bar')
If we switch again, the previous dir is removed:
tmpdir.new('foo/bar')
assert not d1.exists()
d2 = tmpdir.dir
Path.cwd()Path('/private/var/folders/ft/0gnvc3ts5jz4ddqtttp6tjvm0000gn/T/tmpnjqprlft/foo/bar')
Finally, revert to the original working directory, which also removes the remaining temporary directory:
tmpdir.close()
assert not d2.exists()
test_eq(Path.cwd(), d0)You can also use it as a context manager to automatically revert to the original working directory at the end:
with tmpdir() as p:
d3 = tmpdir.dir
test_eq(Path.cwd().name, p.name)
assert not d3.exists()
test_eq(Path.cwd(), d0)The primary use-case is to write executable documentation for code that interacts with its workspace, as described in the examples below.
Example 1: Python git interface
def git_repo():
"Remote repo from git config."
cmd = 'git config --get remote.origin.url'
proc = subprocess.run(cmd, shell=True, capture_output=True, text=True)
if proc.returncode: return
return proc.stdout.strip().split('/', 1)[1].split('.')[0]Let’s initialise a minimal repo to demonstrate:
tmpdir.new()git init -q
git remote add origin git@github.com:my-user/my-repo.gitGet the repo name:
test_eq(git_repo(), 'my-repo')Returns None if you’re not in a git repo:
with tmpdir(): test_is(git_repo(), None)And you’re back to your original working directory:
Path.cwd()Path('/Users/seem/code/sketch-tmpdir')
Example 2: nbdev hooks
This section shows what the end-to-end nbdev hooks test could look like with tmpdir. The test checks that nbdev’s notebook-aware merge driver works. In order to do that we need to simulate a merge conflict, which involves some back-and-forth with git.
from copy import deepcopy
from execnb.nbio import dict2nb, mk_cell, read_nb, write_nb
from fastcore.foundation import Config
from nbdev.read import create_output, show_srcStart a new workspace:
tmpdir.new()Init a git repo and checkout our main branch – this is where we’ll run our tests:
git initInitialized empty Git repository in /private/var/folders/ft/0gnvc3ts5jz4ddqtttp6tjvm0000gn/T/tmpggvpu_sy/.git/
git checkout -b mainSwitched to a new branch 'main'
Install nbdev hooks:
nbdev_install_hooksHooks are installed.
Next, we’ll simulate a merge conflict. First we add random.ipynb notebook to main. Here’s what it looks like to start with:
fn = 'random.ipynb'
meta = {'nbformat': 4,'metadata':{'kernelspec':{'display_name':'Python 3','language': 'python','name': 'python3'}}}
base = dict2nb({'cells':[mk_cell('import random'), mk_cell('random.random()')], **meta})
base.cells[-1].output = create_output('0.3314001088639852\n0.20280244713400464', 'plain')
write_nb(base, fn)
show_nb(fn)# random.ipynb
# %%
import random
# %%
random.random()
# 0.3314001088639852
# 0.20280244713400464Commit it:
git add .
git commit -q -m 'add random.ipynb'Next, checkout a new branch add-heading:
git checkout -b add-headingSwitched to a new branch 'add-heading'
Make a change. We added a new markdown cell Calculate a random number:, imported os, and got different random.random() outputs – the perfect recipe for a merge conflict:
ours = deepcopy(base)
ours.cells[0].source+=',os' # Change first cell
ours.cells.insert(1, mk_cell('Calculate a random number:', cell_type='markdown')) # New cell
ours.cells[-1].output = create_output('0.3379097372590093\n0.7379492349993123', 'plain') # Change outputs
write_nb(ours, fn)
show_nb(fn)# random.ipynb
# %%
import random,os
# %%
Calculate a random number:
# %%
random.random()
# 0.3379097372590093
# 0.7379492349993123Commit it:
git commit -am heading[add-heading 5b71b21] heading
1 file changed, 10 insertions(+), 3 deletions(-)
Go back to main:
git checkout mainSwitched to branch 'main'
Make a different change:
show_nb(fn)# random.ipynb
# %%
# Random numbers
# %%
import random,sys
# %%
random.random()
# 0.6587181429602441
# 0.5962200692415515Commit it:
git commit -am docs[main d3b0252] docs
1 file changed, 10 insertions(+), 3 deletions(-)
And finally try to merge:
git merge add-headingOne or more conflict remains in the notebook, please inspect manually.
Auto-merging random.ipynb
CONFLICT (content): Merge conflict in random.ipynb
Automatic merge failed; fix conflicts and then commit the result.
We have a merge conflict! But thanks to nbdev:
- Conflicting outputs are automatically resolved
- The notebook is left in a readable state.
show_nb(fn)# random.ipynb
# %%
# Random numbers
# %%
`<<<<<<< HEAD`
# %%
import random,sys
# %%
`=======`
# %%
import random,os
# %%
Calculate a random number:
# %%
`>>>>>>> add-heading`
# %%
random.random()
# 0.6587181429602441
# 0.5962200692415515Close tmpdir, and you’re back to your original working directory:
tmpdir.close()
Path.cwd()