Skip to content

Commit

Permalink
Merge branch 'dirs' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
agateau committed Apr 29, 2016
2 parents ce220c5 + af5bea2 commit 5c70b21
Show file tree
Hide file tree
Showing 8 changed files with 283 additions and 68 deletions.
9 changes: 4 additions & 5 deletions README.md
Expand Up @@ -34,7 +34,6 @@ Here is an example of a short Yokadi session:
Start Yokadi:

./bin/yokadi
Using default database (/home/me/.yokadi.db)
Creating database
Added keyword '_severity'
Added keyword '_likelihood'
Expand Down Expand Up @@ -285,8 +284,8 @@ next version of Yokadi.

## Database location

By default, Yokadi creates a database in `$HOME/.yokadi.db`, but you can
specify an alternative location with the `--db` option.
By default, Yokadi creates a database in `$HOME/.local/share/yokadi/yokadi.db`,
but you can specify an alternative location with the `--db` option.

A convenient way to start Yokadi is by creating an alias in your `.bashrc` file
like this:
Expand All @@ -303,8 +302,8 @@ the `YOKADI_DB` environment variable to point to your database:

## History location

By default, Yokadi will store input history in `$HOME/.yokadi_history`. This file
stores commands used in Yokadi for future use and reference.
By default, Yokadi will store input history in `$HOME/.cache/yokadi/history`.
This file stores commands used in Yokadi for future use and reference.

If you do now want to use the default history file location, you can define
the `YOKADI_HISTORY` environment variable to point to your history file:
Expand Down
34 changes: 0 additions & 34 deletions yokadi/core/basedirs.py

This file was deleted.

113 changes: 113 additions & 0 deletions yokadi/core/basepaths.py
@@ -0,0 +1,113 @@
# -*- coding: UTF-8 -*-
"""
Provide standard ways to get various dirs
This is similar to th pyxdg module but it does not automatically creates the
dirs. Not creating the dirs is important to be able to show default values in
`yokadid --help` output without creating anything.
@author: Aurélien Gâteau <mail@agateau.com>
@license: GPL v3 or later
"""
import os
import getpass
import shutil
import tempfile

from yokadi.core import fileutils


_WINDOWS = os.name == "nt"


class MigrationException(Exception):
pass


def _getAppDataDir():
assert _WINDOWS
return os.environ["APPDATA"]


def getRuntimeDir():
value = os.getenv("XDG_RUNTIME_DIR")
if not value:
# Running on a system where XDG_RUNTIME_DIR is not set, fallback to
# $tempdir/yokadi-$user
tmpdir = tempfile.gettempdir()
value = os.path.join(tmpdir, "yokadi-" + getpass.getuser())
return value


def getLogDir():
return getCacheDir()


def getCacheDir():
if _WINDOWS:
value = os.path.join(_getAppDataDir(), "yokadi", "cache")
else:
cacheBaseDir = os.getenv("XDG_CACHE_HOME")
if not cacheBaseDir:
cacheBaseDir = os.path.expandvars("$HOME/.cache")
value = os.path.join(cacheBaseDir, "yokadi")
return value


def getDataDir():
if _WINDOWS:
value = os.path.join(_getAppDataDir(), "yokadi", "data")
else:
dataBaseDir = os.environ.get("XDG_DATA_HOME")
if not dataBaseDir:
dataBaseDir = os.path.expandvars("$HOME/.local/share")
value = os.path.join(dataBaseDir, "yokadi")
return value


def getHistoryPath():
path = os.getenv("YOKADI_HISTORY")
if path:
return path
return os.path.join(getCacheDir(), "history")


def getDbPath():
path = os.getenv("YOKADI_DB")
if path:
return path
return os.path.join(getDataDir(), "yokadi.db")


def _getOldHistoryPath():
if _WINDOWS:
return os.path.join(_getAppDataDir(), ".yokadi_history")
else:
return os.path.expandvars("$HOME/.yokadi_history")


def migrateOldHistory():
oldHistoryPath = _getOldHistoryPath()
if not os.path.exists(oldHistoryPath):
return

