updated migrate.versioning.repository tests and docs, update README

This commit is contained in:
iElectric 2009-06-08 12:57:23 +00:00
parent d44be57771
commit bc1edc5c1b
5 changed files with 118 additions and 84 deletions

16
README
View File

@ -1,19 +1,25 @@
SQLAlchemy-migrate is a tool and a set of APIs for managing changes to
database schemas.
Inspired by Ruby on Rails' migrations, Migrate provides a way to deal with database schema changes in `SQLAlchemy <http://sqlalchemy.org>`_ projects.
Migrate extends SQLAlchemy to have database changeset handling. It provides a database change repository mechanism which can be used from the command line as well as from inside python code.
Help
----
Help is available at the project page [1] and at the public users
mailing list [2].
Sphinx documentation is available at the project page [1].
Users and developers can be found at #sqlalchemy-migrate on Freenode IRC network
and at the public users mailing list [2].
New releases and major changes are announced at the public announce
mailing list [3] and at the Python package index [4].
[1] http://code.google.com/p/sqlalchemy-migrate/
Homepage is located at [5]
[1] http://packages.python.org/sqlalchemy-migrate/
[2] http://groups.google.com/group/migrate-users
[3] http://groups.google.com/group/migrate-announce
[4] http://pypi.python.org/pypi/sqlalchemy-migrate
[5] http://code.google.com/p/sqlalchemy-migrate/
Tests and Bugs
--------------

View File

@ -248,7 +248,7 @@ def drop_version_control(url, repository, **opts):
def manage(file, **opts):
"""%prog manage FILENAME VARIABLES...
"""%prog manage FILENAME [VARIABLES...]
Creates a script that runs Migrate with a set of default values.
@ -263,7 +263,7 @@ def manage(file, **opts):
python manage.py version
%prog version --repository=/path/to/repository
"""
return repository.manage(file, **opts)
return Repository.create_manage_file(file, **opts)
def compare_model_to_db(url, model, repository, **opts):

View File

@ -80,7 +80,7 @@ class Repository(pathed.Pathed):
"""
Ensure the target path is a valid repository.
:raises: :exc:`InvalidRepositoryError` if not valid
:raises: :exc:`InvalidRepositoryError <migrate.versioning.exceptions.InvalidRepositoryError>`
"""
# Ensure the existance of required files
try:
@ -90,22 +90,22 @@ class Repository(pathed.Pathed):
except exceptions.PathNotFoundError, e:
raise exceptions.InvalidRepositoryError(path)
# TODO: what are those options?
@classmethod
def prepare_config(cls, pkg, rsrc, name, **opts):
"""
Prepare a project configuration file for a new project.
"""
# Prepare opts
defaults=dict(
version_table='migrate_version',
repository_id=name,
required_dbs=[], )
for key, val in defaults.iteritems():
if (key not in opts) or (opts[key] is None):
opts[key]=val
defaults = dict(
version_table = 'migrate_version',
repository_id = name,
required_dbs = [])
defaults.update(opts)
tmpl = resource_string(pkg, rsrc)
ret = string.Template(tmpl).substitute(opts)
ret = string.Template(tmpl).substitute(defaults)
return ret
@classmethod
@ -127,40 +127,46 @@ class Repository(pathed.Pathed):
fd.close()
# Create a management script
manager = os.path.join(path, 'manage.py')
manage(manager, repository=path)
Repository.create_manage_file(manager, repository=path)
except:
log.error("There was an error creating your repository")
return cls(path)
def create_script(self, description, **k):
""""""
self.versions.create_new_python_version(description, **k)
def create_script_sql(self, database, **k):
""""""
self.versions.create_new_sql_version(database, **k)
@property
def latest(self):
""""""
return self.versions.latest
@property
def version_table(self):
""""""
return self.config.get('db_settings', 'version_table')
@property
def id(self):
""""""
return self.config.get('db_settings', 'repository_id')
def version(self, *p, **k):
""""""
return self.versions.version(*p, **k)
@classmethod
def clear(cls):
""""""
super(Repository, cls).clear()
version.Collection.clear()
def changeset(self, database, start, end=None):
"""
Create a changeset to migrate this dbms from ver. start to end/latest.
"""Create a changeset to migrate this dbms from ver. start to end/latest.
"""
start = version.VerNum(start)
if end is None:
@ -181,13 +187,19 @@ class Repository(pathed.Pathed):
return ret
def manage(file, **opts):
"""Create a project management script"""
pkg, rsrc = template.manage(as_pkg=True)
tmpl = resource_string(pkg, rsrc)
vars = ",".join(["%s='%s'" % vars for vars in opts.iteritems()])
result = tmpl % dict(defaults=vars)
@classmethod
def create_manage_file(cls, file_, **opts):
"""Create a project management script (manage.py)
fd = open(file, 'w')
fd.write(result)
fd.close()
:param file_: Destination file to be written
:param **opts: Options that are passed to template
"""
vars_ = ",".join(["%s='%s'" % var for var in opts.iteritems()])
pkg, rsrc = template.manage(as_pkg=True)
tmpl = resource_string(pkg, rsrc)
result = tmpl % dict(defaults=vars_)
fd = open(file_, 'w')
fd.write(result)
fd.close()

