update README, add docs to repository.py and schema.py

This commit is contained in:
iElectric 2009-06-08 19:05:27 +00:00
parent d835658f23
commit 2fe569dc69
5 changed files with 77 additions and 64 deletions

25
README
View File

@ -5,36 +5,29 @@ Migrate extends SQLAlchemy to have database changeset handling. It provides a da
Help
----
Sphinx documentation is available at the project page [1].
Sphinx documentation is available at the project page `packages.python.org <http://packages.python.org/sqlalchemy-migrate/>`_.
Users and developers can be found at #sqlalchemy-migrate on Freenode IRC network
and at the public users mailing list [2].
and at the public users mailing list `migrate-users <http://groups.google.com/group/migrate-users>`_.
New releases and major changes are announced at the public announce
mailing list [3] and at the Python package index [4].
mailing list `migrate-announce <http://groups.google.com/group/migrate-announce>`_
and at the Python package index `sqlalchemy-migrate <http://pypi.python.org/pypi/sqlalchemy-migrate>`_.
Homepage is located at [5]
Homepage is located at `code.google.com <http://code.google.com/p/sqlalchemy-migrate/>`_
[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
--------------
To run automated tests:
- Copy test_db.cfg.tmpl to test_db.cfg
- Edit test_db.cfg with database connection strings suitable for
running tests. (Use empty databases.)
- Edit test_db.cfg with database connection strings suitable for running tests. (Use empty databases.)
- python setup.py test
Note that nose [5] is required to run migrate's tests. It should be
Note that `nose <http://somethingaboutorange.com/mrl/projects/nose/>`_ is required to run migrate's tests. It should be
installed automatically; if not, try "easy_install nose".
Please report any issues with sqlalchemy-migrate to the issue tracker
at [6]
[5] http://somethingaboutorange.com/mrl/projects/nose/
[6] http://code.google.com/p/sqlalchemy-migrate/issues/list
at `code.google.com issues <http://code.google.com/p/sqlalchemy-migrate/issues/list>`_

View File

@ -128,12 +128,12 @@ Module :mod:`schema <migrate.versioning.schema>`
:members:
:synopsis: Database schema management
Module :mod:`shell <migrate.versioning.shell>`
------------------------------------------------
Module :mod:`schemadiff <migrate.versioning.schemadiff>`
--------------------------------------------------------
.. automodule:: migrate.versioning.shell
.. automodule:: migrate.versioning.schemadiff
:members:
:synopsis: Shell commands
:synopsis: Database schema and model differencing
Module :mod:`script <migrate.versioning.script>`
------------------------------------------------
@ -152,16 +152,23 @@ Module :mod:`script <migrate.versioning.script>`
:show-inheritance:
:inherited-members:
Module :mod:`shell <migrate.versioning.shell>`
----------------------------------------------
.. automodule:: migrate.versioning.shell
:members:
:synopsis: Shell commands
Module :mod:`util <migrate.versioning.util>`
------------------------------------------------
--------------------------------------------
.. automodule:: migrate.versioning.util
:members:
:synopsis: Utility functions
Module :mod:`schemadiff <migrate.versioning.schemadiff>`
--------------------------------------------------------
Module :mod:`version <migrate.versioning.version>`
--------------------------------------------------
.. automodule:: migrate.versioning.schemadiff
.. automodule:: migrate.versioning.version
:members:
:synopsis: Database schema and model differencing
:synopsis: Version management

View File

@ -14,11 +14,10 @@ from migrate.versioning.base import *
class Changeset(dict):
"""A collection of changes to be applied to a database.
Changesets are bound to a repository and manage a set of logsql
Changesets are bound to a repository and manage a set of
scripts from that repository.
Behaves like a dict, for the most part. Keys are ordered based on
start/end.
Behaves like a dict, for the most part. Keys are ordered based on step value.
"""
def __init__(self, start, *changes, **k):
@ -50,11 +49,13 @@ class Changeset(dict):
return zip(self.keys(), self.values())
def add(self, change):
"""Add new change to changeset"""
key = self.end
self.end += self.step
self[key] = change
def run(self, *p, **k):
"""Run the changeset scripts"""
for version, script in self:
script.run(*p, **k)
@ -119,54 +120,62 @@ class Repository(pathed.Pathed):
config_text = cls.prepare_config(tmplpkg, cls._config, name, **opts)
# Create repository
try:
shutil.copytree(tmplfile, path)
# Edit config defaults
fd = open(os.path.join(path, cls._config), 'w')
fd.write(config_text)
fd.close()
# Create a management script
manager = os.path.join(path, 'manage.py')
Repository.create_manage_file(manager, repository=path)
except:
log.error("There was an error creating your repository")
shutil.copytree(tmplfile, path)
# Edit config defaults
fd = open(os.path.join(path, cls._config), 'w')
fd.write(config_text)
fd.close()
# Create a management script
manager = os.path.join(path, 'manage.py')
Repository.create_manage_file(manager, repository=path)
return cls(path)
def create_script(self, description, **k):
""""""
"""API to :meth:`migrate.versioning.version.Collection.create_new_python_version`"""
self.versions.create_new_python_version(description, **k)
def create_script_sql(self, database, **k):
""""""
"""API to :meth:`migrate.versioning.version.Collection.create_new_sql_version`"""
self.versions.create_new_sql_version(database, **k)
@property
def latest(self):
""""""
"""API to :attr:`migrate.versioning.version.Collection.latest`"""
return self.versions.latest
@property
def version_table(self):
""""""
"""Returns version_table name specified in config"""
return self.config.get('db_settings', 'version_table')
@property
def id(self):
""""""
"""Returns repository id specified in config"""
return self.config.get('db_settings', 'repository_id')
def version(self, *p, **k):
""""""
"""API to :attr:`migrate.versioning.version.Collection.version`"""
return self.versions.version(*p, **k)
@classmethod
def clear(cls):
""""""
# TODO: deletes repo
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 database from ver. start to end/latest.
:param database: name of database to generate changeset
:param start: version to start at
:param end: version to end at (latest if None given)
:type database: string
:type start: int
:type end: int
:returns: :class:`Changeset instance <migration.versioning.repository.Changeset>`
"""
start = version.VerNum(start)
@ -189,13 +198,12 @@ class Repository(pathed.Pathed):
ret = Changeset(start, step=step, *changes)
return ret
@classmethod
def create_manage_file(cls, file_, **opts):
"""Create a project management script (manage.py)
:param file_: Destination file to be written
:param **opts: Options that are passed to template
:param opts: Options that are passed to template
"""
vars_ = ",".join(["%s='%s'" % var for var in opts.iteritems()])

View File

@ -17,25 +17,26 @@ class ControlledSchema(object):
def __init__(self, engine, repository):
if isinstance(repository, str):
repository=Repository(repository)
repository = Repository(repository)
self.engine = engine
self.repository = repository
self.meta=MetaData(engine)
self._load()
self.meta = MetaData(engine)
self.load()
def __eq__(self, other):
"""Compare two schemas by repositories and versions"""
return (self.repository is other.repository \
and self.version == other.version)
def _load(self):
def load(self):
"""Load controlled schema version info from DB"""
tname = self.repository.version_table
self.meta=MetaData(self.engine)
if not hasattr(self, 'table') or self.table is None:
try:
self.table = Table(tname, self.meta, autoload=True)
except (exceptions.NoSuchTableError):
raise exceptions.DatabaseNotControlledError(tname)
# TODO?: verify that the table is correct (# cols, etc.)
result = self.engine.execute(self.table.select(
self.table.c.repository_id == str(self.repository.id)))
@ -57,13 +58,15 @@ class ControlledSchema(object):
def create(cls, engine, repository, version=None):
"""
Declare a database to be under a repository's version control.
:returns: :class:`ControlledSchema`
"""
# Confirm that the version # is valid: positive, integer,
# exists in repos
if type(repository) is str:
repository=Repository(repository)
if isinstance(repository, str):
repository = Repository(repository)
version = cls._validate_version(repository, version)
table=cls._create_table_version(engine, repository, version)
table = cls._create_table_version(engine, repository, version)
# TODO: history table
# Load repository information and return
return cls(engine, repository)
@ -73,7 +76,7 @@ class ControlledSchema(object):
"""
Ensures this is a valid version number for this repository.
:raises: :exc:`cls.InvalidVersionError` if invalid
:raises: :exc:`ControlledSchema.InvalidVersionError` if invalid
:return: valid version number
"""
if version is None:
@ -164,8 +167,7 @@ class ControlledSchema(object):
"""
Returns the database name of an engine - ``postgres``, ``sqlite`` ...
"""
# TODO: This is a bit of a hack...
return str(engine.dialect.__module__).split('.')[-1]
return engine.name
def changeset(self, version=None):
database = self._engine_db(self.engine)
@ -187,7 +189,7 @@ class ControlledSchema(object):
and_(self.table.c.version == int(startver),
self.table.c.repository_id == str(self.repository.id)))
self.engine.execute(update, version=int(endver))
self._load()
self.load()
def upgrade(self, version=None):
"""

View File

@ -9,7 +9,7 @@ from migrate.versioning import exceptions, pathed, script
class VerNum(object):
"""A version number"""
"""A version number that behaves like a string and int at the same time"""
_instances = dict()
@ -51,7 +51,9 @@ class Collection(pathed.Pathed):
FILENAME_WITH_VERSION = re.compile(r'^(\d{3,}).*')
def __init__(self, path):
"""Collect current version scripts in repository"""
"""Collect current version scripts in repository
and store them in self.versions
"""
super(Collection, self).__init__(path)
# Create temporary list of files, allowing skipped version numbers.
@ -79,6 +81,7 @@ class Collection(pathed.Pathed):
@property
def latest(self):
""":returns: Latest version in Collection"""
return max([VerNum(0)] + self.versions.keys())
def create_new_python_version(self, description, **k):
@ -118,7 +121,7 @@ class Collection(pathed.Pathed):
self.versions[ver].add_script(filepath)
def version(self, vernum=None):
"""Returns latest Version if vernum is not given. \
"""Returns latest Version if vernum is not given.
Otherwise, returns wanted version"""
if vernum is None:
vernum = self.latest