homely.files

homely.files.blockinfile()

blockinfile() will add a several line of text to a target file in a single block. The lines of text are surrounded by prefix and suffix lines so that the block can be maintained by homely over time.

blockinfile(filename, contents, where=None, *, prefix=None, suffix=None)

filename
The name of the file to modify. If filename begins with a / it will be treated as an absolute path, otherwise it is assumed to be relative to $HOME. You can also use ~ and environment variables like $HOME directly in the filename string.
contents
A sequence of strings representing the lines of text to add to the file. The individual lines must not contain \n or \r.
where
One of None, homely.files.WHERE_TOP, homely.files.WHERE_BOT or homely.files.WHERE_ANY. If set to None the behaviour of homely.files.WHERE_ANY will be used.
prefix=None
A string containing a line of text which is guaranteed to be unique in the file and can be used to determine where the block starts.
suffix=None
A string containing a line of text which is guaranteed to be unique in the file and can be used to determine where the block ends.

If prefix=None or suffix=None then a prefix or suffix (respectively) will be generated for you automatically. The generated line will start with # since this represents a comment in most config file languages. If the target file is named .vimrc or ends in .vim, the vim comment character " will be used instead.

block() is idempotent and will also make sure there is only one occurrence of the prefix...suffix block in the file, at the location specified by where:

where=homely.files.WHERE_TOP
The block will be added or moved to the top of the file.
where=homely.files.WHERE_BOT
The block will be added or moved to the bottom of the file.
where=homely.files.WHERE_ANY or where=None
The block will be added to the bottom of the file, but if it is already present in the file it will be left wherever it is. If there are multiple occurrences of the block already in the file, the first will be kept and subsequent occurrences will be removed.

Examples

You want to include powerline config in your .tmux.conf, but the powerline config file is in different locations depending on the host operating system. You could find the location of the powerline module programmatically and then use blockinfile() to insert the config into your .tmux.conf using blockinfile():

from os.path import dirname
from homely.files import blockinfile, WHERE_TOP
from homely.system import execute

# use python to import powerline and find out where it is installed
cmd = ['python', '-c', 'import powerline; print(powerline.__file__)']
stdout = execute(cmd, stdout=True)[1]
powerline_pkg = dirname(stdout.strip().decode('utf-8'))
lines = [
    'run-shell "powerline-daemon --replace -q"',
    'source "{}/bindings/tmux/powerline.conf"'.format(powerline_pkg),
])
blockinfile('.tmux.conf', lines, WHERE_BOT)

Automatic Cleanup

If homely does modify a file using blockinfile(), it will remember this fact so that it can [possibly] perform automatic cleanup in the future. Each time you run homely update homely will check to see if blockinfile() was called with the same prefix/suffix combination, and if it wasn’t then the block will be removed from the file. This means that you don’t need to remember to remove the lines you aren’t using any more – simply remove the call to blockinfile() and homely will clean it up for you.

Note: after cleaning up a blockinfile() section, homely will re-run all lineinfile() and blockinfile() functions that targetted that file. This ensures that when a block is removed from a file, it won’t accidentally remove something that was still wanted by a lineinfile(). See Example 4: Cleaning Modified Files for more information about this feature.

homely.files.download()

download() will download a single file from a target URL.

download(url, dest, expiry=None)

url
The URL of the file to be downloaded.
dest
Path where the file should be downloaded to. If dest begins with a / it will be treated as an absolute path, otherwise it is assumed to be relative to $HOME. You can also use ~ and environment variables like $HOME directly in the path string.
expiry
The file will be downloaded again when the local copy is <expiry> seconds old. When expiry=0 the file will be downloaded every time you run homely update. When expiry=-1 the file will never be downloaded again. When expiry=None it will default to 60*60*24*14 (2 weeks).

Examples

Download git completion script for bash:

from homely.files import download

url = 'https://raw.githubusercontent.com/git/git/master/contrib/completion/git-completion.bash'
download(url, '~/src/git-completion.bash')

Automatic Cleanup

If homely creates the file at dest, it will remember this fact so that it can [possibly] perform automatic cleanup in the future. Each time you run homely update homely will check to see if download() was called with the same dest, and if it wasn’t then the file will be removed. See Automatic Cleanup for more information.

homely.files.lineinfile()

lineinfile() will add a single line of text to a target file.

lineinfile(filename, contents, where=None)