View File

@ -1,5 +1,7 @@
#!/usr/bin/python
import os
try:
from setuptools import setup, find_packages
except ImportError:
@ -14,6 +16,7 @@ except ImportError:
test_requirements = ['nose >= 0.10']
required_deps = ['sqlalchemy >= 0.5', 'decorator']
readme_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'README')
setup(
name = "sqlalchemy-migrate",
@ -21,12 +24,7 @@ setup(
packages = find_packages(exclude=['test*']),
include_package_data = True,
description = "Database schema migration for SQLAlchemy",
long_description = """
Inspired by Ruby on Rails' migrations, Migrate provides a way to deal with database schema changes in `SQLAlchemy <http://sqlalchemy.org>`_ projects.
Migrate extends SQLAlchemy to have database changeset handling. It provides a database change repository mechanism which can be used from the command line as well as from inside python code.
""",
long_description = readme_file.read(),
install_requires = required_deps,
tests_require = test_requirements,
extras_require = {
@ -38,7 +36,6 @@ Migrate extends SQLAlchemy to have database changeset handling. It provides a da
maintainer = "Jan Dittberner",
maintainer_email = "jan@dittberner.info",
license = "MIT",
entry_points = """
[console_scripts]
migrate = migrate.versioning.shell:main

View File

@ -1,98 +1,116 @@
from test import fixture
from migrate.versioning.repository import *
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import shutil
from migrate.versioning import exceptions
import os,shutil
from migrate.versioning.repository import *
from nose.tools import raises
from test import fixture
class TestRepository(fixture.Pathed):
def test_create(self):
"""Repositories are created successfully"""
path=self.tmp_repos()
name='repository_name'
path = self.tmp_repos()
name = 'repository_name'
# Creating a repository that doesn't exist should succeed
repos=Repository.create(path,name)
config_path=repos.config.path
manage_path=os.path.join(repos.path,'manage.py')
self.assert_(repos)
repo = Repository.create(path, name)
config_path = repo.config.path
manage_path = os.path.join(repo.path, 'manage.py')
self.assert_(repo)
# Files should actually be created
self.assert_(os.path.exists(path))
self.assert_(os.path.exists(config_path))
self.assert_(os.path.exists(manage_path))
# Can't create it again: it already exists
self.assertRaises(exceptions.PathFoundError,Repository.create,path,name)
self.assertRaises(exceptions.PathFoundError, Repository.create, path, name)
return path
def test_load(self):
"""We should be able to load information about an existing repository"""
# Create a repository to load
path=self.test_create()
repos=Repository(path)
path = self.test_create()
repos = Repository(path)
self.assert_(repos)
self.assert_(repos.config)
self.assert_(repos.config.get('db_settings','version_table'))
self.assert_(repos.config.get('db_settings', 'version_table'))
# version_table's default isn't none
self.assertNotEquals(repos.config.get('db_settings','version_table'),'None')
from nose.tools import raises
self.assertNotEquals(repos.config.get('db_settings', 'version_table'), 'None')
def test_load_notfound(self):
"""Nonexistant repositories shouldn't be loaded"""
path=self.tmp_repos()
path = self.tmp_repos()
self.assert_(not os.path.exists(path))
self.assertRaises(exceptions.InvalidRepositoryError,Repository,path)
self.assertRaises(exceptions.InvalidRepositoryError, Repository, path)
def test_load_invalid(self):
"""Invalid repos shouldn't be loaded"""
# Here, invalid=empty directory. There may be other conditions too,
# but we shouldn't need to test all of them
path=self.tmp_repos()
path = self.tmp_repos()
os.mkdir(path)
self.assertRaises(exceptions.InvalidRepositoryError,Repository,path)
self.assertRaises(exceptions.InvalidRepositoryError, Repository, path)
class TestVersionedRepository(fixture.Pathed):
"""Tests on an existing repository with a single python script"""
script_cls = script.PythonScript
def setUp(self):
super(TestVersionedRepository, self).setUp()
Repository.clear()
self.path_repos=self.tmp_repos()
# Create repository, script
Repository.create(self.path_repos,'repository_name')
self.path_repos = self.tmp_repos()
Repository.create(self.path_repos, 'repository_name')
def test_version(self):
"""We should correctly detect the version of a repository"""
repos=Repository(self.path_repos)
repos = Repository(self.path_repos)
# Get latest version, or detect if a specified version exists
self.assertEquals(repos.latest,0)
self.assertEquals(repos.latest, 0)
# repos.latest isn't an integer, but a VerNum
# (so we can't just assume the following tests are correct)
self.assert_(repos.latest>=0)
self.assert_(repos.latest<1)
self.assert_(repos.latest >= 0)
self.assert_(repos.latest < 1)
# Create a script and test again
repos.create_script('')
self.assertEquals(repos.latest,1)
self.assert_(repos.latest>=0)
self.assert_(repos.latest>=1)
self.assert_(repos.latest<2)
self.assertEquals(repos.latest, 1)
self.assert_(repos.latest >= 0)
self.assert_(repos.latest >= 1)
self.assert_(repos.latest < 2)
# Create a new script and test again
repos.create_script('')
self.assertEquals(repos.latest,2)
self.assert_(repos.latest>=0)
self.assert_(repos.latest>=1)
self.assert_(repos.latest>=2)
self.assert_(repos.latest<3)
self.assertEquals(repos.latest, 2)
self.assert_(repos.latest >= 0)
self.assert_(repos.latest >= 1)
self.assert_(repos.latest >= 2)
self.assert_(repos.latest < 3)
def test_source(self):
"""Get a script object by version number and view its source"""
# Load repository and commit script
repos=Repository(self.path_repos)
repos.create_script('')
repo = Repository(self.path_repos)
repo.create_script('')
# Get script object
source=repos.version(1).script().source()
source = repo.version(1).script().source()
# Source is valid: script must have an upgrade function
# (not a very thorough test, but should be plenty)
self.assert_(source.find('def upgrade')>=0)
self.assert_(source.find('def upgrade') >= 0)
def test_latestversion(self):
"""Repository.version() (no params) returns the latest version"""
repos=Repository(self.path_repos)
repos = Repository(self.path_repos)
repos.create_script('')
self.assert_(repos.version(repos.latest) is repos.version())
self.assert_(repos.version() is not None)
@ -100,26 +118,26 @@ class TestVersionedRepository(fixture.Pathed):
def test_changeset(self):
"""Repositories can create changesets properly"""
# Create a nonzero-version repository of empty scripts
repos=Repository(self.path_repos)
repos = Repository(self.path_repos)
for i in range(10):
repos.create_script('')
def check_changeset(params,length):
def check_changeset(params, length):
"""Creates and verifies a changeset"""
changeset = repos.changeset('postgres',*params)
self.assertEquals(len(changeset),length)
self.assert_(isinstance(changeset,Changeset))
changeset = repos.changeset('postgres', *params)
self.assertEquals(len(changeset), length)
self.assert_(isinstance(changeset, Changeset))
uniq = list()
# Changesets are iterable
for version,change in changeset:
self.assert_(isinstance(change,script.BaseScript))
for version, change in changeset:
self.assert_(isinstance(change, script.BaseScript))
# Changes aren't identical
self.assert_(id(change) not in uniq)
uniq.append(id(change))
return changeset
# Upgrade to a specified version...
cs=check_changeset((0,10),10)
cs = check_changeset((0,10),10)
self.assertEquals(cs.keys().pop(0),0) # 0 -> 1: index is starting version
self.assertEquals(cs.keys().pop(),9) # 9 -> 10: index is starting version
self.assertEquals(cs.start,0) # starting version
@ -165,3 +183,4 @@ class TestVersionedRepository(fixture.Pathed):
self.assert_(os.path.exists('%s/versions/1000.py' % self.path_repos))
self.assert_(os.path.exists('%s/versions/1001.py' % self.path_repos))
# TODO: test manage file