newHistoryPath = getHistoryPath()
if os.path.exists(newHistoryPath):
# History is not critical, just overwrite the new file
os.unlink(newHistoryPath)
fileutils.createParentDirs(newHistoryPath)
shutil.move(oldHistoryPath, newHistoryPath)
print("Moved %s to %s" % (oldHistoryPath, newHistoryPath))


def migrateOldDb():
oldDbPath = os.path.normcase(os.path.expandvars("$HOME/.yokadi.db"))
if not os.path.exists(oldDbPath):
return

newDbPath = getDbPath()
if os.path.exists(newDbPath):
raise MigrationException("Tried to move %s to %s, but %s already exists. You must remove one of the two files." % (oldDbPath, newDbPath, newDbPath))
fileutils.createParentDirs(newDbPath)
shutil.move(oldDbPath, newDbPath)
print("Moved %s to %s" % (oldDbPath, newDbPath))
14 changes: 14 additions & 0 deletions yokadi/core/fileutils.py
@@ -0,0 +1,14 @@
"""
Various file utility functions
@author: Aurélien Gâteau <mail@agateau.com>
@license: GPL v3 or later
"""
import os


def createParentDirs(path, mode=0o777):
parent = os.path.dirname(path)
if os.path.exists(parent):
return
os.makedirs(parent, mode=mode)
128 changes: 128 additions & 0 deletions yokadi/tests/basepathstestcase.py
@@ -0,0 +1,128 @@
# -*- coding: UTF-8 -*-
"""
Basepaths test cases
@author: Aurélien Gâteau <mail@agateau.com>
@license: GPL v3 or later
"""
import os
import shutil
import tempfile
import unittest

from pathlib import Path

from yokadi.core import basepaths


def saveEnv():
return dict(os.environ)


def restoreEnv(env):
# Do not use `os.environ = env`: this would replace the special os.environ
# object with a plain dict. We must update the *existing* object.
os.environ.clear()
os.environ.update(env)


class BasePathsUnixTestCase(unittest.TestCase):
def setUp(self):
self._oldWindows = basepaths._WINDOWS
basepaths._WINDOWS = False

self._oldEnv = saveEnv()
self.testHomeDir = tempfile.mkdtemp(prefix="yokadi-basepaths-testcase")
os.environ["HOME"] = self.testHomeDir

def tearDown(self):
shutil.rmtree(self.testHomeDir)
restoreEnv(self._oldEnv)
basepaths._WINDOWS = self._oldWindows

def testMigrateOldDb(self):
oldDb = Path(self.testHomeDir) / '.yokadi.db'
newDb = Path(basepaths.getDbPath())

oldDb.touch()

basepaths.migrateOldDb()
self.assertFalse(oldDb.exists())
self.assertTrue(newDb.exists())

def testMigrateNothingToDo(self):
newDb = Path(basepaths.getDbPath())
basepaths.migrateOldDb()
basepaths.migrateOldHistory()
self.assertFalse(newDb.exists())

def testMigrateOldDbFails(self):
oldDb = Path(self.testHomeDir) / '.yokadi.db'
newDb = Path(basepaths.getDbPath())

oldDb.touch()
newDb.parent.mkdir(parents=True)
newDb.touch()

self.assertRaises(basepaths.MigrationException, basepaths.migrateOldDb)

def testMigrateOldHistory(self):
old = Path(self.testHomeDir) / '.yokadi_history'
new = Path(basepaths.getHistoryPath())

old.touch()

basepaths.migrateOldHistory()
self.assertFalse(old.exists())
self.assertTrue(new.exists())

def testMigrateOldHistoryOverwriteNew(self):
old = Path(self.testHomeDir) / '.yokadi_history'
new = Path(basepaths.getHistoryPath())

with old.open('w') as f:
f.write('old')
new.parent.mkdir(parents=True)
with new.open('w') as f:
f.write('new')

basepaths.migrateOldHistory()
self.assertFalse(old.exists())
with new.open() as f:
newData = f.read()
self.assertEqual(newData, 'old')

def testHistoryEnvVar(self):
path = "foo"
os.environ["YOKADI_HISTORY"] = path
self.assertEqual(basepaths.getHistoryPath(), path)

def testDbEnvVar(self):
path = "foo"
os.environ["YOKADI_DB"] = path
self.assertEqual(basepaths.getDbPath(), path)