filename
The name of the file to modify. If filename begins with a / it will be treated as an absolute path, otherwise it is assumed to be relative to $HOME. You can also use ~ and environment variables like $HOME directly in the filename string.
contents
The line of text to add to the file. contents must not contain \n or \r.
where
One of None, homely.files.WHERE_TOP, homely.files.WHERE_BOT or homely.files.WHERE_ANY. If set to None the behaviour of homely.files.WHERE_ANY will be used.

lineinfile() is idempotent and will also make sure there is only one occurrence of the line contents in the file, at the location specified by where:

where=homely.files.WHERE_TOP
The line will be added or moved to the top of the file.
where=homely.files.WHERE_BOT
The line will be added or moved to the bottom of the file.
where=homely.files.WHERE_ANY or where=None
The line will be added to the bottom of the file, but if it is already present in the file it will be left wherever it is. If there are multiple occurrences of the line already in the file, the first will be kept and subsequent occurrences will be removed.

Examples

Use lineinfile() to add a line to the end of your .bashrc:

from homely.files import lineinfile, WHERE_BOT
lineinfile('.bashrc', 'PATH=$HOME/dotfiles/bin:$PATH', WHERE_BOT)

Use lineinfile() to add a line to the top of your ~/.vimrc which sources a shared vimrc inside your dotfiles repo:

from homely.files import lineinfile, WHERE_TOP
lineinfile('~/.vimrc', 'source $HOME/dotfiles/vimrc.vim', WHERE_TOP)

Automatic Cleanup

If homely does modify a file using lineinfile(), it will remember this fact so that it can [possibly] perform automatic cleanup in the future. Each time you run homely update homely will check to see if lineinfile() was called with the same arguments, and if it wasn’t then the line will be removed from the file. This means that you don’t need to remember to remove the lines you aren’t using any more – simply remove the call to lineinfile() and homely will clean it up for you.

Note: after cleaning up line added by a lineinfile() that is no longer present, homely will re-run all lineinfile() and blockinfile() functions that targetted that file. This ensures that when a line is removed from a file, it won’t accidentally remove something that was still wanted by another lineinfile() or blockinfile(). See Example 4: Cleaning Modified Files for more information about this feature.

homely.files.mkdir()

mkdir() will create the nominated directory if it doesn’t already exist.

mkdir(path)

path
The path to be created. If path begins with a / it will be treated as an absolute path, otherwise it is assumed to be relative to $HOME. You can also use ~ and environment variables like $HOME directly in the path string.

Examples

Different ways to create ~/bin directory:

from homely.files import mkdir

# absolute path
mkdir('/home/peter/bin')

# path implicitly relative to $HOME
mkdir('bin')

# "~" expansion works
mkdir('~/bin')

# Environment variables are also expanded
mkdir('$HOME/bin')

Automatic Cleanup

If homely does create the directory, it will remember this fact so that it can [possibly] perform automatic cleanup in the future. Each time you run homely update homely will check to see if mkdir() was called, and if it wasn’t then the directory will be removed. This means that you don’t need to remember to delete directories you aren’t using any more - simply remove the call to mkdir() and homely will clean it up for you. Note that the directory won’t be cleaned up if it is still in use. See Automatic Cleanup for more information.

homely.files.writefile()

writefile() gives you a file handle for creating or overwriting a file on disk.

writefile(filename)

filename
The name of the file to create or overwrite. If filename begins with a / it will be treated as an absolute path, otherwise it is assumed to be relative to $HOME. You can also use ~ and environment variables like $HOME directly in the filename string.

Examples

Write out a config file for the Composer dependency manager for PHP. An additional private repository will be conditionally added to the config JSON if the user answers Y when asked.:

import json

from homely.files import mkdir, writefile

# base config
composer_config = {
    'minimum-stability': 'dev',
    'prefer-stable': True,
}

# optional extra repository
if yesno('use_my_private_composer_repo', 'Use my private composer repo?'):
    composer_config['repositories'] = [
        {'type': 'composer', 'url': 'http://packages.example.com'}
    ]

# now write the JSON file
mkdir('~/.config')
mkdir('~/.config/composer')
with writefile('~/.config/composer/composer.json') as f:
    json.dump(composer_config, f)

Automatic Cleanup

If a file doesn’t exist and Homely has to create it, then Homely will take ownership of the file and will [possibly] perform automatic cleanup in the future. Each time you run homely update homely will check to see if writefile() was used to write the same file again, and if it wasn’t then the file will be removed.

But if the file already exists before Homely writes to it for the first time, then homely won’t take ownership of the file and won’t automatically remove it aftewards.

See Automatic Cleanup for more information.