class BasePathsWindowsTestCase(unittest.TestCase):
def setUp(self):
self._oldWindows = basepaths._WINDOWS
self._oldEnv = saveEnv()
basepaths._WINDOWS = True
self.testAppDataDir = tempfile.mkdtemp(prefix="yokadi-basepaths-testcase")
os.environ["APPDATA"] = self.testAppDataDir

def tearDown(self):
shutil.rmtree(self.testAppDataDir)
basepaths._WINDOWS = self._oldWindows
restoreEnv(self._oldEnv)

def testGetCacheDir(self):
expected = os.path.join(self.testAppDataDir, "yokadi", "cache")
self.assertEqual(basepaths.getCacheDir(), expected)

def testGetDataDir(self):
expected = os.path.join(self.testAppDataDir, "yokadi", "data")
self.assertEqual(basepaths.getDataDir(), expected)

def testOldHistoryPath(self):
expected = os.path.join(self.testAppDataDir, ".yokadi_history")
self.assertEqual(basepaths._getOldHistoryPath(), expected)
1 change: 1 addition & 0 deletions yokadi/tests/tests.py
Expand Up @@ -40,6 +40,7 @@
from helptestcase import HelpTestCase
from conftestcase import ConfTestCase
from massedittestcase import MassEditTestCase
from basepathstestcase import BasePathsUnixTestCase, BasePathsWindowsTestCase


def main():
Expand Down
31 changes: 15 additions & 16 deletions yokadi/ycli/main.py
Expand Up @@ -37,8 +37,10 @@

from yokadi.core import db
from yokadi.ycli import tui
from yokadi.core import utils
from yokadi.core import basepaths
from yokadi.core import cryptutils
from yokadi.core import fileutils
from yokadi.core import utils

from yokadi.ycli.aliascmd import AliasCmd, resolveAlias
from yokadi.ycli.confcmd import ConfCmd
Expand All @@ -59,13 +61,7 @@ def __init__(self):
AliasCmd.__init__(self)
ConfCmd.__init__(self)
self.prompt = "yokadi> "
self.historyPath = os.getenv("YOKADI_HISTORY")
if not self.historyPath:
if os.name == "posix":
self.historyPath = os.path.join(os.path.expandvars("$HOME"), ".yokadi_history")
else:
# Windows location
self.historyPath = os.path.join(os.path.expandvars("$APPDATA"), ".yokadi_history")
self.historyPath = basepaths.getHistoryPath()
self.loadHistory()
self.cryptoMgr = cryptutils.YokadiCryptoManager() # Load shared cryptographic manager

Expand Down Expand Up @@ -154,6 +150,7 @@ def loadHistory(self):
def writeHistory(self):
"""Writes shell history to disk"""
try:
fileutils.createParentDirs(self.historyPath)
# Open r/w and close file to create one if needed
historyFile = open(self.historyPath, "w", encoding='utf-8')
historyFile.close()
Expand Down Expand Up @@ -196,7 +193,7 @@ def main():
parser = ArgumentParser()

parser.add_argument("-d", "--db", dest="filename",
help="TODO database", metavar="FILE")
help="TODO database (default: %s)" % basepaths.getDbPath(), metavar="FILE")

parser.add_argument("-c", "--create-only",
dest="createOnly", default=False, action="store_true",
Expand All @@ -214,14 +211,16 @@ def main():
print("Yokadi - %s" % utils.currentVersion())
return

basepaths.migrateOldHistory()
try:
basepaths.migrateOldDb()
except basepaths.MigrationException as exc:
print(exc)
sys.exit(1)

if not args.filename:
# Look if user define an env VAR for yokadi db
args.filename = os.getenv("YOKADI_DB")
if args.filename:
print("Using env defined database (%s)" % args.filename)
else:
args.filename = os.path.normcase(os.path.expanduser("~/.yokadi.db"))
print("Using default database (%s)" % args.filename)
args.filename = basepaths.getDbPath()
fileutils.createParentDirs(args.filename)

try:
db.connectDatabase(args.filename)
Expand Down

0 comments on commit 5c70b21

Please sign in to comment.